索引失效
在工作中,我们经常会碰到 SQL 语句不适用已有索引的情况,来看一个索引失效的例子:
这条带有 like 查询的 SQL 语句,没有用到 product 表中的 index_name
索引。
我们结合普通索引的 B+Tree 结构看一下索引失效的原因——
当 MySQL 优化器根据 name like '%路由器'
这个条件,到索引 index_name
的 B+Tree 结构上进行查询评估时,发现当前节点的左右子节点上的值都有可能符合 '%路由器'
这个条件,于是优化器判定当前索引需要扫描整个索引,并且还要回表查询,不如直接全表扫描。
当然,还有其他类似的索引失效的情况:
-
索引列上做了计算、函数、类型转换操作,这些情况下索引失效是因为查询过程需要扫描整个索引并回表,代价高于直接全表扫描
-
like
匹配使用了前缀匹配符'%abc'
-
字符串不加引号导致类型转换
我给你的建议是——
如果 MySQL 查询优化器预估走索引的代价比全表扫描的代价还要大,则不走对应的索引,直接全表扫描,如果走索引比全表扫描代价小,则使用索引。
常见优化索引的方法
1. 前缀索引优化
前缀索引就是用某个字段中,字符串的前几个字符建立索引,比如我们可以在订单表上对商品名称字段的前 5 个字符建立索引。
使用前缀索引是为了减小索引字段大小,可以增加一个索引页中存储的索引值,有效提高索引的查询速度。在一些大字符串的字段作为索引时,使用前缀索引可以帮助我们减小索引项的大小。
但是,前缀索引有一定的局限性,例如 order by
就无法使用前缀索引,无法把前缀索引用作覆盖索引。
2. 覆盖索引优化
覆盖索引是指 SQL 中 query
的所有字段,在索引 B+tree 的叶子节点上都能找得到的那些索引,从辅助索引中查询得到记录,而不需要通过聚簇索引查询获得。
假设我们只需要查询商品的名称、价格,有什么方式可以避免回表呢?
我们可以建立一个组合索引,即商品ID
、名称
、价格
作为一个组合索引。如果索引中存在这些数据,查询将不会再次检索主键索引,从而避免回表。
所以,使用覆盖索引的好处很明显,即不需要查询出包含整行记录的所有信息,也就减少了大量的 I/O 操作。
3. 联合索引
联合索引时,存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。
比如联合索引 (userpin
, username
),如果查询条件是 WHERE userpin=1 AND username=2
,就可以匹配上联合索引。
或者查询条件是 WHERE userpin=1
,也能匹配上联合索引,但是如果查询条件是 WHERE username=2
,就无法匹配上联合索引。
另外,建立联合索引时的字段顺序,对索引效率也有很大影响。
越靠前的字段被用于索引过滤的概率越高,实际开发工作中建立联合索引时,要把区分度大的字段排在前面,这样区分度大的字段越有可能被更多的 SQL 使用到。
区分度就是某个字段 column
不同值的个数除以表的总行数,比如性别的区分度就很小,不适合建立索引或不适合排在联合索引列的靠前的位置,而 uid
这类字段就比较适合做索引或排在联合索引列的靠前的位置。