thinkphp5 chunk 分块处理数据的坑

场景:

 使用chunk方法进行分块查询写入数据,执行发现chunk分几条一次处理 数据库就插入几条,并没有return false;

源码分析如下

chunk 第3个参数是数组时,取的是分页,如果状态不断的更新,数据源是不断的在变化

第3个参数非数组,每次取的都是前面几条 ,数据源取的永远是前面的

通常第3个参数用到的数组是 多个字段排序,才会遇到此问题,非多个字段 慎用数组

thinkphp5 chunk 分块处理数据的坑_第1张图片

代码举例如下:

1、注意此时有问题的是,chunk 第3个参数是个数组

-- count 44
SELECT COUNT(*) FROM agent WHERE `status` = 6;  

-- count 0
SELECT COUNT(*) FROM agent WHERE `status` = 7;

thinkphp5 chunk 分块处理数据的坑_第2张图片

sql语句如下

[ SQL ] SELECT `id`,`name` FROM `agent` WHERE `status` = 6 ORDER BY `id` LIMIT 0,30 [ RunTime:0.000926s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 31 [ RunTime:0.005106s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 32 [ RunTime:0.003842s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 34 [ RunTime:0.002873s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 35 [ RunTime:0.003258s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 36 [ RunTime:0.004567s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 37 [ RunTime:0.003386s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 38 [ RunTime:0.006443s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 41 [ RunTime:0.003224s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 42 [ RunTime:0.003093s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 43 [ RunTime:0.003712s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 44 [ RunTime:0.004631s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 45 [ RunTime:0.003607s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 46 [ RunTime:0.003660s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 48 [ RunTime:0.004149s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 50 [ RunTime:0.003582s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 51 [ RunTime:0.004171s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 52 [ RunTime:0.004329s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 53 [ RunTime:0.010809s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 55 [ RunTime:0.003725s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 58 [ RunTime:0.004220s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 64 [ RunTime:0.004714s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 65 [ RunTime:0.003782s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 68 [ RunTime:0.003905s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 76 [ RunTime:0.004706s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 78 [ RunTime:0.004264s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 81 [ RunTime:0.004535s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 83 [ RunTime:0.006476s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 88 [ RunTime:0.003775s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 91 [ RunTime:0.004364s ]
[ SQL ] UPDATE `agent` SET `status` = 7 WHERE `id` = 92 [ RunTime:0.005330s ]
[ SQL ] SELECT `id`,`name` FROM `agent` WHERE `status` = 6 ORDER BY `id` LIMIT 30,30 [ RunTime:0.002369s ]

此时查sql语句

thinkphp5 chunk 分块处理数据的坑_第3张图片

 说明状态6 改成7的还没有执行完,数据出现了漏处理的情况

出现问题的原因是

SELECT `id`,`name` FROM `agent` WHERE `status` = 6 ORDER BY `id` LIMIT 0,30 

SELECT `id`,`name` FROM `agent` WHERE `status` = 6 ORDER BY `id` LIMIT 30,30

chunk第3个参数是数组,取的是LIMIT 0,30和 LIMIT 30,30,用的是分页当更改status状态后 他的数据源一直在变 ,

解决方案,一定要确保数据源保持不变!!!!!!即去掉status条件限制,放在循环里判断

\app\model\Agent::field('id, name,status')
//            ->where('status', 6)  // 条件去掉 放到循环处理
            ->chunk(30, function ($agentModel) {
                foreach ($agentModel as $info) {
                    dump($info);
                    if ($info->status == 6) {
                        \app\model\Agent::where('id', $info['id'])->update(['status' => 7]);
                    }

                }
            }, ['id']);
[ SQL ] SELECT `id`,`name`,`status` FROM `agent` ORDER BY `id` LIMIT 0,30 [ RunTime:0.001081s ]
[ SQL ] SELECT `id`,`name`,`status` FROM `agent` ORDER BY `id` LIMIT 30,30 [ RunTime:0.001012s ]
[ SQL ] SELECT `id`,`name`,`status` FROM `agent` ORDER BY `id` LIMIT 60,30 [ RunTime:0.000878s ]
[ SQL ] SELECT `id`,`name`,`status` FROM `agent` ORDER BY `id` LIMIT 90,30 [ RunTime:0.000963s ]
[ SQL ] SELECT `id`,`name`,`status` FROM `agent` ORDER BY `id` LIMIT 120,30 [ RunTime:0.001247s ]

thinkphp5 chunk 分块处理数据的坑_第4张图片

通常第3个参数用到的数组是 多个字段排序,才会遇到此问题,非多个字段 慎用数组

代码示例

      \app\model\Agent::field('id, name,status')
//            ->where('status', 6)  // 条件去掉 放到循环处理
            ->chunk(30, function ($agentModel) {
                foreach ($agentModel as $info) {
                    dump($info);
                    if ($info->status == 6) {
                        \app\model\Agent::where('id', $info['id'])->update(['status' => 7]);
                    }

                }
            }, ['sort desc', 'id asc']);

 

2、如果有id主键正常执行,第三个参数不传,会自动获取主键,是这样是没错的

\app\model\Agent::field('id, name')
            ->where('status', 5)  // 注意这个有个status条件
            ->chunk(30, function ($agentModel) {
                foreach ($agentModel as $info) {
                    dump($info);
                    \app\model\Agent::where('id', $info['id'])->update(['status' => 6]);
                }
            });

3、如果没有设置主键,第三个参数要传,为了安全起见,建议还是写上,不要偷懒!!!

thinkphp5 chunk 分块处理数据的坑_第5张图片

 

上面两个id主键sql执行如下 

[ SQL ] SELECT `id`,`name` FROM `agent` WHERE `status` = 5 ORDER BY `id` ASC LIMIT 30 [ RunTime:0.000991s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 31 [ RunTime:0.005297s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 32 [ RunTime:0.004962s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 34 [ RunTime:0.005891s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 35 [ RunTime:0.003951s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 36 [ RunTime:0.006202s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 37 [ RunTime:0.005032s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 38 [ RunTime:0.006677s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 41 [ RunTime:0.003708s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 42 [ RunTime:0.004749s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 43 [ RunTime:0.003820s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 44 [ RunTime:0.004658s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 45 [ RunTime:0.004239s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 46 [ RunTime:0.003897s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 48 [ RunTime:0.004970s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 50 [ RunTime:0.003220s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 51 [ RunTime:0.004782s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 52 [ RunTime:0.004196s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 53 [ RunTime:0.004342s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 55 [ RunTime:0.003299s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 58 [ RunTime:0.003533s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 64 [ RunTime:0.004330s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 65 [ RunTime:0.004645s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 68 [ RunTime:0.004522s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 76 [ RunTime:0.003577s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 78 [ RunTime:0.006653s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 81 [ RunTime:0.004538s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 83 [ RunTime:0.003604s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 88 [ RunTime:0.004038s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 91 [ RunTime:0.005194s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 92 [ RunTime:0.003052s ]
[ SQL ] SELECT `id`,`name` FROM `agent` WHERE `status` = 5 AND `id` > 92 ORDER BY `id` ASC LIMIT 30 [ RunTime:0.001586s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 99 [ RunTime:0.003963s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 108 [ RunTime:0.004367s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 110 [ RunTime:0.003883s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 115 [ RunTime:0.004952s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 134 [ RunTime:0.004294s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 145 [ RunTime:0.003549s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 171 [ RunTime:0.004260s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 175 [ RunTime:0.003893s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 182 [ RunTime:0.002691s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 195 [ RunTime:0.005611s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 197 [ RunTime:0.004149s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 199 [ RunTime:0.003191s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 202 [ RunTime:0.004697s ]
[ SQL ] UPDATE `agent` SET `status` = 6 WHERE `id` = 205 [ RunTime:0.004768s ]
[ SQL ] SELECT `id`,`name` FROM `agent` WHERE `status` = 5 AND `id` > 205 ORDER BY `id` ASC LIMIT 30 [ RunTime:0.001893s ]

这里的sql取的一直是前30条

SELECT `id`,`name` FROM `agent` WHERE `status` = 5 ORDER BY `id` ASC LIMIT 30

 

你可能感兴趣的:(thinkphp,php,php)