这是我参与8月更文挑战的第29天,活动详情查看:8月更文挑战
在开启一个事务A时,如果事务B先将表里数据都删除,再调用存储过程插入10万行数据,那么没有使用索引a 不断删除历史数据和新增数据时,就会导致mySQL选错索引。 可以通过force index(a)来强制使用索引a。但MySQL具体是如何决定使用哪个索引的呢?
MySQL决定使用哪个索引
选择索引是由优化器所控制的,目的是找到一个最优的执行方案,并用最小的代价去执行语句
- 扫描更少的行数 【访问磁盘数据的次数越少,消耗的CPU资源越少】
- 临时表
- 排序
那么扫描行数是怎么判断的?
在真正开始执行语句之前,并不能精确地知道满足这个条件的记录有多少条,所以是根据统计信息来估算记录数
而统计信息是指索引的“区分度”。一个索引上不同的值越多,表明这个索引的区分度越好。
一个索引上不同的值的个数被称为“基数”。
基数越大,索引的区分度越好
记录数是指统计信息【区分度】估计(基数越大,区分度越好) - 基数(采样统计方法)
MySQL采样统计的方法 - 得到索引的基数
因为把整张表取出来一行行统计代价太高,所以就通过采样统计 - InnoDB会默认选择N个数据页 - 统计这些页面上的不同值 - 得到平均值 - 乘以这个索引的页面数
变更的数据行数超过1/M时 - 自动重新做一次索引统计
MySQL中 - 两种存储索引统计的方法 - innodb_stats_persistent
- 设置为on时 - 统计信息会持久化存储 - N默认为20,M是10
- 设置为off时 - 统计信息只存储在内存中 - N默认为8,M是16
优化器相当于是判断 索引统计值 + 本身要扫描多少行
那么在一开始的例子中为何优化器不选择使用索引a的执行计划?是因为每次从索引a上拿到一个值,都要回到主键索引上查出整行数据 - 这个代价优化器也要算进去。所以优化器任务选择索引的代价会更大。而使用普通索引需要把回表的代价算进去
因此总结出的结论就是:MySQL选错索引的原因 - 没能准确地判断出扫描行数