假如你是MySQL作者,你会如何实现order by?

大家好,我是IT周瑜,最近在研究MySQL源码,同时自己也在尝试手写一个MySQL,发现挺有趣的,因此这篇文章来跟大家一起扮演MySQL作者,一起来分析一下order by该如何实现。

大家如果对手写MySQL感兴趣,可以关注我的公众号:IT周瑜

大家先看以下SQL:

sql 复制代码
select * from t1 order by name;

以上SQL对应的需求很简单:对t1表中的全部数据按照name字段进行升序排序。

请大家思考5秒钟:如果你是MySQL作者,你会如何实现以上需求?

思路一

首先,我想到的最简单的思路是:

  1. 从磁盘中读取t1表的全部数据,并放到到一个内存List中,如果用Java,那就是一个ArrayList
  2. 然后基于此内存List,将数据按照name字段进行排序
  3. 把排序后的内存List中的数据返回给客户端
  4. 结束

思路很简单,但是我在想一个问题:如果表的数据特别多呢?如果没有足够的内存来存储所有数据呢?

也就是说,如果数据比较多,思路一就行不不通了,于是,我想到了思路二。

思路二

思路一的问题在于不能处理数据量比较大的情况,那么,我们能不能减少内存List中的数据呢,答案是可以的。

思路一的内存List中是存了一条记录的全部字段,那能不能只存部分字段呢,比如只存排序字段和主键,于是我得到思路二:

  1. 从磁盘中读取t1表的全部数据,但只读取name字段和主键字段,把读取的数据放到一个内存List中
  2. 然后基于此内存List,将数据按照name字段进行排序
  3. 然后依次取排序后内存List中的每一条记录,取出该记录的主键进行回表,得到该记录对应的完整记录,并把完整记录发送给客户端
  4. 循环执行第3步,直到遍历内存List结束

此思路的:

  • 优点是:内存List中只存了排序字段和主键,从而能够支持对更多数据进行排序
  • 缺点1:排序之后需要根据主键进行回表得到完整记录
  • 缺点2:内存不是无限的,就算只存排序字段和主键,数据量特别大的时候,内存可能仍然不够用

思路二相比较思路一:能支持更多数据的排序,但整个过程多了回表步骤。

思路三

思路二的缺点2,依然是内存不够的情况,俗话说:"内存不够,磁盘来凑",我的思路是:

  1. 如果内存够用,那就直接用内存进行排序
  2. 如果内存不够,那就需要用文件来排序

思路三是这样的:

  1. 依次遍历t1表的每条记录,取排序字段和主键字段,存到内存List中
  2. 如果内存List没有满,则继续执行第1步,如果内存List满了,则执行第3步
  3. 把List中的数据按排序字段进行排序,并把排序后的List持久化到一个临时文件中
  4. 清空内存List,继续执行第1步,直到把所有记录都遍历完,遍历完后执行第5步
  5. 最后就得到了多个临时文件,每个临时文件本质上都是一个排序之后的内存List
  6. 接下来就进行文件的归并排序,最终得到一个所有记录排序后的大临时文件,不过该临时文件中只有排序字段和主键
  7. 最后依次读取大临时文件中主键字段进行回表,得到对应的完整记录,并把完整记录发送给客户端

这样,不管表里面有多少条记录,都会先加到内存List中,内存List如果满了就持久化到临时文件,最后对所有的临时文件进行归并排序,这种思路理论上是可以处理无限多条数据的。

不过要注意,文件的归并排序,不能把文件的内容都读到内存中进行排序,这样就违背了内存不够的前提。

文件的归并排序步骤是这样的:

  1. 先取文件A和文件B
  2. 取文件A的记录A1,取文件B的记录B1
  3. 比较两条记录,假如A1小,那就将AI记录存入一个新的临时文件C中
  4. 取文件的A的记录A2,和文件B的记录B1进行比较
  5. 直到某个文件的记录被遍历完
  6. 这样文件A和文件B最终就合并为了文件C
  7. 再拿文件C和其他文件进行合并
  8. 最后只得到一个合并文件,并且是排好序的

在这个过程中每次都只占用了两条记录的内存空间。

对于思路三:

  • 优点:相比思路二,能支持更多数据的排序
  • 缺点:需要进行更多的磁盘IO,需要存临时文件,需要读临时文件

思路四

以上三种思路,要么基于内存排序,要么基于文件排序,都需要排序,那有没有不需要进行排序的呢?

那就是索引了,因为索引的本质就是对数据按指定的索引字段进行排序,所以如果排序字段有索引,那么直接取索引上现成的、已经排好序的记录直接返回给客户端就好了。

MySQL用的哪种思路?

MySQL说:"成年人不做选择,我都要"。

MySQL会自动进行选择:

  1. 思路一:叫做不回表排序
  2. 思路二和思路三:叫做回表排序
  3. 思路四:叫做索引扫描

上面提到的内存List,在MySQL中叫做sort_buffer,默认大小为256KB,可以通过sort_buffer_size参数进行配置。

好啦,以上就是order by的实现思路,大家还有其他的实现思路吗?

大家如果对手写MySQL感兴趣,可以关注我的公众号:IT周瑜

相关推荐
2301_811274319 分钟前
大数据基于Spring Boot的化妆品推荐系统的设计与实现
大数据·spring boot·后端
武子康22 分钟前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql
黑色叉腰丶大魔王27 分钟前
《MySQL 数据库备份与恢复》
mysql
运维&陈同学33 分钟前
【zookeeper01】消息队列与微服务之zookeeper工作原理
运维·分布式·微服务·zookeeper·云原生·架构·消息队列
Ljw...34 分钟前
索引(MySQL)
数据库·mysql·索引
草莓base1 小时前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
OpsEye1 小时前
MySQL 8.0.40版本自动升级异常的预警提示
数据库·mysql·数据库升级
Ljw...1 小时前
表的增删改查(MySQL)
数据库·后端·mysql·表的增删查改
编程重生之路1 小时前
Springboot启动异常 错误: 找不到或无法加载主类 xxx.Application异常
java·spring boot·后端
薯条不要番茄酱1 小时前
数据结构-8.Java. 七大排序算法(中篇)
java·开发语言·数据结构·后端·算法·排序算法·intellij-idea