全字段排序
先把所有符合条件的行(和列)读到内存中,然后对相应字段进行快排
外部排序
内存中放不下全部的数据时进行,将一部分结果存入到文件中(磁盘中)
rowid排序
当一行中字段过多时,把待排序字段和rowID读入内存中,排序后,按照id再读一遍数据库。(读两遍,第一遍取排序字段,第二遍取select的字段)
随机消息
例如随机展示三个单词
select word from words order by rand() limit 3;
内存临时表
这条语句的执行流程是这样的:
- 创建一个临时表。这个临时表使用的是memory引擎,表里有两个字段,第一个字段是double类型,为了后面描述方便,记为字段R,第二个字段是varchar(64)类型,记为字段W。并且,这个表没有建索引。
- 从words表中,按主键顺序取出所有的word值。对于每一个word值,调用rand()函数生成一个大于0小于1的随机小数,并把这个随机小数和word分别存入临时表的R和W字段中,到此,扫描行数是10000。
- 现在临时表有10000行数据了,接下来你要在这个没有索引的内存临时表上,按照字段R排序。
- 初始化 sort_buffer。sort_buffer中有两个字段,一个是double类型,另一个是整型。
- 从内存临时表中一行一行地取出R值和位置信息(建临时表时生成的rowid),分别存入sort_buffer中的两个字段里。这个过程要对内存临时表做全表扫描,此时扫描行数增加10000,变成了20000。
- 在sort_buffer中根据R的值进行排序。注意,这个过程没有涉及到表操作,所以不会增加扫描行数。
- 排序完成后,取出前三个结果的位置信息,依次到内存临时表中取出word值,返回给客户端。这个过程中,访问了表的三行数据,总扫描行数变成了20003。
磁盘临时表
如果临时表大小超过了tmp_table_size,那么内存临时表就会转成磁盘临时表。磁盘临时表使用的引擎默认是InnoDB。
执行这条语句时,InnoDB会采用优先队列排序算法。(只取最小的三个,对剩余的不进行排序)
当堆内存存不下所有的需求时,会采用快排+归并。
优解
无论用上述那种方法,都需要扫至少一遍全表。更优的解法如下:
-
算法一
- 取得这个表的主键id的最大值M和最小值N;
- 用随机函数生成一个最大值到最小值之间的数 X = (M-N)*rand() + N;
- 取不小于X的第一个ID的行。
但是缺点是伪随机,中间取得的概率大于两边的。(由其当主键不均匀时)
-
算法二
- 取得整个表的行数,并记为C。
- 取得 Y = floor(C * rand())。 floor函数在这里的作用,就是取整数部分。
- 再用limit Y,1 取得一行。