mysql 查询语句解析笔记(按执行顺序理解)

一、msyql 一个查询语句执行顺序是这样的:

bash 复制代码
from->where->group by/having->select ->distinct -> order by -> limit/offset

二、解析关键字实现以及坑点;

2.1 from 决定使用那张表,哪个索引

2.1.1 from的坑点: 大表驱动小表,会出问题:

因为 a join b 的数据匹配是, 遍历 外表(a表)索引,用外表的索引值去命中内表(b表)的索引,

所以 时间复杂度是 o(aN * log2bN),所以只有小表在前面性能才会好;

2.1.2 from的坑点: join 列无索引:

由上可知,如果都无索引,Nested Loop 直接炸(时间复杂度o(aN*bN),且b还要回表找主键)

2.2 where 行过滤
2.2.1 坑点:索引不满足最左前缀,索引失效

比如联合索引 (a,b,c) where b=n and a=m and c=k; 完全用不上,必须严格 abc 或者你另加索引

2.2.2 坑点:对索引做函数或者计算,索引失效:

sql 复制代码
where date(create_at)="2025-01-01"
where num + 1= 10
where num = '123' -- 隐式转换
2.2.3 坑点:范围查找破坏联合索引
sql 复制代码
where a =1 and b > 2 and c=3	-- c=3将无法使用索引命中

log2N快速命中索引的前提是有序, a =1 and b > 2 后的c是无序的或者说是多段有序(比如5,6,8,|2,3,6,|4,7),这样只能遍历所有找到 c=3

2.2.4 坑点:or 破坏索引(b无索引,a的值影响不了b=2,所以只能全表扫描)

where a=1 or b=2

2.2.4 坑点 in /not in

in 是可以用索引的,但是列表过大的话,优化器会认为走索引>全表扫描

not in :必然失效,因为 取反不能利用 命中索引的特性

2.2.5 坑点 not exists 和 not in 对比

not exists 可以用到索引 ,是因为他是一个探测短路机制,外表探测内表存在就可立刻短路返回false,否则探测完不存在既可以记录结果;(这里外表是遍历全索引,但是内表可以快速用索引命中 结果,前文from有说的)

下面的语句就充分利用了索引

sql 复制代码
SELECT a.*
FROM account a
WHERE NOT EXISTS (
    SELECT 1
    FROM friend B
    WHERE B.PlayerId = A.Id
);

not in 为什么没有有效利用索引: 是因为他需要把所有结果返回,成为一个列表,在对这个列表进行反向处理; 逻辑解析: 首先,外表的遍历行数是一样的;

但是内表遍历行数不一样,这里需要遍历所有结果,但其实是浪费的;比如我需要确认 B.playerId = 5 是否存在,这里查询了所有 B.playerId = 5 的结果(这里其实可以用索引 ),不像 not exists 可以短路。除此之外, 这个结果列表还需要一一比对。这也是花销,这个是用不了索引的。

sql 复制代码
explain SELECT a.*
FROM mango.account as a
WHERE A.Id NOT IN (
    SELECT B.PlayerId
    FROM friend as B
);

扩展: 如果如果是对常量列表检索,其实差别不大:

bash 复制代码
SELECT *
FROM A
WHERE id NOT IN (1, 3, 5, 7);

NOT EXISTS必须有子查询的,其实怎么做还不如直接用not in,有些优化器也是直接优化成 not in

sql 复制代码
SELECT *
FROM A
WHERE NOT EXISTS (
    SELECT 1
    WHERE A.id IN (1, 3, 5, 7)
);

【后面再仔细研究 in, not in,exists, join的内容,这里先不讨论】

2.2.5 坑点 like 以 %开头无法确认前缀失效

%可以放结尾, 可以用索引前缀快速确认

sql 复制代码
where like "xxx%"	-- 有效
where like "%xxx"	-- 无效

如果必须用前缀模式查找,可以考虑用全文索引 FULLTEXT

2.2.5 is null/ is not null

普通索引: is null 通常生效(索引会存储null值), is not null 生效不了

复合索引: a = 1 and b is null and c= 3

2.3 group by 分组: 多行汇聚一行

分组逻辑:

本质:

1.索引分组(天生有序,顺序分组,根本不用排序)

2.临时表+hash/sort(额外分组就会产生临时表)
分组方式:排序分组 o(nlogn), 哈希分组 o(n);
排序分组 o(nlogn):

先排序,然后把 字段相同的内容放一次实现分组;
哈希分组 o(n)

如果没有索引且不能分组或者分组性价比不高,就会采用hash

哈希其实更快,但是会消耗大量内存(服务器会判断是否够内存处理,不够会特殊分段处理)

