mysql为什么会选错索引?

数据库为什么会选错索引?

mysql可能不走索引
比如mysql判断是否走 c 字段索引时,系统会预测走 c 字段索引大概需要扫描多少行。如果预测到要扫描的行数很多,它可能就不走索引而直接扫描全表了。

系统是怎么预测的呢?
系统是通过索引的区分度来判断的,我们也把区分度称之为基数,即区分度越高,基数越大,
一个索引上不同的值越多,意味着出现相同数值的索引越少,意味着利用索引查询的次数越少。
所以,一个索引的基数越大,意味着走索引查询越有优势。
即基数越大越好,性别由于基数太小就不适合建索引。

msyql怎么知道这个索引的基数呢?
系统当然是不会遍历全部行来获得一个索引的基数的,代价太大了,索引系统是通过遍历部分行,通过采样的方式,来预测索引的基数的。

重点来了,因为是采样,所以必然存在误差,比如c 这个索引的基数实际上是很大的,但是采样的时候,却很不幸,把这个索引的基数预测成很小。然后就误以为索引的基数很小。所以系统就不走 c 索引了,直接走全部扫描了。于是就选错了索引。

结论:由于统计的失误,导致系统没有走索引,而是走了全表扫描,而这,也是导致我们 SQL 语句执行的很慢的原因。

系统判断是否走索引,扫描行数的预测其实只是原因之一,临时表、是否需要排序等也是会影响系统的选择的。

举例

比如下面的sql语句:

mysql> explain select * from t where (a between 1 and 1000) and (b between 50000 and 100000) order by b limit 1;

用索引a明显快于索引b,因为a索引只需要扫描1000行,而b索引要扫描5000行。
但是mysql实际执行时会选择索引b。因为它认为使用索引 b 可以避免排序(order by b),所以即使扫描行数多,也判定为代价更小。

选错索引该怎么办?

2、考虑修改语句,引导 MySQL 使用我们期望的索引。

比如,在这个例子里,显然把“order by b limit 1” 改成 “order by b,a limit 1” ,语义的逻辑是相同的。
现在 order by b,a 这种写法,要求按照 b,a 排序,就意味着使用这两个索引都需要排序。因此,扫描行数成了影响决策的主要条件,于是此时优化器就会选择只需要扫描 1000 行的索引 a。

当然,这种修改并不是通用的优化手段,只是刚好在这个语句里面有 limit 1,因此如果有满足条件的记录, order by b limit 1 和 order by b,a limit 1 都会返回 b 是最小的那一行,逻辑上一致,才可以这么做。

3、删掉误用的索引
将b索引删掉即可

1、采用 force index 强行帮mysql选择正确的索引。
在这里插入图片描述

MySQL 会根据词法解析的结果分析出可能可以使用的索引作为候选项,然后在候选列表中依次判断每个索引需要扫描多少行。如果 force index 指定的索引在候选索引列表中,就直接选择这个索引,不再评估其他索引的执行代价。

force index用的不多,因为选错索引的情况还是比较少出现的,所以开发的时候通常不会先写上 force index。而是等到线上出现问题的时候,你才会再去修改 SQL 语句、加上 force index。但是修改之后还要测试和发布,对于生产系统来说,这个过程不够敏捷。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值