当前位置: 首页 > news >正文

Laravel chunk和chunkById的坑

在编写定时任务脚本的时候,经常会用到chunk和chunkById的API。

一、前言

数据库引擎为innodb。

表结构简述,只列出了本文用到的字段。

字段类型注释
idint(11)ID
typeint(11)类型
mark_timeint(10)标注时间(时间戳)

索引,也只列出需要的部分。

索引名字段
PRIMARYid
idx_sid_blogdel_marktimetype blog_del mark_time
Idx_marktimemark_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){// 业务处理
});

从一个月中的数据,筛选出type为99,并且标注时间在某天的00:00:00-23:59:59的数据。可以使用到mark_time和type的索引。

type为99,一天的数据大概在15-25w上下的样子。使用->get()->toArray()内存会直接炸掉。所以使用chunk方法,每次取出1000条数据。

使用chucnk,不会出现内存不够的情况。但是性能较差。粗略估计,从一月数据中取出最后一天的数据,跑完20w数据大概需要一两分钟。

查看源码,底层的chunk方法,是为sql语句添加了限制和偏移量。

select * from users asc limit 500 offset 500;

在数据较多的时候,越往后的话效率会越慢,因为Mysql的limit方法底层是这样的。

limit 10000,10

是扫描满足条件的10010行,然后扔掉前面的10000行,返回最后最后20行。在数据较多的时候,性能会非常差。

查了下API,对于这种情况Laraverl提供了另一个API chunkById。

四、chunkById 原理

使用limit和偏移量在处理大量的数据会有性能的明显下降。于是chunkById使用了id进行分页处理。很好理解,代码如下:

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

API会自动保存最后一个ID,然后通过id > :last_id 再加上limit就可以通过主键索引进行分页。只取出来需要的行数。性能会有明显的提升。

五、chunkById的坑

API显示chunk和chunkById的用法完全相同。于是把脚本的代码换成了chunkById。

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

在执行脚本的时候,1月2号和1月1号的数据没有任何问题。执行速度快了很多。但是在执行12月31号的数据的时候,发现脚本一直执行不完。

在定位后发现是脚本没有进入业务处理的部分,也就是sql一直没有执行完。当时很疑惑,因为刚才执行的没问题,为什么执行12月31号的就出问题了呢。

于是查看sql服务器中的执行情况。

show full processlist;

发现了问题。上节说了chunkById的底层是通过id进行order by,然后limie取出一部分一部分的数据,也就是我们预想的sql是这样的。

select * from tabel where type = 99 and mark_time between :begin_date and :end_date limit 500;

explain出来的情况如下:

在这里插入图片描述

实际上的sql是这样的:

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

实际explain出来的情况是这样的:

在这里插入图片描述

chunkById会自动添加order by id。innodb一定会使用主键索引。那么就不会再使用mark_time的索引了。导致sql执行效率及其缓慢。

六、解决方法

再次查看chunkById的源码。

