MySQL分页查询limit踩坑记
1 问题背景
线上有一个批处理任务,会批量读取昨日的数据,经过一系列加工后,插入到今日的表中。表结构如下:
1 CREATE TABLE `detail_yyyyMMdd` (
2 `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
3 `batch_no` varchar(64) NOT NULL COMMENT '批次号',
4 `order_id` varchar(64) NOT NULL COMMENT '订单ID',
5 `user_id` varchar(64) NOT NULL COMMENT '用户ID',
6 `status` varchar(4) NOT NULL COMMENT '状态',
7 `product_id` varchar(32) NOT NULL COMMENT '产品ID',
8 PRIMARY KEY (`id`),
9 KEY `idx_batchno_userid` (`batch_no`,`user_id`)
10 ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='明细表';
因数据量较大,批量读取昨日数据时,使用了分页查询limit语句,查询sql如下:
SELECT id,batch_no,order_id,user_id,status,product_id FROM detail_yyyyMMdd WHERE batch_no=‘batch_type_yyyyMMdd’ LIMIT ?,?;
从某一天开始,客服频繁收到客诉,反馈数据未更新。
2 问题排查
初步排查,客诉反馈的数据确实没有插入。线上增加了一些业务日志后,发现分页查询昨日数据时,第二次查询的结果集与第一次查询的结果集有重复数据。
与DBA沟通,确认了客诉爆发前一天做了一次数据库变更,兄弟团队为了解决一个慢查询问题,增加了一个索引,变更sql如下:
ALTER TABLE `detail_yyyyMMdd` ADD INDEX `idx_batchno_status_productid` (`batch_no`, `status`, `product_id`);
DBA分析,该变更sql上线前,分页查询会走idx_batchno_userid索引,上线后则走idx_batchno_status_productid,而该索引存在大量重复记录,导致每次分页查询的数据都可能和之前的重复。
为什么索引有重复记录时,分页查询的数据就可能与之前的重复呢?在网上搜了下,这里贴一篇官方的文档:https://dev.mysql.com/doc/refman/5.6/en/limit-optimization.html
If multiple rows have identical values in the ORDER BY columns, the server is free to return those rows in any order, and may do so differently depending on the overall execution plan. In other words, the sort order of those rows is nondeterministic with respect to the nonordered columns.
One factor that affects the execution plan is LIMIT, so an ORDER BY query with and without LIMIT may return rows in different orders.
If it is important to ensure the same row order with and without LIMIT, include additional columns in the ORDER BY clause to make the order deterministic.
在MySQL 5.6的版本上,优化器在遇到order by limit语句时,做了个查询优化,即使用了priority queue(优先级队列)。采用priority queue能够根据limit N维护一个大小为N的堆,在排序的过程中,只用保留N条记录即可。但堆排序是一个不稳定的排序算法,所以当排序字段值存在重复时,返回的数据顺序可能会不一样,其中一个影响因素就是limit。
解决方案:查询语句加上order by id(保证排序字段的唯一性),上线后,问题得到解决。
但仍然存在两个疑点:
原来的索引idx_batchno_userid也会有重复记录,为什么一直没有爆出问题?
线上的查询语句只有limit,没有order by,可以直接按照索引的有序性(索引聚簇表)进行读取并分页,不需要触发priority queue优化。
3 问题定位
仔细观察了两次分页查询的结果集后发现,两次结果集的数据顺序分别对应两个索引,即第一次分页查询走了索引idx_batchno_userid,第二次分页查询走了索引idx_batchno_status_productid,两个索引的数据顺序是不一样的,从而导致两次分页查询的数据存在重复。与DBA交流,MySQL的优化器会基于成本选择最优的索引,而这两个索引的成本相差不大。
这个结论在线下得到复现,但并不能稳定复现。在总结果集没有变化的情况下,两次分页查询分别走了不同索引的根因,还有待继续深挖。
End
MySQL使用limit进行分页查询时,可能会出现重复数据,可以通过加上order by子句并保证排序字段的唯一性来解决。

MySQL分页查询limit踩坑记的更多相关文章
- mysql分页查询-limit
分页查询的sql: select * from table limit 4,10; 4表示查询的索引,索引是从0开始,4表示从第五条数据开始查询,10表示要查询多少条数据,10表示查询十条数据 如果从 ...
- Spark踩坑记——数据库(Hbase+Mysql)
[TOC] 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库,去统计或者改变一些值.最近一个实时消费者处理任务,在使用spark streami ...
- [转]Spark 踩坑记:数据库(Hbase+Mysql)
https://cloud.tencent.com/developer/article/1004820 Spark 踩坑记:数据库(Hbase+Mysql) 前言 在使用Spark Streaming ...
- Spark踩坑记——数据库(Hbase+Mysql)转
转自:http://www.cnblogs.com/xlturing/p/spark.html 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库 ...
- Spark踩坑记——共享变量
[TOC] 前言 Spark踩坑记--初试 Spark踩坑记--数据库(Hbase+Mysql) Spark踩坑记--Spark Streaming+kafka应用及调优 在前面总结的几篇spark踩 ...
- Spark踩坑记——Spark Streaming+Kafka
[TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...
- Spark踩坑记——从RDD看集群调度
[TOC] 前言 在Spark的使用中,性能的调优配置过程中,查阅了很多资料,之前自己总结过两篇小博文Spark踩坑记--初试和Spark踩坑记--数据库(Hbase+Mysql),第一篇概况的归纳了 ...
- 记一次 Spring 事务配置踩坑记
记一次 Spring 事务配置踩坑记 问题描述:(SpringBoot + MyBatisPlus) 业务逻辑伪代码如下.理论上,插入数据 t1 后,xxService.getXxx() 方法的查询条 ...
- Spark踩坑记:Spark Streaming+kafka应用及调优
前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark streaming从k ...
随机推荐
- 【python】Leetcode每日一题-二叉搜索迭代器
[python]Leetcode每日一题-二叉搜索迭代器 [题目描述] 实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器: BSTIterator(T ...
- [LeetCode每日一题]781. 森林中的兔子
[LeetCode每日一题]781. 森林中的兔子 问题 森林中,每个兔子都有颜色.其中一些兔子(可能是全部)告诉你还有多少其他的兔子和自己有相同的颜色.我们将这些回答放在 answers 数组里. ...
- 三、postman持久化及批量运行
一.设置环境变量 环境变量的引用为{{变量名}},运行脚本之前切记要在右上角选择对应脚本设置的环境变量后点击保存 二.设置全局变量 三.脚本导入导出及分享 1.导入脚本 2.导出脚本 3.分享脚本 四 ...
- mysql安装_图文详细安装步骤_让你轻松安装并使用(超详细步骤)
mysql的下载就不用说了,自行到官网下载..(本人下载的是mysql5.0版本) 下面开始正式安装 1.双击mysql_setup.exe后,直接点击Next 2.选择"I accept ...
- 【原创】JVM如何运行Java程序的?
[Deerhang] 我们知道Java程序的运行是依赖于JVM虚拟机的,JVM类语言经过编译生成class字节码文件,字节码又经JVM进一步的编译生成机器码,最终运行在硬件上.那么JVM存在的意义是什 ...
- 改善c++程序的150个建议(读后总结)-------27-35
27. 区分内存分配的方式 c++中内存分为5个不同的区 ①栈区 栈是一种特殊的数据结构,其存取数据特点为(先进后出,后进先出).栈区中主要用于存储一些函数的入口地址,函数调用时的实参值以及局部变量. ...
- 使用ldap客户端创建zimbra ldap用户的格式
cat << EOF | ldapadd -x -W -H ldap://:389 -D "uid=zimbra,cn=admins,cn=zimbra" dn: ui ...
- linux服务之NTP及chrony时间同步
博客园 首页 联系 管理 linux服务之NTP及chrony时间同步 一.NTP时间同步 NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步 ...
- jolokia配置Java监控
wget http://search.maven.org/remotecontent?filepath=org/jolokia/jolokia-jvm/1.3.6/jolokia-jvm-1.3.6- ...
- Linux创建RAID5_实战
Linux创建RAID5实战 Linux创建RAID5 RAID5最少由三个硬盘组成,它将数据分散存储于阵列中的每个硬盘,并且还伴有一个数据校验位,数据位与校验位通过算法能相互验证 RAID5最多能允 ...