You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
故障根因

核心问题有两个:

  1. 所有循环迭代共用同一个Redis键名:循环内写入Redis时使用的$key变量没有在每次循环中赋值为当前条目的唯一标识,每一次循环执行set操作都是在覆盖上一次写入的同一个键,循环结束后自然只保留最后一次写入的结果。
  2. 循环内重复创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 05:57:17