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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 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)
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
177 9
|
1月前
|
存储 关系型数据库 MySQL
double ,FLOAT还是double(m,n)--深入解析MySQL数据库中双精度浮点数的使用
本文探讨了在MySQL中使用`float`和`double`时指定精度和刻度的影响。对于`float`,指定精度会影响存储大小:0-23位使用4字节单精度存储,24-53位使用8字节双精度存储。而对于`double`,指定精度和刻度对存储空间没有影响,但可以限制数值的输入范围,提高数据的规范性和业务意义。从性能角度看,`float`和`double`的区别不大,但在存储空间和数据输入方面,指定精度和刻度有助于优化和约束。
172 5
|
2月前
|
前端开发 Java Maven
深入解析:如何用 Spring Boot 实现分页和排序
深入解析:如何用 Spring Boot 实现分页和排序
102 2
|
2月前
|
监控 关系型数据库 MySQL
MySQL自增ID耗尽应对策略:技术解决方案全解析
在数据库管理中,MySQL的自增ID(AUTO_INCREMENT)属性为表中的每一行提供了一个唯一的标识符。然而,当自增ID达到其最大值时,如何处理这一情况成为了数据库管理员和开发者必须面对的问题。本文将探讨MySQL自增ID耗尽的原因、影响以及有效的应对策略。
195 3
|
2月前
|
存储 关系型数据库 MySQL
MySQL 字段类型深度解析:VARCHAR(50) 与 VARCHAR(500) 的差异
在MySQL数据库中,`VARCHAR`类型是一种非常灵活的字符串存储类型,它允许存储可变长度的字符串。然而,`VARCHAR(50)`和`VARCHAR(500)`之间的差异不仅仅是长度的不同,它们在存储效率、性能和使用场景上也有所不同。本文将深入探讨这两种字段类型的区别及其对数据库设计的影响。
114 2
|
2月前
|
存储 关系型数据库 MySQL
PHP与MySQL动态网站开发深度解析####
本文作为技术性文章,深入探讨了PHP与MySQL结合在动态网站开发中的应用实践,从环境搭建到具体案例实现,旨在为开发者提供一套详尽的实战指南。不同于常规摘要仅概述内容,本文将以“手把手”的教学方式,引导读者逐步构建一个功能完备的动态网站,涵盖前端用户界面设计、后端逻辑处理及数据库高效管理等关键环节,确保读者能够全面掌握PHP与MySQL在动态网站开发中的精髓。 ####
|
1天前
|
缓存 关系型数据库 MySQL
【深入了解MySQL】优化查询性能与数据库设计的深度总结
本文详细介绍了MySQL查询优化和数据库设计技巧,涵盖基础优化、高级技巧及性能监控。
10 0
|
28天前
|
存储 Oracle 关系型数据库
数据库传奇:MySQL创世之父的两千金My、Maria
《数据库传奇:MySQL创世之父的两千金My、Maria》介绍了MySQL的发展历程及其分支MariaDB。MySQL由Michael Widenius等人于1994年创建,现归Oracle所有,广泛应用于阿里巴巴、腾讯等企业。2009年,Widenius因担心Oracle收购影响MySQL的开源性,创建了MariaDB,提供额外功能和改进。维基百科、Google等已逐步替换为MariaDB,以确保更好的性能和社区支持。掌握MariaDB作为备用方案,对未来发展至关重要。
56 3
|
28天前
|
安全 关系型数据库 MySQL
MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!
《MySQL崩溃保险箱:探秘Redo/Undo日志确保数据库安全无忧!》介绍了MySQL中的三种关键日志:二进制日志(Binary Log)、重做日志(Redo Log)和撤销日志(Undo Log)。这些日志确保了数据库的ACID特性,即原子性、一致性、隔离性和持久性。Redo Log记录数据页的物理修改,保证事务持久性;Undo Log记录事务的逆操作,支持回滚和多版本并发控制(MVCC)。文章还详细对比了InnoDB和MyISAM存储引擎在事务支持、锁定机制、并发性等方面的差异,强调了InnoDB在高并发和事务处理中的优势。通过这些机制,MySQL能够在事务执行、崩溃和恢复过程中保持
66 3
|
28天前
|
SQL 关系型数据库 MySQL
数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog
《数据库灾难应对:MySQL误删除数据的救赎之道,技巧get起来!之binlog》介绍了如何利用MySQL的二进制日志(Binlog)恢复误删除的数据。主要内容包括: 1. **启用二进制日志**:在`my.cnf`中配置`log-bin`并重启MySQL服务。 2. **查看二进制日志文件**:使用`SHOW VARIABLES LIKE 'log_%';`和`SHOW MASTER STATUS;`命令获取当前日志文件及位置。 3. **创建数据备份**:确保在恢复前已有备份,以防意外。 4. **导出二进制日志为SQL语句**:使用`mysqlbinlog`
86 2

推荐镜像

更多