16 | “order by”是怎么工作的?

Page content

16 | “order by”是怎么工作的?

MySQL 在哪里排序

  • 内存排序 : MySQL为每个线程分配一块内存用于排序,数据量少的时候在这里进行。具体大小通过 sort_buffer_size 控制。
  • 磁盘临时文件排序 : 数据量大于 sort_buffer_size 时,使用多个磁盘临时文件排序,有点类似于归并排序

MySQL排序方案

建表如下:

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `city` varchar(16) NOT NULL,
  `name` varchar(16) NOT NULL,
  `age` int(11) NOT NULL,
  `addr` varchar(128) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `city` (`city`)
) ENGINE=InnoDB;

排序语句如下:

select city,name,age from t where city='杭州' order by name limit 1000;

1. 全字段排序

  • (1)初始化 sort_buffer,确定放入 name、city、age 这三个字段;
  • (2)从索引 city 找到第一个满足 city=‘杭州’条件的主键 id
  • (3)到主键 id 索引取出整行,取 name、city、age 三个字段的值,存入 sort_buffer 中;
  • (4)从索引 city 取下一个记录的主键 id;
  • (5)重复步骤 3、4 直到 city 的值不满足查询条件为止,对应的主键 id 也就是图中的 ID_Y;
  • (6)对 sort_buffer 中的数据按照字段 name 做快速排序;
  • (7)按照排序结果取前 1000 行返回给客户端。

2. rowid排序

  • (1)初始化 sort_buffer,确定放入两个字段,即 name 和 id;
  • (2)从索引 city 找到第一个满足 city=‘杭州’条件的主键 id
  • (3)到主键 id 索引取出整行,取 name、id 这两个字段,存入 sort_buffer 中;
  • (4)从索引 city 取下一个记录的主键 id;
  • (5)重复步骤 3、4 直到不满足 city=‘杭州’条件为止
  • (6)对 sort_buffer 中的数据按照字段 name 进行排序;
  • (7)遍历排序结果,取前 1000 行,并按照 id 的值回到原表中取出 city、name 和 age 三个字段返回给客户端。

全字段排序 VS rowid排序

  • rowid排序多了一次回表操作,会多造成磁盘读操作,不会被优先选择
  • 如果内存够,就要多利用内存,尽量减少磁盘访问

问题讨论

假设你的表里面已经有了 city_name(city, name) 这个联合索引,然后你要查杭州和苏州两个城市中所有的市民的姓名,并且按名字排序,显示前 100 条记录。如果 SQL 查询语句是这么写的 : select * from t where city in ('杭州'," 苏州 ") order by name limit 100; (1)那么,这个语句执行的时候会有排序过程吗,为什么? (2)如果业务端代码由你来开发,需要实现一个在数据库端不需要排序的方案,你会怎么实现呢? (3)进一步地,如果有分页需求,要显示第 101 页,也就是说语句最后要改成 “limit 10000,100”, 你的实现方法又会是什么呢?

答案 (1) 会排序,如果只查一个城市可以用到覆盖索引的特性。 (2) 分别执行 select * from t where city=“杭州” order by name limit 100;select * from t where city=“苏州” order by name limit 100; 对得到的结果业务层面进行一次归并排序. (3) 类似(2),把 limit 100 改成 limit 10100