MySQL:排序(filesort)详细解析(8000字长文1)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: MySQL:排序(filesort)详细解析(8000字长文)

能力有限有误请指出。

本文使用源码版本:5.7.22

引擎为:Innodb

排序(filesort)作为DBA绕不开的话题,也经常有朋友讨论它,比如常见的问题如下:

  • 排序的时候,用于排序的数据会不会如Innodb一样压缩空字符存储,比如varchar(30),我只是存储了1个字符是否会压缩,还是按照30个字符计算?
  • max_length_for_sort_data/max_sort_length 到底是什么含义?
  • original filesort algorithm(回表排序) 和 modified filesort algorithm(不回表排序) 的根本区别是什么?
  • 为什么使用到排序的时候慢查询中的Rows_examined会更大,计算方式到底是什么样的?

在MySQL通常有如下算法来完成排序:

  • 内存排序(优先队列 order by limit 返回少量行常用,提高排序效率,但是注意order by limit n,m 如果n过大可能会涉及到排序算法的切换)
  • 内存排序(快速排序)
  • 外部排序(归并排序)

但是由于能力有限本文不解释这些算法,并且本文不考虑优先队列算法的分支逻辑,只以快速排序和归并排序作为基础进行流程剖析。我们在执行计划中如果出现filesort字样通常代表使用到了排序,但是执行计划中看不出来下面问题:

  • 是否使用了临时文件。
  • 是否使用了优先队列。
  • 是original filesort algorithm(回表排序)还是modified filesort algorithm(不回表排序)。

如何查看将在后面进行描述。本文还会给出大量的排序接口供感兴趣的朋友使用,也给自己留下笔记。

一、从一个问题出发

这是最近一个朋友遇到的案例,大概意思就是说我的表在Innodb中只有30G左右,为什么使用如下语句进行排序操作后临时文件居然达到了200多G,当然语句很变态,我们可以先不问为什么会有这样的语句,我们只需要研究原理即可,在本文的第13节会进行原因解释和问题重现。

临时文件如下:

image.png

