CPU 成本:将数据读入内存后,还要检测数据是否知足条件和排序等 CPU 操做的成本,显然它与行数有关,默认状况下,检测记录的成本是 0.2。
实例说明
为了根据以上两个成原本算出使用索引的最终成本,咱们先准备一个表(如下操做基于 MySQL 5.7.18)code
CREATE TABLE person ( id bigint(20) NOT NULL AUTO_INCREMENT, name varchar(255) NOT NULL, score int(11) NOT NULL, create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY name_score (name(191),score), KEY create_time (create_time) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; personidnamescorecreate_timeidname_scorenamescorecreate_timecreate_time
这个表除了主键索引以外,还有另外两个索引, name_score 及 create_time。而后咱们在此表中插入 10 w 行数据,只要写一个存储过程调用便可,以下:
CREATE PROCEDURE insert_person() begin declare c_id integer default 1; while c_id<=100000 do insert into person values(c_id, concat('name',c_id), c_id+100, date_sub(NOW(), interval c_id second)); set c_id=c_id+1; end while; end
插入以后咱们如今使用 EXPLAIN 来计算下统计总行数到底使用的是哪一个索引
EXPLAIN SELECT COUNT(*) FROM person
从结果上看它选择了 create_time 辅助索引,显然 MySQL 认为使用此索引进行查询成本最小,这也是符合咱们的预期,使用辅助索引来查询确实是性能最高的!
咱们再来看如下 SQL 会使用哪一个索引
SELECT * FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'
用了全表扫描!理论上应该用 name_score 或者 create_time 索引才对,从 WHERE 的查询条件来看确实都能命中索引,那是不是使用 SELECT * 形成的回表代价太大所致呢,咱们改为覆盖索引的形式试一下
SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18'
结果 MySQL 依然选择了全表扫描!这就比较有意思了,理论上采用了覆盖索引的方式进行查找性能确定是比全表扫描更好的,为啥 MySQL 选择了全表扫描呢,既然它认为全表扫描比使用覆盖索引的形式性能更好,那咱们分别用这二者执行来比较下查询时间吧
-- 全表扫描执行时间: 4.0 ms SELECT create_time FROM person WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'
-- 使用覆盖索引执行时间: 2.0 ms SELECT create_time FROM person force index(create_time) WHERE NAME >'name84059' AND create_time>'2020-05-23 14:39:18'
从实际执行的效果看使用覆盖索引查询比使用全表扫描执行的时间快了一倍!说明 MySQL 在查询前作的成本估算不许!咱们先来看看 MySQL 作全表扫描的成本有多少。
前面咱们说了成本主要 IO 成本和 CPU 成本有关,对于全表扫描来讲也就是分别和聚簇索引占用的页面数和表中的记录数。执行如下命令
SHOW TABLE STATUS LIKE 'person'
能够发现
行数是 100264,咱们不是插入了 10 w 行的数据了吗,怎么算出的数据反而多了,其实这里的计算是估算,也有可能这里的行数统计出来比 10 w 少了,估算方式有兴趣你们去网上查找,这里不是本文重点,就不展开了。得知行数,那咱们知道 CPU 成本是 100264 * 0.2 = 20052.8。
这个结果对不对呢,咱们能够用一个工具验证一下。在 MySQL 5.6 及以后的版本中,咱们能够用 optimizer trace 功能来查看优化器生成计划的整个过程 ,它列出了选择每一个索引的执行计划成本以及最终的选择结果,咱们能够依赖这些信息来进一步优化咱们的 SQL。
optimizer_trace 功能使用以下
SET optimizer_trace="enabled=on";
SELECT create_time FROM person WHERE NAME >'name84059' AND create_time > '2020-05-23 14:39:18';
SELECT * FROM information_schema.OPTIMIZER_TRACE;
SET optimizer_trace="enabled=off";