更好理解ORDER BY内部排序和性能优化-mysql

sql 复制代码
create table a(
    city varchar(16) not null,
    name varchar(16) not null,
    id int(11) not null,
    primary key(id)
)

一、全字段排序

比如在执行一个

vbnet 复制代码
select city, name, ID from T 
where city='广州' 
order by name limit 200;
  • city 字段有索引(用来快速过滤出 "广州" 的记录)
  • name 字段没有索引(无法直接按 name 有序返回)

    触发全字段排序,mysql就会把需要返回的字段(city,name,UID)放到一个 sort-buffer的内存,按这个内存块的name排序。

流程

1、先利用索引过滤

先通过索引 city,找到city= 杭州对应的主键ID。

2、根据主键回表取返回字段

根据主键ID,读取整行数据,只取需要返回的字段city,name,id,

放入内存块sort-buffer

3、在sort-buffer排序

sort_buffer 是 MySQL 为每个线程分配的一块排序专用内存

如果 sort_buffer 能装下所有待排序数据就直接排序。

如果数据太多,就会把数据分成多份,每份排序后存到临时磁盘文件中,把这些有序文件合并成一个大的有序结果

4、返回结果

二、rowid排序

触发条件:当单行数据长度超过 max_length_for_sort_data 参数时,MySQL 会认为单行太大,不适合把所有返回字段都塞进 sort_buffer,而改用 rowid 排序

注:max_length_for_sort_data是用户自己设置。

ini 复制代码
set max_length_for_sort_data = 20;

流程

把需要排序的字段(如name)和主键ID放入sort-buffer

排序完成后,再根据排序后的ID回表提取其他需要返回的字段(city,name,id)。

三、为什么需要排序?

在只有 city 索引的情况下:

  • city 索引只保证了 city 字段的有序性,但满足 city='杭州' 的行,name 字段是无序的。
  • 因此 order by name 必须通过排序(全字段或 rowid)来实现,

四、全字段排序 vs rowid 排序

全字段排序

原理(简):把需要返回的字段在sort-buffer排序。

pre:无需回表,一次读取

pro: 单行数据大时,sort_buffer 很快占满,需依赖磁盘临时文件,性能差

rowid排序

原理(简):只把order by的字段和主键ID在sort-buffer排序,再回表提取其他返回字段。

pre:内存占用小,减少临时文件数量

pro: 多一次回表操作,增加 IO 开销

需要采用全字段排序和rowid排序是因为原数据是无序的。

五、联合索引

如果在city索引取的行是按照name递增排序,就不用做排序了(全字段排序ORrowid排序)。

所以可以在创建表后,表外添加索引,创建一个city和name的联合索引,

sql 复制代码
alter table a add index city_user(city,name);

当我们创建 (city, name) 联合索引后: 索引的结构是先按 city 排序,再按 name 排序。

------------ city='广州' 的行,因为name 天然是有序的。 所以此时 order by name 不再需要排序,直接按索引顺序读取即可,性能大幅提升。

流程

(city, name) 索引找到第一个 city='广州' 的行,获取主键 id

回表到主键索引,取出 id 字段,与索引中的 city, name 一起返回。

按索引顺序取下一行,重复步骤 2,直到取够 1000 行。

六、覆盖索引

如果我们进一步创建 (city, name, di) 覆盖索引:

这样的话,索引中就包含了查询的所有需要返回的字段,就不用回表操作,直接读取索引数据。

输出explain 结果中的 Extra 字段显示 Using index,是覆盖索引的标志,表示性能达到最优。

sql 复制代码
alter table a add index city_user(city,name,id);

流程

  1. (city, name, age) 索引找到第一个 city='杭州' 的行,直接取出 city, name, age 返回。
  2. 按索引顺序取下一行,重复步骤 1,直到取够 1000 行。

结论

排序是成本很高的操作,所以能通过索引避免就尽量避免。

覆盖索引是性能优化的最优手段,但需要权衡索引的维护成本,和查询收益,不是所有场景都适合。

相关推荐
MX_935919 小时前
SpringMVC静态资源访问、annotation-driven的使用原理及数据响应模式
java·后端·spring
无籽西瓜a19 小时前
【西瓜带你学设计模式 | 第十二期 - 装饰器模式】装饰器模式 —— 动态叠加功能实现、优缺点与适用场景
java·后端·设计模式·软件工程·装饰器模式
南山乐只19 小时前
Java并发工具:synchronized演进,从JDK 1.6 锁升级到 JDK 24 重构
java·开发语言·后端·职场和发展
无籽西瓜a19 小时前
【西瓜带你学设计模式 | 第十三期 - 组合模式】组合模式 —— 树形结构统一处理实现、优缺点与适用场景
java·后端·设计模式·组合模式·软件工程
别抢我的锅包肉1 天前
【MySQL】第四节 - 多表查询、多表关系全解析
数据库·mysql·datagrip
zzh0811 天前
MySQL高可用集群笔记
数据库·笔记·mysql
Shely20171 天前
MySQL数据表管理
数据库·mysql
寂夜了无痕1 天前
MySQL 主从延迟全链路根因诊断与破局法则
数据库·mysql·mysql主从延迟
爱丽_1 天前
分页为什么越翻越慢:offset 陷阱、seek 分页与索引排序优化
数据库·mysql
nbwenren1 天前
Springboot中SLF4J详解
java·spring boot·后端