Mysql排序的特性详情


Posted in MySQL onNovember 01, 2021

1、问题场景

新上线一个交易记录导出功能,逻辑很简单:根据查询条件,导出对应的数据。由于数据量比较大,在查询数据库时采用了分页查询,每次查询1000条数据。

自测正常,测试环境正常,上线之后运营反馈导出的数据有重复记录

原本是以为业务逻辑问题,重新Review了一遍代码,依旧未找到问题原因。最后只好把SQL语句拿出来单独执行,导出数据,对比发现竟然是SQL语句查询结果乱序导致的。

2、原因分析

查询语句以create_time进行倒序排序,通过limit进行分页,在正常情况下不会出现问题。但当业务并发量比较大,导致create_time存在大量相同值时,再基于limit进行分页,就会出现乱序问题。

出现的场景是:create_time排序,当create_time存在相同值,通过limit分页,导致分页数据乱序。

比如,查询1000条数据,其中有一批create_time记录值都为”2021-10-28 12:12:12“,当创建时间相同的这些数据,一部分出现在第一页,一部分出现在第二页,在查询第二页的数据时,可能会出现第一页已经查过的数据。

也就是说,数据会来回跳动,一会儿出现在第一页,一会儿出现在第二页,这就导致导出的数据一部分重复,一部分缺失。

查看了Mysql 5.7和8.0的官方文档,描述如下:

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.

上述内容概述:在使用ORDER BY对列进行排序时,如果对应(ORDER BY的列)列存在多行相同数据,(Mysql)服务器会按照任意顺序返回这些行,并且可能会根据整体执行计划以不同的方式返回。

简单来说就是:ORDER BY查询的数据,如果ORDER BY列存在多行相同数据,Mysql会随机返回。这就会导致虽然使用了排序,但也会发生乱序的状况。

3、解决方案

针对上述问题,基本的解决思路是:避免ORDER BY列的值出现重复。因此,可以加入其他维度,比如ID等其他排序列。

select * from tb_order order by create_time ,id desc;

这样,在create_time相同时,会根据id进行排序,而id肯定是不同的,就再不会出现上述问题了。

4、拓展知识

其实,上述内容在Mysql的官网已经有明确说明,而且还举了例子。下面对官网的内容和例子做一个简单的汇总总结。

4.1 limit查询优化

如果我们只是查询一个结果集的一部分,那么不要查询所有数据,然后再丢弃不需要的数据,而是要通过limit条件来进行限制。

在没使用having条件时,Mysql可能会对limit条件优化:

  • 如果只查询几条数据,建议使用limit,这样Mysql可能会用到索引,而通常情况下Mysql是全表扫描;
  • 如果将limit row_countorder by结合使用,Mysql会在找到第一个row_count结果集后立刻停止排序,而不是对整个结果集进行排序。如果此时基于索引进行操作,速度会更快。如果必须进行文件排序,在找到row_count结果集之前,会对部分或所有符合条件的结果进行排序。但当找到row_count结果之后,便不会对剩余部分进行排序了。这种特性的一个表现就是我们前面提到的带有limit和不带limit进行查询时,返回的结果顺序可能不同。
  • 如果将limit row_count和distinct结合使用,Mysql会在找到row_count结果集唯一行后立马停止。
  • 在某些情况下,可以通过按照顺序读取索引(或对索引进行排序),然后计算摘要直到索引变化来实现group by。在这种情况下,limit row_count不会计算任何不必要的group by值。
  • 一旦MySQL向客户端发送了所需数量的行,就会中止查询,除非使用了SQL_CALC_FOUND_ROWS。在这种情况下,可以使用 SELECT FOUND_ROWS() 检索行数。
  • LIMIT 0会快速返回一个空集合,通常可用于检查SQL的有效性。还可以用于在应用程序中获得结果集的类型。在Mysql客户端中,可以使用--column-type-info来显示结果列类型。
  • 如果使用临时表来解析查询,Mysql会使用 limit row_count来计算需要多少空间。
  • 如果order by未使用索引,且存在limit条件,则优化器可能会避免使用合并文件,而采用内存filesort操作对内存中的行进行排序。

了解了limit的一些特性,下面再回到本文的重点,limit row_countorder by结合使用特性。

4.2 limit与order by结合使用

在上面第二条中已经提到,limit row_countorder by结合呈现的特性之一就是结果返回的顺序是不确定的。而影响执行计划的一个因素就是limit,因此带有limit与不带有limit执行同样的查询语句,返回结果的顺序可能不同。

下面示例中,根据category列进行排序查询,而id和rating是不确定的:

mysql> SELECT * FROM ratings ORDER BY category;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
|  1 |        1 |    4.5 |
|  5 |        1 |    3.2 |
|  3 |        2 |    3.7 |
|  4 |        2 |    3.5 |
|  6 |        2 |    3.5 |
|  2 |        3 |    5.0 |
|  7 |        3 |    2.7 |
+----+----------+--------+

当查询语句包含limit时,可能会影响到category值相同的数据:

mysql> SELECT * FROM ratings ORDER BY category LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
|  1 |        1 |    4.5 |
|  5 |        1 |    3.2 |
|  4 |        2 |    3.5 |
|  3 |        2 |    3.7 |
|  6 |        2 |    3.5 |
+----+----------+--------+

其中id为3和4的结果位置发生了变化。

在实践中,保持查询结果的顺序性往往非常重要,此时就需要引入其他列来保证结果的顺序性了。

当上述实例引入id之后,查询语句及结果如下:

