Laravel chunk 不使用id
发布日期:2025-04-04 02:18:20 浏览次数:18 分类:精选文章

本文共 2520 字,大约阅读时间需要 8 分钟。

Laravel chunk和chunkById的坑

前言

数据库引擎为InnoDB。以下为表结构简述及索引情况:

字段 类型 注释
id int(11) ID
type int(11) 类型
mark_time int(10) 标注时间(时间戳)

索引

索引名 字段
PRIMARY id
idx_sid_blogdel_marktime type, blog_del, mark_time
Idx_marktime mark_time

需求

每天凌晨一点,取出昨天标注type为99的所有数据,进行逻辑判断和其他操作。重点在于取数据阶段。数据按月分表,每个月表中数据量为1000w左右。

chunk处理数据

初始代码如下:

$this->dao->where('type', 99)
->whereBetween('mark_time', [$date, $date+86399])
->select(array('mark_time', 'id'))
->chunk(1000, function ($rows) {
// 业务处理
});

chunk方法的获取方式可以避免一次性获取所有数据至内存,但在处理10000w数据时,chunk的方式会导致查询效率很低。

chunk的内部实现:

select * from `表` asc limit 500 offset 500;

在处理大数据量时,Mysql的这种处理方式由于会扫描大量数据,导致性能问题。

chunkById的原理

chunkById通过id字段进行分页,优化性能。使用方法如下:

$this->dao->where('type', 99)
->whereBetween('mark_time', [$date, $date+86399])
->select(array('mark_time', 'id'))
->chunkById(1000, function ($rows) {
// 业务处理
});

chunkById的内部查询:

select * from `表` where `id` > :last_id order by `id` asc limit 500;

这种方式通过主键id进行分页,每次查询控制在500条内,性能较好。

chunkById的坑

改用chunkById后,在处理13月31日的数据时,发现脚本未能进入业务处理部分,无法完成查询。经过检查发现,chunkById默认使用id字段作为分页依据,可能导致索引失效,影响查询性能。

chunkById的查询结果仅使用mark_time索引:

select * from `表` where `type` = 99 and mark_time between :begin_date and :end_date order by id limit 500;

此时,chunkById未使用mark_time索引,导致查询效率很低。

解决方法

检查chunkById的源码:

public function chunkById($count, callable $callback, $column = null, $alias = null)
{
$column = is_null($column) ? $this->getModel()->getKeyName() : $column;
$alias = is_null($alias) ? $column : $alias;
$lastId = null;
do {
$clone = clone $this;
$results = $clone->forPageAfterId($count, $lastId, $column)->get();
$countResults = $results->count();
if ($countResults == 0) {
break;
}
if ($callback($results) === false) {
return false;
}
$lastId = $results->last()->{$alias};
unset($results);
} while ($countResults == $count);
return true;
}

默认情况下,chunkById使用id字段作为分页依据。若想指定mark_time字段作为分页依据,可以通过方法参数传递:

$this->dao->where('type', 99)
->whereBetween('mark_time', [$date, $date+86399])
->select(array('mark_time', 'id'))
->chunkById(1000, function ($rows) {
// 业务处理
}, 'mark_time');

这样查询方式得到优化:

select * from `表` where `type` = 99 and mark_time between :begin_date and :end_date order by mark_time limit 500;

此时,数据库会使用mark_time索引,查询效率明显提升。

总结

chunk与chunkById的主要区别在于后者通过主键字段分页,而默认时使用id字段,可能导致索引失效。若想优化查询方式,建议指定需要排序的字段作为分页依据。

特别提醒:未传递column参数时,默认使用id字段,可能导致索引失效,影响性能。建议根据应用需求传递合适的字段。

以上是对实际案例中的技术问题及解决方案的总结,如有疑问或需要进一步的帮助,欢迎随时留言。

上一篇:Laravel Dry Requests 项目教程
下一篇:Laravel 6.0

发表评论

最新留言

表示我来过!
[***.240.166.169]2025年05月05日 05时26分23秒