假如你是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周瑜

相关推荐
paopaokaka_luck1 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
码农小旋风3 小时前
详解K8S--声明式API
后端
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml43 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~3 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端
hong1616883 小时前
Spring Boot中实现多数据源连接和切换的方案
java·spring boot·后端
追风林4 小时前
mac 本地docker-mysql主从复制部署
mysql·macos·docker
睡觉谁叫~~~5 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
Dann Hiroaki6 小时前
GPU架构概述
架构
Hsu_kk6 小时前
MySQL 批量删除海量数据的几种方法
数据库·mysql