MySQL 执行计划
本文将介绍 MySQL 中的执行计划。
一、基础知识
具体请看:
二、访问方法 - type
1. const
表示可以通过索引唯一地确定某条记录(无论是否回表)。
将通过
唯一二级索引 / 主键
获取唯一二级索引值 + 主键值 / 完整记录
所消耗的时间定为常量级。因此,
通过索引唯一地获取某一行记录 = 常量
通过索引唯一地获取主键,通过主键获取完整记录 = 常量 + 常量 = 常量
该访问方法的出现条件为:
- 搜索条件为主键与常数的等值比较
- 搜索条件为唯一二级索引与常数的等值比较
2. ref
表示通过二级索引定位到索引值相同的多条记录。
该访问方法的出现条件为:
搜索条件为 “非唯一” 二级索引与常数的等值比较
搜索条件为唯一二级索引
is null
唯一二级索引并不限制 null 的数量,可能会定位到多条记录
搜索条件为联合二级索引的符合 “最左前缀原则” (且非 “全值匹配”)的等值比较
联合二级索引依据多个列排序,当查询符合 “最左前缀原则”(且非 “全值匹配”)时,可以定位到多条记录
3. range
表示通过索引定位到索引值处于某个范围内的多条记录。
该访问方法的出现条件为:
- 搜索条件为索引值的范围匹配
4. index
表示通过遍历二级索引查询记录。
该访问方法的出现条件为:
联合二级索引,查询的列 “覆盖索引”,查询条件不符合 “最左前缀原则”
1
2
3KEY idx_key_part(key_part1, key_part2, key_part3)
SELECT key_part1, key_part2, key_part3 FROM single_table WHERE key_part2 = 'abc';
5. all
对聚簇索引进行全表扫描。
三、执行计划的选择
1. 基于成本的执行计划选择
MySQL 会在执行查询语句之前会分析各种执行计划的成本,在其中选择最优的执行计划。
2. 成本
成本由两个部分组成:
- I/O 成本:在查询记录时,首先要将存储于磁盘的数据加载到内存中,这个过程的损耗称为 I/O 成本
- CPU 成本:读取记录、比较记录、排序等操作称为 CPU 成本
在 InnoDB 中,规定读取一个页花费 1.0 的(I/O)成本,读取记录、比较记录花费 0.2 的(CPU)成本。
3. 选择的流程
- 计算全表扫描时的成本
- 根据搜索条件,找出可用的索引,计算使用不同索引时的成本
- 选择成本最小的执行计划
4. 单表查询的成本计算
进行全表扫描时,需要遍历并比较聚簇索引中的所有记录。因此,成本由页面个数和记录数量共同贡献,
成本 = 页面个数 * 1 + 记录数 * 0.2
页面个数和记录数通过表的统计信息获取
1
SHOW TABLE STATUS LIKE '表名';
使用索引时,需要通过索引定位到记录,还可能进行回表,并且需要进行记录的比较。
因此,成本由 “定位”、”回表” 和 “比较” 三部分组成:
定位:在定位时,需要访问 “索引定位到的记录” 所在的页面,读取所有记录,因此成本
页数 * 1.0 + 记录数 * 0.2
页数计算方法:InnoDB 粗暴地认为单个索引值、一个索引值范围对应一个页面
记录数有两种计算方法:
通过 B+ 树中定位到所有记录的 “最左记录” 和 “最右记录”,计算得出预估记录数
MySQL 中为每一个索引维护了一份统计数据,可用使用其中的基数(Cardinality)属性作为预估记录数
1
SHOW INDEX FROM 表名;
回表:InnoDB 将每一次回表操作简单看作访问一次页面,因此每有一条数据便需要进行一次页面的读取,成本为
记录数 * 1.0
比较:成本为
记录数 * 0.2
5. 连接查询的成本计算
连接查询总成本 = 访问驱动表的成本 + 驱动表查询得到的记录数 * 访问被驱动表的成本
- 对于左外连接和右外连接,驱动表是固定的,因此只需要分别确定访问驱动表和非驱动表成本最低的方式
- 对于内连接看,驱动表和被驱动表的位置可以互换,因此需要分别确定两种连接顺序时的最低成本,并选择成本最低的连接顺序
参考
- MySQL 技术内幕
- MySQL 实战 45 讲
- MySQL 是怎样运行的:从根儿上理解 MySQL