2.4 having 对聚合结果过滤

这个是不能用索引的

2.4.1 坑:能写where但写在了 having 将会巨慢,因为不能用索引

2.5 select 取列

确定:
是否回表
是否覆盖索引

2.5.1 select 坑:select *

查询的内容需要回表+io放大

原因是 select *强行需要读取整行数据,第一是会读取一些不必要的数据,第二就是直接放弃覆盖索引的可能,必定会回表
可以稍微了解索引逻辑
【二级索引和主键索引】 :查找一个数据,如果二级索引命中,从二级索引中获得主键索引;或者直接索引主键索引;再或者都没有索引,那就遍历整个主键索引;然后再那具体的值;

**【索引存储和数据结构】**二级索引和主键索引一样都是内存和磁盘都有;主键索引的B+Tree 的叶子节点=整行数据;但是主键索引根节点必定存在内存中;

**【回表查询的消耗】**二级索引查完后,还要到主键索引在查一次;这就有可能会都一次io;如果只是返回一条数据,微乎其微,如果要返回或者匹配十万个数据,那差距将非常的大!

**【磁盘io】**单次查询磁盘io最多两三次;因为B+tree不是二叉树,他的所有节点都是16KB大小;一般非叶子节点能存上千个Key;这样100万行数据最多他也就3层;最多也是两次磁盘IO(主键根节点常驻内存)

【一个节点内如何访问】;节点内的数据是有序的,一般用二分查找+顺序遍历;特别是id > n and id < m 的情况,都是 到指定节点,然后二分到n,在顺序遍历到m.

2.6 DISTINCT 去重

等价于
group by 所有 select 列

sql 复制代码
select playerId, toid  from log_gift_send GROUP BY PlayerId, toid
--等价于
select DISTINCT  playerId, toid  from log_gift_send

所以 distinct的坑点 和group by 是一样的

2.7 order by -- 排序(最大性能杀手,阻塞因子,不读完不能吐)

本质:结果集排序

排序方式:

  1. 顺着索引读(零成本)
    2. filesort(全局排序)
    为什么需要做全局排序,而不是做结果集排序
    因为你不能确定结果集的长度;其实如果后面加了 limit n;且n的值是比较小的,那么就可以用一个长度=n的堆做排序;常用topN的大小根堆算法
    不能确定长度,如果长度是m;那复杂读将是 o(m*m);那mysql服务器炸了; 所以在没有索引的情况下,选择最稳定的 o(mlogm)全局排序是最好的,

2.7.1 坑点: group by a,c 和 order by a,c 是否可以用一个排序结果;

如果他俩都是sort 排序的话,可以复用,

如果 group by 没有排序,而是用hash实现分组,那么你需要重新生成sort;
2.7.2 坑点: group by a,c order by c,a 产生filesort

还有就是 group by a,c order by c,a 是行不通;哪怕都有索引,但是结果集是按 a,c 排序的, 你的c,a索引对于这个结果集一点用没有;

2.8 limit/offset

offset n 放弃结果集 的前n条;性能爆炸语句,不能用

limit m; 取m行结果: id > 0 limit n搭配非常合适;

2.8.1 坑:

有个问题 limit 只是获取结果集的m行;group by order by 对整个结果集的处理是不能通过limit避免不了的;

相关推荐
lpfasd1232 小时前
《魔力创业》精读笔记:从理念到中国实践的落地指南
笔记
福尔摩斯张2 小时前
嵌入式硬件篇:常见单片机型号深度解析与技术选型指南
网络·数据库·stm32·单片机·网络协议·tcp/ip·mongodb
NineData2 小时前
如何通过 NineData 将 Oracle 不停机迁移到 GaussDB
数据库·oracle·gaussdb·数据库管理工具·ninedata·数据库迁移·迁移工具
云老大TG:@yunlaoda3602 小时前
华为云国际站代理商GSL的跨境合规适配具体体现在哪些方面?
网络·数据库·华为云
WarPigs2 小时前
Unity NetCode for GameObject笔记
笔记·unity·游戏引擎
颜大哦2 小时前
大模型学习笔记
笔记·学习
lpfasd1232 小时前
《复利效应》精读笔记
笔记
红队it2 小时前
【数据分析】基于Spark链家网租房数据分析可视化大屏(完整系统源码+数据库+开发笔记+详细部署教程+虚拟机分布式启动教程)✅
java·数据库·hadoop·分布式·python·数据分析·spark
祁思妙想2 小时前
《知讯头条》Python后端项目详解---基于FastAPI和SQLAlchemy
数据库