成本 #
一条查询语句在MySQL中的执行成本,由两方面组成:
I/O成本:(此处指将数据存储在磁盘上的存储引擎)当查询表中的记录时,需要先把数据或者索引加载到内存中,然后再进行操作。这个从磁盘到内存到加载过程损耗的时间称为I/O成本。
CPU成本:读取记录以及检测记录是否满足对应的搜索条件、对结果集进行排序等这些操作损耗的时间称为CPU成本。
成本常数 #
MySQL规定:
- 读取一个页面花费的成本默认是1.0;
- 读取以及检测一条记录是否符合搜索条件的成本默认是0.2。 1.0、0.2这些数字称为成本常数。
在读取记录时,即使不需要检测记录是否符合搜索条件,其成本也算0.2。
优化器确定执行计划的步骤 #
优化器会查找成本最低的执行计划,其步骤如下:
- 根据搜索条件,找出所有可能使用的索引。
- 计算全表扫描的代价。
- 计算使用不同索引执行查询的代价。
- 对比各种执行方案的代价,找出成本最低的执行方案。
计算全表扫描的代价 #
计算全表扫描的代价时需要两个信息:
- 聚簇索引占用的页面数。
- 该表中的记录数。 这些信息都是从MySQL维护的一系列统计信息中获取的。
可以通过show table status命令查看表的统计信息。
计算使用不同索引执行查询的代价 #
对于使用二级索引+回表方式执行的查询,MySQL在计算这种查询的成本时,依赖于两方面的数据:扫描区间数量和需要回表的记录数。
- 扫描区间的数量 查询优化器认为读取索引的一个扫描区间的I/O成本与读取一个页面的I/O成本相同,即1.0。
- 需要回表的记录数 如果区间最左记录和最右记录相隔不远,可以精确统计。 否则,沿着区间最左记录向右读取10个页面,计算每个页面平均包含多少记录,然后用这个平均值乘以区间的页面数量。区间的页面数量可以根据目录项来确定。
回表的成本 #
MySQL在评估回表的I/O成本时,认为每次回表操作都相当于访问一个页面。
索引成本统计 #
index dive #
MySQL把直接访问索引对应的B+树来计算某个扫描区间内对应的索引记录条数的方式称为index dive。
show index #
如果in 语句产生的单点扫描区间的数量小于200个,将使用index dive来计算各个区间的记录条数。如果大于等于200个,要使用索引统计数据。
可以使用show index from 表名命令查看索引统计数据。
如下所示:
其中:
- Cardinality:该列中不重复值的数量。
连接查询的成本 #
两表连接 #
两表连接的成本由两部分组成:
- 单次查询驱动表的成本。
- 多次查询被驱动表的成本(具体查询多少次取决于针对驱动表查询有的结果集中有多少条记录)。
扇出 #
我们把查询驱动表后得到的记录条数称为驱动表的扇出。
查询成本计算公式: 连接查询总成本 = 单次访问驱动表的成本+ 驱动表扇出值*单次访问被驱动表的成本。
多表连接 #
多表连接,可能会生成多种连接顺序,为了防止无穷无尽的分析各种连接顺序的成本,MySQL提供了optimizer_search_depth参数来限制优化器分析连接顺序的成本。如果连接表的个数小于该值,就继续穷举每一种连接顺序的成本,否则只对数量与optimizer_search_depth相等的连接表进行穷举。
调节成本常数 #
server层操作对应的成本常数存储在server_host表中,依赖于存储引擎的操作对应的成本常数存储在engine_cost表中。这两张表都在mysql库中。