更好理解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 行。

结论

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

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

相关推荐
立心者04 小时前
SpringBoot中使用TOTP实现MFA(多因素认证)
java·spring boot·后端
AskHarries4 小时前
SEO 页面怎么生成
后端
爱吃牛肉的大老虎5 小时前
Rust对象之结构体,枚举,特性
开发语言·后端·rust
石榴5 小时前
NestJS 的请求到底经过了什么:装饰器、守卫、拦截器、管道与中间件如何配合
后端
Gopher_HBo5 小时前
moby-client客户端
后端
杨运交5 小时前
[055][调度模块]Spring动态任务调度框架的设计与实现
java·后端·spring
卷福同学6 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
Csvn7 小时前
📊 SQL 入门 Day 11:CASE 表达式:SQL 里的 if-else 魔法
后端·sql
QQ_21696290967 小时前
Spring Boot 养老院管理系统:从入住、护理到费用结算的全流程实现(源码可领)
java·spring boot·后端
万少10 小时前
DeepSeek-V4-Flash 正式版上线了,但这 3 个坑我帮你提前踩了
前端·javascript·后端