mysql的表支持多索引,但是如果没有指定使用的索引,将由mysql确定使用哪个索引。
执行的很快的语句,由于MySQL选错了索引,导致执行速度变得很慢?
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`a` int(11) DEFAULT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `a` (`a`),
KEY `b` (`b`)
)
select * from t where a between 10000 and 20000;
正常执行查询,能够使用索引 a,但是按照以下顺序执行,执行查询就不会使用索引,采用全表查询
优化器:选择索引是优化器的工作
选择索引是为了用最小的代价执行语句,扫描的行数是一个指标,扫描的行数越少,访问磁盘的次数也就越少,消耗的cpu也就越少。除此之外,是否使用临时表,是否排序也会作为索引选择的指标。
扫描行数是怎么判断的?
MySql执行之前并不知道满足条件的记录有多少条,只能根据统计信息估算记录数;统计信息就是索引的区分度,索引上不同值越多,区分度越好,索引的不同值称为基数(cardinality),基数越大,索引的区分度越好
MySql怎么得到索引的基数?
mysql采用采样统计,因为如果一行一行统计虽然准确,但是代价太大。
采样统计的时候,InnoDB默认选择N个数据页,统计页面上的不同值,得到一个平均值,然后乘以这个索引的页面数,得到索引基数。
数据不断变化,索引统计信息也不会固定不变,变更数据行数超过1/M的时候,自动触发索引统计。
MySql有两种索引统计的方式,可以设置innodb_stats_persistent值选择:
设置为 on 的时候,表示统计信息会持久化存储。这时,默认的 N 是 20,M 是 10。
设置为 off 的时候,表示统计信息只存储在内存中。这时,默认的 N 是 8,M 是 16。
为什么Mysql放着扫描行数少的不用,而用扫描行数多的执行计划?
因为每次查询要回表查询其他字段,
优化器认为直接扫描主键索引更快,
当然,从执
行时间看来,这个选择并不是最优的
统计信息不对,使用 analyze table t命令修正,重新统计索引信息;
实践中,如果你发现 explain
的结果预估的 rows 值跟实际情况差距比较大,可以采用这个方法来处理。
索引选择异常和处理:
1、采用
force index() 强制使用一个索引
。
MySQL 会根据词
法解析的结果分析出可能可以使用的索引作为候选项,然后在候选列表中依次判断每个索引需要
扫描多少行。如果 force index 指定的索引在候选索引列表中,就直接选择这个索引,不再评估
其他索引的执行代价。
2、考虑修改语句,引导mysql使用我们的期望结果。比如使用某索引扫描行数比较多,但是用他进行排序,优化器也会使用它,那么可以考虑将期望的索引也加入排序,根据场景处理
3、新建更合适的索引,提供给优化器,或者删掉无用索引