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踩坑记的更多相关文章

  1. mysql分页查询-limit

    分页查询的sql: select * from table limit 4,10; 4表示查询的索引,索引是从0开始,4表示从第五条数据开始查询,10表示要查询多少条数据,10表示查询十条数据 如果从 ...

  2. Spark踩坑记——数据库(Hbase+Mysql)

    [TOC] 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库,去统计或者改变一些值.最近一个实时消费者处理任务,在使用spark streami ...

  3. [转]Spark 踩坑记:数据库(Hbase+Mysql)

    https://cloud.tencent.com/developer/article/1004820 Spark 踩坑记:数据库(Hbase+Mysql) 前言 在使用Spark Streaming ...

  4. Spark踩坑记——数据库(Hbase+Mysql)转

    转自:http://www.cnblogs.com/xlturing/p/spark.html 前言 在使用Spark Streaming的过程中对于计算产生结果的进行持久化时,我们往往需要操作数据库 ...

  5. Spark踩坑记——共享变量

    [TOC] 前言 Spark踩坑记--初试 Spark踩坑记--数据库(Hbase+Mysql) Spark踩坑记--Spark Streaming+kafka应用及调优 在前面总结的几篇spark踩 ...

  6. Spark踩坑记——Spark Streaming+Kafka

    [TOC] 前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark strea ...

  7. Spark踩坑记——从RDD看集群调度

    [TOC] 前言 在Spark的使用中,性能的调优配置过程中,查阅了很多资料,之前自己总结过两篇小博文Spark踩坑记--初试和Spark踩坑记--数据库(Hbase+Mysql),第一篇概况的归纳了 ...

  8. 记一次 Spring 事务配置踩坑记

    记一次 Spring 事务配置踩坑记 问题描述:(SpringBoot + MyBatisPlus) 业务逻辑伪代码如下.理论上,插入数据 t1 后,xxService.getXxx() 方法的查询条 ...

  9. Spark踩坑记:Spark Streaming+kafka应用及调优

    前言 在WeTest舆情项目中,需要对每天千万级的游戏评论信息进行词频统计,在生产者一端,我们将数据按照每天的拉取时间存入了Kafka当中,而在消费者一端,我们利用了spark streaming从k ...

随机推荐

  1. 【JavaScript】Leetcode每日一题-在D天内送包裹的能力

    [JavaScript]Leetcode每日一题-在D天内送包裹的能力 [题目描述] 传送带上的包裹必须在 D 天内从一个港口运送到另一个港口. 传送带上的第 i 个包裹的重量为 weights[i] ...

  2. Docker为PHP安装gd扩展

    安装扩展库的通常命令 docker-php-ext-install 扩展库名 安装gd库需要特殊照顾,步骤如下 //进入PHP容器 //更新软件源 apt update //安装各种库 apt ins ...

  3. leetcode 1081

    开始的思路是遍历存储每个字符的所有位置,再进行扫描处理,但是实际操作并没有很熟练,于是在讨论区学习后,有了下面的解法! 首先需要知道不同的字符在字符串中的最后的位置(理论上的最优位置) 然后扫描字符串 ...

  4. OCR-Form-Tools项目试玩记录(一)本地部署

    OCR-Form-Tools是微软的一个开源代码库,包含微软表单识别和OCR服务相关的多种工具.Github仓库地址 目前,表单标注工具(OCR Form Labeling Tool)是首个发布到本该 ...

  5. Spring循环依赖问题的解决

    循环依赖问题 一个bean的创建分为如下步骤: 当创建一个简单对象的时候,过程如下: 先从单例池中获取bean,发现无 a 创建 a 的实例 为 a 赋值 把 a 放到单例池中 当创建一个对象并且其中 ...

  6. sed -n "29496,29516p" service.log:从29496行开始检索,到29516行结束

    在工作中常用的Linux命令  javalinux 发布于 2019-07-24   约 11 分钟 前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://gith ...

  7. Linux_yum仓库管理

    一.yum配置本地仓库 1.挂载光盘 [root@localhost ~]# mount /dev/sr0 /mnt/ [root@localhost ~]# df -hT 文件系统 类型 容量 已用 ...

  8. sersync 实时同步网站数据

    sersync同步实战 sersync是基于inotify和srync的二次开发,代码托管在github上面在rsync的客户端上安装,下载地址: https://raw.githubusercont ...

  9. Java 运行 Jar 包中java -cp 与 java jar 的区别

    java -cp java -cp 和 -classpath 一样,是指定类运行所依赖其他类的路径,通常是类库和jar包,需要全路径到jar包,多个jar包之间连接符:window上分号"; ...

  10. JavaEE 学大数据是否掌握 JavaSE 和 Linux 就够了?

    引言 如果你是学习大数据的童靴,可能经常在网上看到一些公众号或博客告诉你,学习大数据基础部分只需要掌握 JavaSE 和 Linux 就够了,至于 JavaWeb 和 JavaEE 简单了解一下就可以 ...