MySQL排序工作原理

数据库 MySQL
在程序设计当中,我们很多场景下都会用 group by 关键字。比如在分页读取数据时,为了避免重复扫描记录,这就是必须要使用 group by 了。

MySQL的 order by 工作原理

在程序设计当中,我们很多场景下都会用 group by 关键字。比如在分页读取数据时,为了避免重复扫描记录,这就是必须要使用 group by 了。

比如我们使用如下 DDL 创建表:

  1. CREATE TABLE `user_info` ( 
  2.  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID'
  3.  `city` varchar(16) NOT NULL COMMENT '城市'
  4.  `namevarchar(16) NOT NULL COMMENT '姓名'
  5.  `age` int(11) NOT NULL COMMENT '年龄'
  6.  `addr` varchar(128) DEFAULT NULL COMMENT '地址'
  7.  PRIMARY KEY (`id`), 
  8.  KEY `city` (`city`) 
  9. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 

并且我们会执行如下查询语句

  1. SELECT city,`name`,age FROM user_info WHERE city='上海' ORDER BY `name` LIMIT 1000; 

全字段排序

因为上面的建表语句已经在 city 字段上面创建索引了,当我们使用 EXPLAIN 命令时,会有如下结果:

MySQL的 order by 工作原理

上面 Extra 字段中的 “Using filesort” 表示的就是需要排序,MySQL 会为每个线程分配一块内存用于排序,成为 sort_buffer。下面我们看一下 index(city) 的结构示意图。

MySQL的 order by 工作原理

执行流程如下:

  1. 初始化 sort_buffer,确定放入 city name age 这 3 个字段;
  2. 从 city 索引中获取到***个 city='上海' 的记录,也就是 id_x;
  3. 到主键索引中获取对应的记录,并取出 name city age 的值放入 sort_buffer;
  4. 取下一条符合条件的记录,重复 3 4 的操作,直至不符合条件为止;
  5. 对 sort_buffer 中的数据按照 name 做快速排序;
  6. 取出前 1000 条数据并返回。

我们暂时叫这种排序过程为“全字段排序”,如下所示:

MySQL的 order by 工作原理

图中的“按 name 排序” 可能在内存中,也可能使用磁盘文件排序,这取决与排序所需要的内存和 sort_buffer_size 。sort_buffer_size 就是 MySQL 为排序开辟的内存大小,当所需内存小于 sort_buffer_size 时,就直接在内存中完成排序,如果所需要的内存 大于 sort_buffer_size ,就需要额外的磁盘空间辅助排序。

rowid 排序

上面的算法在数据量比较大的时候,可能会出现一些问题。因为在排序的时候,存放了所有的返回字段,增加了 排序空间 (sort_buffer)的压力。

  1. SET max_length_for_sort_data=16; 

max_length_for_sort_data 是MySQL 限制排序行大小的参数。意思是,如果排序行大小超过了这个值,就会另选排序算法。上面 name city age 3 个字段的大小为 36,大于 16 ,在新的算法中将只有 name (排序字段) 和id 参与 sort_buffer 中的排序。过程如下

  1. 初始化 sort_buffer,确定放入 name id 这 2 个字段;
  2. 从 city 索引中获取到***个 city='上海' 的记录,也就是 id_x;
  3. 到主键索引中获取对应的记录,并取出 name id 的值放入 sort_buffer;
  4. 取下一条符合条件的记录,重复 3 4 的操作,直至不符合条件为止;
  5. 对 sort_buffer 中的数据按照 name 做快速排序;
  6. 取出前 1000 条数据,然后根据 id 取出对应记录的 name city age 3 个字段并返回结果。

这种排序过程,我们称为 rowid 排序,过程如下所示:

MySQL的 order by 工作原理

全字段排序 VS rowid 排序

从上面 2 个流程看来,如果内存足够时,MySQL 会让返回值中所有字段存放在排序空间。当MySQL 内存过小时,才会考虑使用rowid 排序。但是从上面的流程看来,rowid 排序在返回结果前,还会再一次的回表。因此MySQL 认为内存充足的时候,会优先采用 全字段排序。

上面的场景是:city 字段过滤后,name 字段不是有序的。其实我们可以通过联合索引来规避掉 name 字段的排序。

  1. alter table user_info add index idx_city_user(city, name); 

下面我们看一下联合索引的示意图:

MySQL的 order by 工作原理

从上面流程图可以看出,当我们取出 city='上海' 的记录时,name的字段也是有序的。过程如下

  1. 从 (city, name)索引中获取到***个 city='上海' 的记录 id_x;
  2. 到主键索引中获取对应的记录,并取出 name city age 的值作为结果集的一部分直接返回;
  3. 取下一条符合条件的记录,重复 2 3 的操作,直至不符合条件或者达到 1000 条为止;

MySQL的 order by 工作原理

从联合索引看来,我们是可以不用排序操作了,那么我们是否可以直接通过 索引就直接返回结果呢?也就是不要回表操作。答案是有的,那就是覆盖索引。

  1. alter table user_info add index idx_city_user_age(city, name, age); 

当执行查询语句时,不仅 name 中的字段是有序的,并且 索引中已经包含了结果集中的所有字段,过程如下:

  1. 从 (city, name,age)索引中获取到***个 city='上海' 的记录,并取出 name city age 的值作为结果集的一部分直接返回;
  2. 取下一条符合条件的记录,重复 1 2 的操作,直至不符合条件或者达到 1000 条为止;

MySQL的 order by 工作原理

 

责任编辑:庞桂玉 来源: 今天头条
相关推荐

2010-11-25 10:28:28

MySQL查询优化器

2019-04-29 11:14:25

MySQL存储排序

2009-06-18 13:31:03

Spring工作原理

2009-08-14 13:19:23

2020-06-15 07:00:00

GitOpsKubernetesDevOps

2009-06-15 15:57:21

Spring工作原理

2009-07-16 10:23:30

iBATIS工作原理

2021-02-05 15:01:41

GitLinux命令

2010-09-25 13:11:48

DHCP工作原理

2015-11-04 09:23:17

JavaServlet工作原理

2010-05-05 16:47:57

Unix Tripwi

2010-02-04 10:43:05

Android DDM

2013-08-26 10:48:02

Reddit排名算法算法

2009-08-27 17:13:20

DHCP的工作原理

2020-05-19 09:11:20

机器学习技术数据

2011-03-25 09:34:34

Nagios网络监控

2013-09-18 14:01:46

JavaScript

2010-09-29 09:28:04

DHCP工作原理

2010-08-02 16:14:54

2009-07-06 12:32:26

JSP引擎
点赞
收藏

51CTO技术栈公众号