今天学习时才发现,原来mysql中的order by的道理是如下的,涉及到两种算法
下面笔记之.
mysql把需要排序的地方都叫filesort, 名字上看有个file在里面,但不一定与文件有关, 可能就是在内存完成的排序。

MySQL 有二个 filesort 算法, 第一个方法(原始方法)只使用了ORDER BY中指定的字段。第二个方法(改进的方法)不仅使用ORDER BY中指定的字段,还是用了查询中所涉及到的所有的字段。

优化器决定选择哪个filesort 算法, 除了查询中涉及到TEXT或者BLOCk字段外,它通常使用改进的算法,当查询中涉及到TEXT或者BLOCk字段时,通常使用原始的方法。

原始的 filesort 算法的工作机制大体是(典型的外排算法):

1. 通过主键扫描或表扫描读出所有的数据记录,不相配WHERE条件的记录被跳过。

2. 对于每条记录,储存一对信息(主键和指向记录的指针)到查询缓冲区,缓冲区的大小是 sort_buffer_size 系统变量控制的。

3. 一旦缓冲区满了,对缓冲区中的数据做一次 qsort(快速排序)并把排序结果保存到一个临时文件。(如果所有记录信息能放到缓冲区中,就没有必要存到临时文件了)

4. 重复前述的步骤直到所有的记录都处理了完。

5. 每在合并缓冲区中做多路合并, 并把合并后的结果输出到另外一个临时文件

6. 重复多路合并排序, 直到所有的记录信息能分到15个以内的有序集合。

7. 在最后多路合并中,只有指向记录的指针被写到一个结果文件。

8. 根据结果文件中的记录指针的读取出记录放到返回集合中, 这个结果缓冲区的大小是 read_rnd_buffer_size 系统变量设置的。

这个排序算法有几个问题值得注意:

1. 它需要读取记录两次:第一次是执行的WHERE时候,第二次是在对key进行排序之后。 尽管第一次是读取时记录是连续读取的(比如表扫描), 第二次就是随机读取了。(这时候键值是有序的了,但是记录还不是)

2. 使用到了快速排序,也就是说,排序结果是不稳定的

改进的 filesort 算法在原始的算法的基础上做了一些优化: 在排序时,不只是对键值进行操作,还带有记录位置和查询中需要的字段信息,这避免两次都记录。改进的 filesort 算法的工作机制:

1. 读取符合WHERE条件的记录。

2. 对于每条记录,把键值, 记录位置和查询所需要的字段记录到一个元组中。

3. 根据键值对元组进行排序

4. 从排序后的元组中取得返回结果, 直接从排序好的元组中读取即可, 避免了第扫表。

在改进的 filesort 算法中,元组比原始方法中用的信息对(键值和记录指针)所占得空间要多,因此同样大的sort_buffer_size放的记录数更少,因此可能会导致更多的I/O操作,从而使得效率更慢,而不是更快。为了避免这种负面优化,只有当元组中存储的额外字段信息的大小不超过 max_length_for_sort_data 系统变量时才使用改进的排序算法。(设定太高的max_length_for_sort_data是明显症状是高的磁盘I/O活动和低的处理器使用率。)

因此, 如果想要提高ORDER BY的速度,尽量使MySQL使用索引而不是通过额外的排序工作来排序。如果无法使用索引,试一下改变一些系统参数:

1. 增加 sort_buffer_size 变数的大小, 这个参数控制着进行排序时的缓冲区的大小, 这个参数越大,进行归并排序的次数就会少

2. 增加 read_rnd_buffer_size 变数的大小, 这个参数控制着用来存放扫表记录存放缓冲额大小,这个参数越大, 能存放的记录数会多

3. 在满足业务需要的前提下把字段声明的尽可能的小,这样排序时占的内存也会少;比如, 如果字段的值无法超过 16个字符的话,VARCHAR(16) 比VARCHAR (200) 好。

4. 把 tmpdir 设置成有大量的自由空间的专用的 filesystem 。另外,这选项接受的值是可以使由”:”(UNIX上)或”;”( Windows, NetWare, and OS/2)分隔的若干个路径,在使用时, mysql会用轮转法来依次使用这些路径,因此, 可以考虑设置多个路径来提高排序时的i/o速度。当然,路径应该是给位于不同的物理磁盘之上的 filesystems, 而不是在相同的磁盘片上的不同的目录。


5 使用算法2的前提是,列长度的值小于max_length_for_sort_data


5. 在需要排序的字段中, 尽量避免涉及到TEXT和BLOB字段;如果有这些字段,第一系统无法使用改进的排序算法,第二,系统在处理时需要分配更多的内存来存放这些字段(在排序处理时, 每条记录分配的空间是一样大,因此系统会根据选取最大可能的长度来分配空间, 这个通常会很低效,因此,有一个变量max_sort_length来限制排序时BLOB或TEXT字段的长度);当确实需要用到这些字段时, 考虑用SUBSTR进行截取;

6. 设置合适的max_length_for_sort_data, 注意mysql在通过这个值控制排序算法的选择时,对于VARCHAR字段使用的最大长度,而不是实际用到的长度。