下面是这些案例信息:

  1. show create table t\G
  2. *************************** 1. row ***************************
  3. Table: t
  4. CreateTable: CREATE TABLE `t`(
  5. `ID` bigint(20) NOT NULL COMMENT 'ID',
  6. `UNLOAD_TASK_NO` varchar(50) NOT NULL ,
  7. `FORKLIFT_TICKETS_COUNT` bigint(20) DEFAULT NULL COMMENT '叉车票数',
  8. `MANAGE_STATUS` varchar(20) DEFAULT NULL COMMENT '管理状态',
  9. `TRAY_BINDING_TASK_NO` varchar(50) NOT NULL ,
  10. `STATISTIC_STATUS` varchar(50) NOT NULL ,
  11. `CREATE_NO` varchar(50) DEFAULT NULL ,
  12. `UPDATE_NO` varchar(50) DEFAULT NULL ,
  13. `CREATE_NAME` varchar(200) DEFAULT NULL COMMENT '创建人名称',
  14. `UPDATE_NAME` varchar(200) DEFAULT NULL COMMENT '更新人名称',
  15. `CREATE_ORG_CODE` varchar(200) DEFAULT NULL COMMENT '创建组织编号',
  16. `UPDATE_ORG_CODE` varchar(200) DEFAULT NULL COMMENT '更新组织编号',
  17. `CREATE_ORG_NAME` varchar(1000) DEFAULT NULL COMMENT '创建组织名称',
  18. `UPDATE_ORG_NAME` varchar(1000) DEFAULT NULL COMMENT '更新组织名称',
  19. `CREATE_TIME` datetime DEFAULT NULL COMMENT '创建时间',
  20. `UPDATE_TIME` datetime DEFAULT NULL COMMENT '更新时间',
  21. `DATA_STATUS` varchar(50) DEFAULT NULL COMMENT '数据状态',
  22. `OPERATION_DEVICE` varchar(200) DEFAULT NULL COMMENT '操作设备',
  23. `OPERATION_DEVICE_CODE` varchar(200) DEFAULT NULL COMMENT '操作设备编码',
  24. `OPERATION_CODE` varchar(50) DEFAULT NULL COMMENT '操作码',
  25. `OPERATION_ASSIST_CODE` varchar(50) DEFAULT NULL COMMENT '辅助操作码',
  26. `CONTROL_STATUS` varchar(50) DEFAULT NULL COMMENT '控制状态',
  27. `OPERATOR_NO` varchar(50) DEFAULT NULL COMMENT '操作人工号',
  28. `OPERATOR_NAME` varchar(200) DEFAULT NULL COMMENT '操作人名称',
  29. `OPERATION_ORG_CODE` varchar(50) DEFAULT NULL COMMENT '操作部门编号',
  30. `OPERATION_ORG_NAME` varchar(200) DEFAULT NULL COMMENT '操作部门名称',
  31. `OPERATION_TIME` datetime DEFAULT NULL COMMENT '操作时间',
  32. `OPERATOR_DEPT_NO` varchar(50) NOT NULL COMMENT '操作人所属部门编号',
  33. `OPERATOR_DEPT_NAME` varchar(200) NOT NULL COMMENT '操作人所属部门名称',
  34. `FORKLIFT_DRIVER_NAME` varchar(200) DEFAULT NULL ,
  35. `FORKLIFT_DRIVER_NO` varchar(50) DEFAULT NULL ,
  36. `FORKLIFT_DRIVER_DEPT_NAME` varchar(200) DEFAULT NULL ,
  37. `FORKLIFT_DRIVER_DEPT_NO` varchar(50) DEFAULT NULL ,
  38. `FORKLIFT_SCAN_TIME` datetime DEFAULT NULL ,
  39. `OUT_FIELD_CODE` varchar(200) DEFAULT NULL,
  40. PRIMARY KEY (`ID`),
  41. KEY `IDX_TRAY_BINDING_TASK_NO`(`TRAY_BINDING_TASK_NO`),
  42. KEY `IDX_OPERATION_ORG_CODE`(`OPERATION_ORG_CODE`),
  43. KEY `IDX_OPERATION_TIME`(`OPERATION_TIME`)
  44. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=8



  45. desc
  46. SELECT
  47. ID,
  48. UNLOAD_TASK_NO,
  49. FORKLIFT_TICKETS_COUNT,
  50. MANAGE_STATUS,
  51. TRAY_BINDING_TASK_NO,
  52. STATISTIC_STATUS,
  53. CREATE_NO,
  54. UPDATE_NO,
  55. CREATE_NAME,
  56. UPDATE_NAME,
  57. CREATE_ORG_CODE,
  58. UPDATE_ORG_CODE,
  59. CREATE_ORG_NAME,
  60. UPDATE_ORG_NAME,
  61. CREATE_TIME,
  62. UPDATE_TIME,
  63. DATA_STATUS,
  64. OPERATION_DEVICE,
  65. OPERATION_DEVICE_CODE,
  66. OPERATION_CODE,
  67. OPERATION_ASSIST_CODE,
  68. CONTROL_STATUS,
  69. OPERATOR_NO,
  70. OPERATOR_NAME,
  71. OPERATION_ORG_CODE,
  72. OPERATION_ORG_NAME,
  73. OPERATION_TIME,
  74. OPERATOR_DEPT_NO,
  75. OPERATOR_DEPT_NAME,
  76. FORKLIFT_DRIVER_NAME,
  77. FORKLIFT_DRIVER_NO,
  78. FORKLIFT_DRIVER_DEPT_NAME,
  79. FORKLIFT_DRIVER_DEPT_NO,
  80. FORKLIFT_SCAN_TIME,
  81. OUT_FIELD_CODE
  82. FROM
  83. t
  84. GROUP BY id , UNLOAD_TASK_NO , FORKLIFT_TICKETS_COUNT ,
  85. MANAGE_STATUS , TRAY_BINDING_TASK_NO , STATISTIC_STATUS ,
  86. CREATE_NO , UPDATE_NO , CREATE_NAME , UPDATE_NAME ,
  87. CREATE_ORG_CODE , UPDATE_ORG_CODE , CREATE_ORG_NAME ,
  88. UPDATE_ORG_NAME , CREATE_TIME , UPDATE_TIME , DATA_STATUS ,
  89. OPERATION_DEVICE , OPERATION_DEVICE_CODE , OPERATION_CODE ,
  90. OPERATION_ASSIST_CODE , CONTROL_STATUS , OPERATOR_NO ,
  91. OPERATOR_NAME , OPERATION_ORG_CODE , OPERATION_ORG_NAME ,
  92. OPERATION_TIME , OPERATOR_DEPT_NO , OPERATOR_DEPT_NAME ,
  93. FORKLIFT_DRIVER_NAME , FORKLIFT_DRIVER_NO ,
  94. FORKLIFT_DRIVER_DEPT_NAME , FORKLIFT_DRIVER_DEPT_NO ,
  95. FORKLIFT_SCAN_TIME , OUT_FIELD_CODE;


  96. +----+-------------+-------------------------+------------+------+---------------+------+---------+------+---------+----------+----------------+
  97. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref| rows | filtered | Extra|
  98. +----+-------------+-------------------------+------------+------+---------------+------+---------+------+---------+----------+----------------+
  99. | 1| SIMPLE | t | NULL | ALL | NULL | NULL | NULL | NULL | 5381145| 100.00| Using filesort |
  100. +----+-------------+-------------------------+------------+------+---------------+------+---------+------+---------+----------+----------------+
  101. 1 row inset, 1 warning (0.00 sec)


也许你会怀疑这个语句有什么用,我们先不考虑功能,我们只考虑为什么它会生成200G的临时文件这个问题。接下来我将分阶段进行排序的流程解析,注意了整个排序的流程均处于状态‘Creating sort index’下面,我们以filesort函数接口为开始进行分析。

二、测试案例为了更好的说明后面的流程我们使用2个除了字段长度不同,其他完全一样的表来说明,但是需要注意这两个表数据量很少,不会出现外部排序,如果涉及外部排序的时候我们需要假设它们数据量很大。其次这里根据original filesort algorithm和modified filesort algorithm进行划分,但是这两种方法还没讲述,不用太多理会。

  • original filesort algorithm(回表排序)
  1. mysql> show create table tests1 \G
  2. *************************** 1. row ***************************
  3. Table: tests1
  4. CreateTable: CREATE TABLE `tests1`(
  5. `a1` varchar(300) DEFAULT NULL,
  6. `a2` varchar(300) DEFAULT NULL,
  7. `a3` varchar(300) DEFAULT NULL
  8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  9. 1 row inset(0.00 sec)

  10. mysql> select* from tests1;
  11. +------+------+------+
  12. | a1 | a2 | a3 |
  13. +------+------+------+
  14. | a | a | a |
  15. | a | b | b |
  16. | a | c | c |
  17. | b | d | d |
  18. | b | e | e |
  19. | b | f | f |
  20. | c | g | g |
  21. | c | h | h |
  22. +------+------+------+
  23. 8 rows inset(0.00 sec)

  24. mysql> desc select* from tests1 where a1='b' order by a2,a3;
  25. +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
  26. | id | select_type | table | partitions | type | possible_keys | key | key_len | ref| rows | filtered | Extra|
  27. +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
  28. | 1| SIMPLE | tests1 | NULL | ALL | NULL | NULL | NULL | NULL | 8| 12.50| Usingwhere; Using filesort |
  29. +----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
  30. 1 row inset, 1 warning (0.00 sec)
            </div>
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
3月前
|
存储 SQL 关系型数据库
MySQL中binlog、redolog与undolog的不同之处解析
每个都扮演回答回溯与错误修正机构角色: BinLog像历史记载员详细记载每件大大小小事件; RedoLog则像紧急救援队伍遇见突發情況追踪最后活动轨迹尽力补救; UndoLog就类似时间机器可倒带历史让一切归位原始样貌同时兼具平行宇宙观察能让多人同时看见各自期望看见历程而互不干扰.
205 9
|
4月前
|
存储 SQL 关系型数据库
MySQL 核心知识与索引优化全解析
本文系统梳理了 MySQL 的核心知识与索引优化策略。在基础概念部分,阐述了 char 与 varchar 在存储方式和性能上的差异,以及事务的 ACID 特性、并发事务问题及对应的隔离级别(MySQL 默认 REPEATABLE READ)。 索引基础部分,详解了 InnoDB 默认的 B+tree 索引结构(多路平衡树、叶子节点存数据、双向链表支持区间查询),区分了聚簇索引(数据与索引共存,唯一)和二级索引(数据与索引分离,多个),解释了回表查询的概念及优化方法,并分析了 B+tree 作为索引结构的优势(树高低、效率稳、支持区间查询)。 索引优化部分,列出了索引创建的六大原则
123 2
|
3月前
|
存储 关系型数据库 MySQL
MySQL中实施排序(sorting)及分组(grouping)操作的技巧。
使用这些技巧时,需要根据实际的数据量、表的设计和服务器性能等因素来确定最合适的做法。通过反复测试和优化,可以得到最佳的查询性能。
264 0
|
11月前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
2295 10
|
4月前
|
存储 SQL 关系型数据库
MySQL 核心知识与性能优化全解析
我整理的这份内容涵盖了 MySQL 诸多核心知识。包括查询语句的书写与执行顺序,多表查询的连接方式及内、外连接的区别。还讲了 CHAR 和 VARCHAR 的差异,索引的类型、底层结构、聚簇与非聚簇之分,以及回表查询、覆盖索引、左前缀原则和索引失效情形,还有建索引的取舍。对比了 MyISAM 和 InnoDB 存储引擎的不同,提及性能优化的多方面方法,以及超大分页处理、慢查询定位与分析等,最后提到了锁和分库分表可参考相关资料。
109 0
|
5月前
|
关系型数据库 MySQL
MySQL字符串拼接方法全解析
本文介绍了四种常用的字符串处理函数及其用法。方法一:CONCAT,用于基础拼接,参数含NULL时返回NULL;方法二:CONCAT_WS,带分隔符拼接,自动忽略NULL值;方法三:GROUP_CONCAT,适用于分组拼接,支持去重、排序和自定义分隔符;方法四:算术运算符拼接,仅适用于数值类型,字符串会尝试转为数值处理。通过示例展示了各函数的特点与应用场景。
|
7月前
|
SQL 运维 关系型数据库
MySQL Binlog 日志查看方法及查看内容解析
本文介绍了 MySQL 的 Binlog(二进制日志)功能及其使用方法。Binlog 记录了数据库的所有数据变更操作,如 INSERT、UPDATE 和 DELETE,对数据恢复、主从复制和审计至关重要。文章详细说明了如何开启 Binlog 功能、查看当前日志文件及内容,并解析了常见的事件类型,包括 Format_desc、Query、Table_map、Write_rows、Update_rows 和 Delete_rows 等,帮助用户掌握数据库变化历史,提升维护和排障能力。
|
8月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
9月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》图、查找、排序专题考点(含解析)
408考研——《数据结构》图,查找和排序专题考点选择题汇总(含解析)。
491 29
|
11月前
|
存储 关系型数据库 MySQL
double ,FLOAT还是double(m,n)--深入解析MySQL数据库中双精度浮点数的使用
本文探讨了在MySQL中使用`float`和`double`时指定精度和刻度的影响。对于`float`,指定精度会影响存储大小:0-23位使用4字节单精度存储,24-53位使用8字节双精度存储。而对于`double`,指定精度和刻度对存储空间没有影响,但可以限制数值的输入范围,提高数据的规范性和业务意义。从性能角度看,`float`和`double`的区别不大,但在存储空间和数据输入方面,指定精度和刻度有助于优化和约束。
1655 5

推荐镜像

更多