/*** Chunk the results of a query by comparing IDs.** @param  int  $count* @param  callable  $callback* @param  string|null  $column* @param  string|null  $alias* @return bool*/
public function chunkById($count, callable $callback, $column = null, $alias = null)
{$column = $column ?? $this->defaultKeyName();$alias = $alias ?? $column;$lastId = null;do {$clone = clone $this;// We'll execute the query for the given page and get the results. If there are// no results we can just break and return from here. When there are results// we will call the callback with the current chunk of these results here.$results = $clone->forPageAfterId($count, $lastId, $column)->get();$countResults = $results->count();if ($countResults == 0) {break;}// On each chunk result set, we will pass them to the callback and then let the// developer take care of everything within the callback, which allows us to// keep the memory low for spinning through large result sets for working.if ($callback($results) === false) {return false;}$lastId = $results->last()->{$alias};unset($results);} while ($countResults == $count);return true;
}

能看到这个方法有四个参数count,callback,column,alias。
默认的column为null,第一行会进行默认赋值。

/*** Get the default key name of the table.** @return string*/
protected function defaultKeyName()
{return 'id';
}

能看到默认的column为id。
进入forPageAfterId方法。

/*** Constrain the query to the next "page" of results after a given ID.** @param  int  $perPage* @param  int|null  $lastId* @param  string  $column* @return \Illuminate\Database\Query\Builder|static*/
public function forPageAfterId($perPage = 15, $lastId = 0, $column = 'id')
{$this->orders = $this->removeExistingOrdersFor($column);if (! is_null($lastId)) {$this->where($column, '>', $lastId);}return $this->orderBy($column, 'asc')->limit($perPage);
}

能看到如果lastId不为0则自动添加where语句,还会自动添加order by column。

看到这里就明白了。上文的chunkById没有添加column参数,所以底层自动添加了order by id。走了主键索引,没有使用上mark_time的索引。导致查询效率非常低。

chunkById的源码显示了我们可以传递一个column字段来让底层使用这个字段来order by。

代码修改如下:

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

这样最后执行的sql如下:

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

再次执行脚本,大概执行一次也就十秒作用了,性能提升显著。

七、总结

使用 chunkById 或者 chunk 方法的时候不要添加自定义的排序,chunk和chunkById的区别就是chunk是单纯的通过偏移量来获取数据,chunkById进行了优化,不使用偏移量,使用id过滤,性能提升巨大。在数据量大的时候,性能可以差到几十倍的样子。

而且使用chunk在更新的时候,也会遇到数据会被跳过的问题。详见解决Laravel中chunk方法分块处理数据的坑

同时chunkById在你没有传递column参数时,会默认添加order by id。可能会遇到索引失效的问题。解决办法就是传递column参数即可。

本人感觉chunkById不光是根据Id分块,而是可以根据某一字段进行分块,这个字段是可以指定的。叫chunkById有一些误导性,chunkByColumn可能更容易理解。

使用chunkById时不要加排序
使用chunkById比chunk更快

原文

相关文章:

Laravel chunk和chunkById的坑

在编写定时任务脚本的时候,经常会用到chunk和chunkById的API。 一、前言 数据库引擎为innodb。 表结构简述,只列出了本文用到的字段。 字段类型注释idint(11)IDtypeint(11)类型mark_timeint(10)标注时间(时间戳) 索引&#x…...

从零开始学习 Java:简单易懂的入门指南之泛型及set集合(二十二)

泛型及set集合扩展 1.泛型1.1泛型概述 2.Set集合2.1Set集合概述和特点【应用】2.2Set集合的使用【应用】 3.TreeSet集合3.1TreeSet集合概述和特点【应用】3.2TreeSet集合基本使用【应用】3.3自然排序Comparable的使用【应用】3.4比较器排序Comparator的使用【应用】3.5两种比较…...

JVM----GC(垃圾回收)详解

一、Automatic Garbage Collection(垃圾回收)简介 Automatic Garbage Collection (自动垃圾回收)是JVM的一个特性,JVM会启动相关的线程,该线程会轮训检查heap memeory,并确定哪些是未被引用的(…...

数据库的三个范式

数据库的三个范式是关系数据库设计中的一组规范,用于确保数据的有效性和一致性。这三个范式分别是: 第一范式(1NF):要求数据库表中的每一列都是不可分割的原子值。换句话说,每个表中的每个字段不能包含多个…...

谷歌浏览器打开白屏 后台还有还有很多google chrome进程在运行

环境: Win10 专业版 谷歌浏览器 版本 116.0.5845.141(正式版本) (64 位) L盾加密终端 问题描述: 谷歌浏览器打开白屏 后台还有还有很多google chrome进程在运行,要全部结束谷歌浏览器进程&…...

Java EE 突击 15 - Spring Boot 统一功能处理

Spring Boot 统一功能处理 一 . 统一功能的处理1.1 初级阶段 : 不断重复1.2 中级阶段 : 集成方法1.3 高级阶段 : Spring AOP1.4 超高级阶段 : Spring 拦截器准备工作实现拦截器自定义拦截器将自定义拦截器加入到系统配置 拦截器实现原理扩展 : 统一访问前缀添加 二 . 统一异常的…...

JasperReport定义变量后打印PDF变量为null以及整个pdf文件为空白

问题1: JasperReport打印出来的整个pdf文件为空白文件; 问题2:JasperReport定义变量后打印PDF变量为null; 问题1原因是因为缺少数据源JRDataSource JasperFillManager.fillReport(jasperReport, params,new JREmptyDataSource());如果你打印…...

Python 及 Pycharm 的安装 2023.8

Python 及 PyCharm 的安装 仅适用于 Windows 系统! 视频教程:【Python及Pycharm的安装 2023.8】 https://www.bilibili.com/video/BV1A34y1T7Gu 文章目录 Python 及 PyCharm 的安装安装 Python安装 PyCharmHi, PyCharmPyCharm 汉化 安装 Python 进入 …...

java中的线程中断

java中的线程中断 1、线程中断 即 线程的取消/关闭的机制2、线程对中断interrupt()的反应2.1、RUNNABLE:线程在运行或具备运行条件只是在等待操作系统调度2.2、WAITING/TIMED_WAITING:线程在等待某个条件或超时2.3、BLOCKED:线程在等待锁&…...

【跟小嘉学 Rust 编程】二十三、Cargo 使用指南

系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…...

R Removing package报错(as ‘lib’ is unspecified)

remove.packages(ggpubr) Removing package from ‘/Library/Frameworks/R.framework/Versions/4.0/Resources/library’ (as ‘lib’ is unspecified) 解决办法: > .libPaths() [1] "/Library/Frameworks/R.framework/Versions/4.0/Resources/library&qu…...

金融信创,软件规划需关注自主安全及生态建设

软件信创化,就是信息技术软件应用创新发展的意思(简称为“信创”)。 相信在中国,企业对于“信创化”这个概念并不陌生。「国强则民强」,今年来中国经济的快速发展,受到了各大欧美强国的“卡脖子”操作的影…...

无重叠区间【贪心算法】

无重叠区间 给定一个区间的集合 intervals ,其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 。 class Solution {public int eraseOverlapIntervals(int[][] intervals) {//先排序,按照左边界升序,注…...

nlp系列(7)实体识别(Bert)pytorch

模型介绍 本项目是使用Bert模型来进行文本的实体识别。 Bert模型介绍可以查看这篇文章:nlp系列(2)文本分类(Bert)pytorch_bert文本分类_牧子川的博客-CSDN博客 模型结构 Bert模型的模型结构: 数据介绍 …...

Uniapp学习之从零开始写一个简单的小程序demo(新建页面,通过导航切换页面,发送请求)

先把官网文档摆在这,后面会用到的 [uniapp官网文档]: https://uniapp.dcloud.net.cn/vernacular.html# 一、开发工具准备 1-1 安装HBuilder 按照官方推荐,先装一个HBuilder 下载地址: https://www.dcloud.io/hbuilderx.html1-2 安装微信开…...

uniapp微信小程序隐私保护引导新规

1.components中新建组件PrivacyPop.vue <template><view class"privacy" v-if"showPrivacy"><view class"content"><view class"title">隐私保护指引</view><view class"des">在使用当…...

超图嵌入论文阅读2:超图神经网络

超图嵌入论文阅读2&#xff1a;超图神经网络 原文&#xff1a;Hypergraph Neural Networks ——AAAI2019&#xff08;CCF-A&#xff09; 源码&#xff1a;https://github.com/iMoonLab/HGNN 500star 概述 贡献&#xff1a;用于数据表示学习的超图神经网络 (HGNN) 框架&#xf…...

安全运营中心(SOC)技术框架

2018年曾经画过一个安全运营体系框架&#xff0c;基本思路是在基础单点技术防护体系基础上&#xff0c;围绕着动态防御、深度分析、实时检测&#xff0c;建立安全运营大数据分析平台&#xff0c;可以算作是解决方案产品的思路。 依据这个体系框架&#xff0c;当时写了《基于主动…...

并行和并发的区别

从操作系统的角度来看&#xff0c;线程是CPU分配的最小单位。 并行就是同一时刻&#xff0c;两个线程都在执行。这就要求有两个CPU去分别执行两个线程。并发就是同一时刻&#xff0c;只有一个执行&#xff0c;但是一个时间段内&#xff0c;两个线程都执行了。并发的实现依赖于…...

GPT转换工具:轻松将MBR转换为GPT磁盘

为什么需要将MBR转换为GPT&#xff1f; 众所周知&#xff0c;Windows 11已经发布很长时间了。在此期间&#xff0c;许多老用户已经从Windows 10升级到Windows 11。但有些用户仍在运行Windows 10。对于那些想要升级到Win 11的用户来说&#xff0c;他们可能不确定Win 11应该使…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”

目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

Java毕业设计:WML信息查询与后端信息发布系统开发

JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发&#xff0c;实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构&#xff0c;服务器端使用Java Servlet处理请求&#xff0c;数据库采用MySQL存储信息&#xff0…...