10 | MySQL为什么有时候会选错索引?
以下内容出自《MySQL 实战 45 讲》
10 | MySQL为什么有时候会选错索引?
优化器的逻辑
选择索引是优化器的工作。
影响优化器选择的判断标准:
- 扫描行数
- 临时表
- 是否排序
扫描行数
MySQL 在真正执行语句之前,会根据统计信息来估算记录数。这个统计信息就是索引的 “区分度“。显然,索引上不同的值越多,索引的区分度就越好。
索引上不同值的个数,称之为 “基数”。即基数越大,索引区分度越好。
MySQL 是怎么得到索引的基数呢? 答案是采样统计,因为精确统计代价太高了。
采样统计的计算方式:InnoDB 默认选择 N 个数据页,统计这些页面上的不同值,得到一个平均值,再乘以这个索引的页面数,就得到了这个索引的基数。
当数据表占用变更的数据行数超过 1/M 的时候,会自动触发重新做一次索引统计。
MySQL 中,有两种存储索引统计的方式,通过设置参数 innodb_stats_persistent
来选择:
- 设置为 ON(默认): 表示统计信息会持久化存储,此时,默认的 N 是 20,M 是 10。
- 设置为 OFF: 表示统计信息只存储在内存中。此时,默认的 N 是 8,M 是 16。
由于索引统计信息不准确导致的问题,可以用 analyze table table_name
来重新统计索引信息。
在实践中,如果你发现 explain 的结果预估的 rows 值跟实际情况差距比较大,可以采用这个方法来处理。
索引选择异常和处理
大多数情况下,优化器都能找到正确的索引,但是当优化器选择索引不正确时,应该怎么办?
- 采用 force index 强行选择一个索引。(不优雅)
- 修改语句,引导 MySQL 使用我们期望的索引。
- 新建一个更合适的索引或删掉误用的索引。