PHP foreach遍历写入Redis仅保存最后一条如何解决
问题背景
- 站点部署了日历事件API拉取+前端展示逻辑,为避免页面每次加载都触发接口请求,采用Redis做缓存层:先将待拉取的日历标识存入Redis,通过cron每30分钟执行一次PHP脚本,脚本读取待拉取列表、调用日历API获取数据、处理完成后将事件数据写回Redis,前端直接读取Redis缓存渲染内容。
- 当前故障:脚本遍历待拉取列表时,确实执行了全部条目的处理逻辑,但最终Redis中仅保留最后一条处理结果,所有前面的条目数据都丢失。
原问题代码
<?php function redis_calendar_fetch() { $redisObj1 = new Redis(); $redisObj1->connect('localhost', 6379, 2.5, NULL, 150 ); date_default_timezone_set('America/Edmonton'); $fetcharray = $redisObj1->smembers('fetch_list'); $effectiveDate = date('Y-m-d', strtotime('+12 months')); $application_id = "REDACTED"; $secret = "REDACTED"; $fafull = array(); foreach($fetcharray as $faraw) { $fa1 = json_decode($faraw); $fa2 = json_decode(json_encode($fa1), true); $fafull[] = $fa2; } $redisObj1->close(); // 以上读取待拉取列表的逻辑运行正常,可正确返回供遍历的数组 foreach($fafull as $fa) { $redisObj = new Redis(); $redisObj->connect('localhost', 6379, 2.5, NULL, 150 ); // 省略中间API拉取、数据处理逻辑(该部分运行正常) // 处理完成后生成包含事件数据的$redisarray数组 $redisarrayfixed = json_encode($redisarray); $redisObj->set($key, $redisarrayfixed); $redisObj->close(); // 若在此处加return语句,脚本仅会处理第一条数据就终止;不加return则遍历全部条目,但仅最后一条数据存入Redis } } redis_calendar_fetch();
故障根因
核心问题有两个:
- 所有循环迭代共用同一个Redis键名:循环内写入Redis时使用的
$key变量没有在每次循环中赋值为当前条目的唯一标识,每一次循环执行set操作都是在覆盖上一次写入的同一个键,循环结束后自然只保留最后一次写入的结果。 - 循环内重复创建Redis连接属于无效开销:每次循环都新建、关闭Redis连接没有实际意义,只会额外消耗TCP连接资源,拖慢脚本执行速度。
另外补充:在foreach循环内部写return会直接终止整个函数执行,因此只会处理第一条数据就退出,这是PHP基础语法逻辑,和Redis或业务逻辑无关。
修复方案
按以下步骤调整代码即可:
- 将Redis连接初始化移到循环外部,全程复用同一个连接,避免重复建连开销。
- 每次循环处理单个待拉取条目时,基于当前条目的唯一属性(比如日历ID、事件分组ID等唯一标识)生成独立的Redis键名,确保不同条目写入不同的Redis键,不会互相覆盖。
- 若追求更高写入效率,可将所有条目处理完成后,用Redis的
mSet方法批量写入所有键值对,减少Redis网络IO次数。
修复后参考代码
<?php function redis_calendar_fetch() { // 全局只初始化一次Redis连接,全程复用 $redisObj = new Redis(); $redisObj->connect('localhost', 6379, 2.5, NULL, 150 ); date_default_timezone_set('America/Edmonton'); $fetcharray = $redisObj->smembers('fetch_list'); $effectiveDate = date('Y-m-d', strtotime('+12 months')); $application_id = "REDACTED"; $secret = "REDACTED"; $fafull = array(); foreach($fetcharray as $faraw) { $fa1 = json_decode($faraw); $fa2 = json_decode(json_encode($fa1), true); $fafull[] = $fa2; } // 用于批量写入的键值对数组 $cacheData = []; foreach($fafull as $fa) { // 省略原有的API拉取、数据处理逻辑,最终生成当前条目对应的$redisarray // *重点:用当前条目的唯一标识生成独立key,此处以$fa中存储的calendar_id为例,替换为实际业务的唯一字段即可 $key = 'calendar:events:' . $fa['calendar_id']; $redisarrayfixed = json_encode($redisarray); // 单条写入写法 $redisObj->set($key, $redisarrayfixed); // 如果用批量写入,就把键值对存到数组里,循环结束后统一写 // $cacheData[$key] = $redisarrayfixed; } // 批量写入(可选,效率更高) // if (!empty($cacheData)) { // $redisObj->mSet($cacheData); // } // 所有操作完成后再关闭连接 $redisObj->close(); } redis_calendar_fetch();
内容的提问来源于stack exchange,提问作者Darien Chaffart
相关产品推荐
相关产品推荐

