【MySQL】06-排序

全字段排序

先把所有符合条件的行(和列)读到内存中,然后对相应字段进行快排

外部排序

内存中放不下全部的数据时进行,将一部分结果存入到文件中(磁盘中)

rowid排序

当一行中字段过多时,把待排序字段和rowID读入内存中,排序后,按照id再读一遍数据库。(读两遍,第一遍取排序字段,第二遍取select的字段)

随机消息

例如随机展示三个单词

select word from words order by rand() limit 3;

内存临时表

这条语句的执行流程是这样的:

  1. 创建一个临时表。这个临时表使用的是memory引擎,表里有两个字段,第一个字段是double类型,为了后面描述方便,记为字段R,第二个字段是varchar(64)类型,记为字段W。并且,这个表没有建索引。
  2. 从words表中,按主键顺序取出所有的word值。对于每一个word值,调用rand()函数生成一个大于0小于1的随机小数,并把这个随机小数和word分别存入临时表的R和W字段中,到此,扫描行数是10000。
  3. 现在临时表有10000行数据了,接下来你要在这个没有索引的内存临时表上,按照字段R排序。
  4. 初始化 sort_buffer。sort_buffer中有两个字段,一个是double类型,另一个是整型。
  5. 从内存临时表中一行一行地取出R值和位置信息(建临时表时生成的rowid),分别存入sort_buffer中的两个字段里。这个过程要对内存临时表做全表扫描,此时扫描行数增加10000,变成了20000。
  6. 在sort_buffer中根据R的值进行排序。注意,这个过程没有涉及到表操作,所以不会增加扫描行数。
  7. 排序完成后,取出前三个结果的位置信息,依次到内存临时表中取出word值,返回给客户端。这个过程中,访问了表的三行数据,总扫描行数变成了20003。

磁盘临时表

如果临时表大小超过了tmp_table_size,那么内存临时表就会转成磁盘临时表。磁盘临时表使用的引擎默认是InnoDB。

执行这条语句时,InnoDB会采用优先队列排序算法。(只取最小的三个,对剩余的不进行排序)

当堆内存存不下所有的需求时,会采用快排+归并。

优解

无论用上述那种方法,都需要扫至少一遍全表。更优的解法如下:

  • 算法一

    1. 取得这个表的主键id的最大值M和最小值N;
    2. 用随机函数生成一个最大值到最小值之间的数 X = (M-N)*rand() + N;
    3. 取不小于X的第一个ID的行。

    但是缺点是伪随机,中间取得的概率大于两边的。(由其当主键不均匀时)

  • 算法二

    1. 取得整个表的行数,并记为C。
    2. 取得 Y = floor(C * rand())。 floor函数在这里的作用,就是取整数部分。
    3. 再用limit Y,1 取得一行。
posted @ 2020-07-15 15:33  Ryan_W  阅读(131)  评论(0编辑  收藏  举报