MySQL:排序(order by)

MySQL是如何进行排序的?

假设有一个表t结构如下图所示:


id为主键,type上建有索引,那么如果要查类型为1,val最小的1000行,那么SQL语句如下:
SELECT type, val, detail FROM t WHERE type = 1 ORDER BY val LIMIT 1000;

全字段排序

对上述查询执行explain结果如下:


Using filesort表示需要排序,MySQL会给每个线程分配一块内存用来排序,称为sort buffer,具体的流程如下:

  1. 初始化sort buffer,确定放入type,val,detail三个字段
  2. 从索引type中找到第一个满足type=1条件的主键id
  3. 根据id回主键索引查询type和val的值存入sort buffer中,从索引type中继续取下一个id
  4. 重复3的操作直到type不满足条件
  5. 对sort buffer中的数据按照val字段做快速排序
  6. 按照排序结果取前1000行返回

如果sort buffer够存下所有需要排序的记录,排序在内存中完成,如果内存放不下则需要借助磁盘临时文件进行外部排序。

rowid排序

全字段排序过程里只对原表扫描的一遍,剩下的操作都是在sort buffer 和临时文件中执行的,但是如果要查询的字段比较多,sort buffer能存的行数就很少,需要分成多个临时文件进行外部排序,性能比较差,所以在单行数大的情况下这种方式明显不合适。

MySQL的参数max_length_for_sort_data表示如果单行记录长度超过这个值,就认为单行太大,要换一种排序算法,排序过程中只放要排序的列和主键id,执行流程如下:

  1. 初始化sort buffer,放入val,id字段
  2. 从索引type中找到第一个满足type=1条件的主键id
  3. 根据id回主键索引查询val的值,将val和id存入sort buffer中,从索引type中继续取下一个id
  4. 重复3的操作直到type不满足条件
  5. 对sort buffer中的数据按照val字段做快速排序
  6. 按照排序结果依次取1000行,并按照id值回表取出type,val,detail三个字段返回

可以看到改流程与全字段排序的主要区别在于:

  • 第1步放入sort buffer的字段不同,rowid排序只放入排序字段和id,全字段排序放入查询的全部字段
  • 第6步,rowid排序完成后要再回主键索引查一次全部数据返回,全字段排序因为所以要返回的字段内容都在sort buffer中了所以直接返回

说明:结果集只是一个逻辑概念,实际上MySQL从排序后的sort buffer中依次取出id,然后到原表查询所有字段的结果不需要在服务端再消耗内存保存,是直接返回的。

联合索引避免排序

上面两种方法都是需要建临时表进行排序的,对于MySQL来说都是成本比较高的操作。但并不是所有order by都是需要排序的,因为MySQL索引是天然有序的,如果在type和val字段创建一个联合索引idx_type_val,那么该查询就不需要排序了,这时执行过程就变成了如下流程:

  1. 在索引idx_type_val上找到第一个满足type=1条件记录
  2. 根据索引上的主键id回主键索引查询所有字段的值返回,在idx_type_val索引上继续取一下个值
  3. 重复2的操作直到不满足type=1或者超过1000行结束。

使用联合索引,首先不在需要建临时表做排序,其次也不需要扫描出满足type=1条件的所有记录,因为索引有序直接扫描前1000行就结束了,大大减少了扫描的行数。

优先队列排序

对于MySQL来说并不是所有的排序都是用快速排序实现的,假如之前的查询变成了如下:
EXPLAIN SELECT type, val FROM t WHERE type = 1 ORDER BY val LIMIT 3;
假设type=1的记录有1万条,只需要去前val最小的前三行。

对于这种情况,即使sort buffer不能放下1万行记录,会发现MySQL也没有使用到临时文件,这时因为选择了另一种算法:优先队列算法。

算法流程

  1. 对于这10000准备排序的记录,先取前三行构造一个最大堆
  2. 取下一行Next记录跟当前堆顶记录Top比较,如果Next.val < Top.val,就把堆顶记录弹出,将Next记录放入堆
  3. 重复2的操作直到取出所有10000行记录,最后堆中的三个记录就是最小的三个

复杂度

快速排序时间复杂度是O(N*logN),优先队列排序时间复杂度为O((N-K)*logK),K表示堆的大小,即返回记录的个数,对于该场景下为(N-3)*log3,基本可以看做线性时间复杂度,如果是limit 1的时候就相当于求最小值,该算法就是线性时间复杂度。
其次sort buffer中只需要维护堆,内存的消耗也大大减少,空间复杂度为O(K)

order by rand()

如果需要随机选1个数,SQL语句可能如下:
SELECT * FROM t ORDER BY RAND() LIMIT 1
需要注意到是这种方式会建临时表进行排序,临时表除了查询字段会多加一个排序字段存放rand()生成的值,即对每一行记录使用rand()函数生成一个随机数,然后根据这个数来排序。

这种写法的成本是比较高的,所以建议尽量避免这种写法,建议先随机一个0~N-1的值(N表示表总行数),然后去查数据库的某行,比如:

def rand1():
    N = mysql.query("select count(*) from t")
    res = mysql.query("select * from t limit N, 1")
    return res

参考

【极客时间】MySQL实战45讲:16、17

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354