设计MySQL的⼤叔规定读取⼀个⻚⾯花费的成本默认是1.0,读取以及检测⼀条记录是否符 合搜索条件的成本默认是0.2。1.0、0.2这些数字称之为成本常数。
1.根据搜索条件,找出所有可能使用的索引:
通过对where条件和order的分析,可以查询到所有使的索引
2.计算全表扫描的代价:
由于查询成本=I/O成本+CPU成本,所以计算全表扫描的代价需要两个信息:
SHOW TABLE STATUS LIKE 'single_table'\G
查询table的查询状态mysql> SHOW TABLE STATUS LIKE 'single_table'\G
*************************** 1. row ***************************
Name: single_table
Engine: InnoDB
Version: 10
Row_format: Dynamic
Rows: 9693
Avg_row_length: 163
Data_length: 1589248
Max_data_length: 0
Index_length: 2752512
Data_free: 4194304
Auto_increment: 10001
Create_time: 2018-12-10 13:37:23
Update_time: 2018-12-10 13:38:03
Check_time: NULL
Collation: utf8_general_ci
Checksum: NULL
Create_options:
Comment:
1 row inset (0.01 sec)
其中有两个参数我们需要关注:
IO成本 = Data_length/每个页面大小(默认16kb)+1.1(微调值)
CPU成本 = rows * 0.2 + 1.0(微调值)
全表扫描的代价 = Data_length/每个页面大小(16kb)+1.1 + rows*0.2 + 1.0;
3. 计算使用不同索引查询的代价
使用二级索引+回表查询的方式主要依赖两个参数:
使用二级索引查询成本 = 索引查询成本 + 回表查询成本 = n个区间 * 1 + 记录数 * 0.2 + 记录数 * 1 + 记录数*0.2;
4. in 查询在其中的特例
有时候使⽤索引执⾏查询时会有许多单点区间,⽐如使⽤IN语句就很容易产⽣⾮常多的单点区间;这种通过直接访问索引对应的B+树来计算某个范围区间对应的索引记录条数的⽅ 式称之为index dive。少数的单点区间是没有任何问题的;但是超过一定范围之后:系统变量eq_range_index_dive_limit
mysql> SHOW VARIABLES LIKE '%dive%';
+---------------------------+-------+
| Variable_name | Value |
+---------------------------+-------+
| eq_range_index_dive_limit | 200 |
+---------------------------+-------+
1 row inset (0.08 sec)
查询优化器就不会使用 index dive的方式了;接下来,我们看一下他采用的计算方式;
首先使用命令 SHOW INDEX FROM single_table
获取索引中的参数Cardinality
(区分度;distinct的值);该值是估算出来的;同时,结合上面的 SHOW TABLE STATUS
中的rows,我们就可以估算出该索引重复的数据 = rows / Cardinality;结合in中的数据量 n 我们就可以估算出 查询的记录数 = n * 索引重复数;
连接查询总成本 = 单次访问驱动表的成本 + 驱动表扇出数(记录数) x 单次访问被驱动表的成本
我们前边之介绍了两个成本常数:
mysql> SHOW TABLES FROM mysql LIKE '%cost%';
+--------------------------+
| Tables_in_mysql (%cost%) |
+--------------------------+
| engine_cost |
| server_cost |
+--------------------------+
2 rows inset (0.00 sec)
⼀条语句的执⾏其实是分为两层的:
从server_cost中的内容可以看出来,⽬前在server层的⼀些操作对应的成本常数有以下⼏种:
成本常数名称 | 默认值 | 描述 |
---|---|---|
disk_temptable_create_cost | 40.0 | 创建基于磁盘的临时表的成本,如果增⼤这个 值的话会让优化器尽量少的创建基于磁盘的临 时表。 |
disk_temptable_row_cost | 1.0 | 向基于磁盘的临时表写⼊或读取⼀条记录的成 本,如果增⼤这个值的话会让优化器尽量少的 创建基于磁盘的临时表。 |
key_compare_cost | 0.1 | 两条记录做⽐较操作的成本,多⽤在排序操作上,如果增⼤这个值的话会提升filesort的成 本,让优化器可能更倾向于使⽤索引完成排序 ⽽不是filesort。 |
memory_temptable_create_cost | 2.0 | 创建基于内存的临时表的成本,如果增⼤这个 值的话会让优化器尽量少的创建基于内存的临 时表。 |
memory_temptable_row_cost | 0.2 | 向基于内存的临时表写⼊或读取⼀条记录的成 本,如果增⼤这个值的话会让优化器尽量少的 创建基于内存的临时表。 |
row_evaluate_cost | 0.2 | 这个就是我们之前⼀直使⽤的检测⼀条记录是 否符合搜索条件的成本,增⼤这个值可能让优 化器更倾向于使⽤索引⽽不是直接全表扫描。 |