mysql> SELECT * FROM ratings ORDER BY category, id;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
|  1 |        1 |    4.5 |
|  5 |        1 |    3.2 |
|  3 |        2 |    3.7 |
|  4 |        2 |    3.5 |
|  6 |        2 |    3.5 |
|  2 |        3 |    5.0 |
|  7 |        3 |    2.7 |
+----+----------+--------+
​
mysql> SELECT * FROM ratings ORDER BY category, id LIMIT 5;
+----+----------+--------+
| id | category | rating |
+----+----------+--------+
|  1 |        1 |    4.5 |
|  5 |        1 |    3.2 |
|  3 |        2 |    3.7 |
|  4 |        2 |    3.5 |
|  6 |        2 |    3.5 |
+----+----------+--------+

可以看出,当添加了id列的排序,即使category相同,也不会出现乱序问题。这正与我们最初的解决方案一致。

5、小结

本来通过实践中偶发的一个坑,聊到了Mysqllimit查询语句的优化,同时提供了解决方案,即满足了业务需求,又避免了业务逻辑的错误。

很多朋友都在使用order bylimit语句进行查询,但如果不知道Mysql的这些优化特性,很可能已经入坑,只不过数据量没有触发呈现而已。

到此这篇关于Mysql排序的特性详情的文章就介绍到这了,更多相关Mysql排序特性内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

MySQL 相关文章推荐
浅析InnoDB索引结构
Apr 05 MySQL
MySQL入门命令之函数-单行函数-流程控制函数
Apr 05 MySQL
MySQL索引篇之千万级数据实战测试
Apr 05 MySQL
MySQL 存储过程的优缺点分析
May 20 MySQL
MySQL注入基础练习
May 30 MySQL
为什么MySQL分页用limit会越来越慢
Jul 25 MySQL
为什么MySQL 删除表数据 磁盘空间还一直被占用
Oct 16 MySQL
浅谈MySQL中的六种日志
Mar 23 MySQL
单机多实例部署 MySQL8.0.20
May 15 MySQL
MySQL 语句执行顺序举例解析
Jun 05 MySQL
Mysql的Table doesn't exist问题及解决
Dec 24 MySQL
详解MySQL的内连接和外连接
May 08 MySQL
MySQL分区表实现按月份归类
Nov 01 #MySQL
MySQL数据库10秒内插入百万条数据的实现
MySQL面试题讲解之如何设置Hash索引
MySQL对数据表已有表进行分区表的实现
Nov 01 #MySQL
mysql分表之后如何平滑上线详解
Nov 01 #MySQL
MySQL8.0升级的踩坑历险记
Nov 01 #MySQL
详细聊聊关于Mysql联合查询的那些事儿
Oct 24 #MySQL
You might like
PHP MemCached高级缓存配置图文教程
2010/08/05 PHP
php更新mysql后获取影响的行数发生异常解决方法
2013/03/28 PHP
PhpStorm的使用教程(本地运行PHP+远程开发+快捷键)
2020/03/26 PHP
javascript qq右下角滑出窗口 sheyMsg
2010/03/21 Javascript
由JavaScript技术实现的web小游戏(不含网游)
2010/06/12 Javascript
通过Javascript将数据导出到外部Excel文档的函数代码
2012/06/15 Javascript
左侧是表头的JS表格控件(自写,网上没有的)
2013/06/04 Javascript
使用jQuery不判断浏览器高度解决iframe自适应高度问题
2014/12/16 Javascript
JavaScript实现节点的删除与序号重建实例
2015/08/05 Javascript
jquery实现可横向和竖向展开的动态下滑菜单效果
2015/08/24 Javascript
js生成随机数的方法实例
2015/10/16 Javascript
JavaScript职责链模式概述
2016/09/17 Javascript
vue实现app页面切换动画效果实例
2017/05/23 Javascript
JavaScript切换搜索引擎的导航网页搜索框实例代码
2017/06/11 Javascript
vue中动态设置meta标签和title标签的方法
2018/07/11 Javascript
用smtplib和email封装python发送邮件模块类分享
2014/02/17 Python
使用Python脚本来控制Windows Azure的简单教程
2015/04/16 Python
在Django的URLconf中进行函数导入的方法
2015/07/18 Python
Python通过DOM和SAX方式解析XML的应用实例分享
2015/11/16 Python
下载python中Crypto库报错:ModuleNotFoundError: No module named ‘Crypto’的解决
2018/04/23 Python
flask框架实现连接sqlite3数据库的方法分析
2018/07/16 Python
对python使用telnet实现弱密码登录的方法详解
2019/01/26 Python
Python考拉兹猜想输出序列代码实践
2019/07/05 Python
django框架使用方法详解
2019/07/18 Python
解析pip安装第三方库但PyCharm中却无法识别的问题及PyCharm安装第三方库的方法教程
2020/03/10 Python
Django更新models数据库结构步骤
2020/04/01 Python
利用keras使用神经网络预测销量操作
2020/07/07 Python
pip install命令安装扩展库整理
2021/03/02 Python
CSS3实现DIV圆角效果完整代码
2012/10/10 HTML / CSS
详解HTML5 window.postMessage与跨域
2017/05/11 HTML / CSS
英国高街品牌:Miss Selfridge(塞尔弗里奇小姐)
2016/09/21 全球购物
西部世纪.net笔试题面试题
2014/04/03 面试题
采购部经理岗位职责
2014/02/10 职场文书
党的群众教育实践活动实施方案
2014/06/12 职场文书
群众路线教育查摆剖析材料
2014/10/10 职场文书
Nginx代理Redis哨兵主从配置的实现
2022/07/15 Servers