前言

网上经常能看到一些文章总结在 mysql 中不能命中索引的各种情况,其中有一种说法就是指使用了 or 的语句都不能命中索引。

这种说法其实是不够正确的,正确的结论应该是,从 mysql5.0 后,如果在 or 连接的字段上都有独立的索引的话,是可以命中索引的,这里就是用到了 index_merge 特性。

在 mysql5.0 版本以前一条 sql 只能选择使用一个索引,而且如果 sql 中使用了 or 关键字,那么已有的索引就会失效,会走全表扫描。因为无论走哪个索引,mysql 都不能一次性查找出符合条件的数据,所以只能放弃索引。

mysql 也是一直在不断升级更新,所以在 mysql5.0 版本后,增加了 index_merge 索引合并这个特性,也因此支持了一条 sql 使用多个索引。

index_merge 核心思想就是先分别使用单个索引查出满足要求的数据,然后再将这些数据合并到一起返回。
我们可以看一个的例子。

这里依然沿用我们前面文章中创建的表和测试数据,表中插入了 10 w 条测试数据,表结构如下。

CREATE TABLE `t` (
 `id` int(11) NOT NULL,
 `a` int(11) DEFAULT NULL,
 `b` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB;

我们先来给  a 字段添加一个索引,然后执行一条带 or 的查询语句看看。

mysql> alter table t add index a_index(a);
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select a from t where a=100 or b=6000;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows  | Extra    |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| 1 | SIMPLE   | t   | ALL | a_index    | NULL | NULL  | NULL | 100332 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

因为字段 b 上没有索引,mysql 认为走全表扫描代价更低一些,因为可以免去回表过程。

那么我们给 b 字段也加上索引试试,然后再执行刚刚那条 sql 。

mysql> alter table t add index b_index(b);
Query OK, 0 rows affected (0.17 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select a from t where a=100 or b=6000;
+----+-------------+-------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+
| id | select_type | table | type    | possible_keys  | key       | key_len | ref | rows | Extra                   |
+----+-------------+-------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+
| 1 | SIMPLE   | t   | index_merge | a_index,b_index | a_index,b_index | 5,5   | NULL |  2 | Using union(a_index,b_index); Using where |
+----+-------------+-------+-------------+-----------------+-----------------+---------+------+------+-------------------------------------------+
1 row in set (0.00 sec)

这回可以看到 mysql 同时使用了 a、b 两个索引,并且看到 type 字段的值为 index_merge。

接下来再来看另一条 sql,看看结果又是怎样的。

mysql> explain select a from t where a>100 or b>6000;
+----+-------------+-------+------+-----------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys  | key | key_len | ref | rows  | Extra    |
+----+-------------+-------+------+-----------------+------+---------+------+--------+-------------+
| 1 | SIMPLE   | t   | ALL | a_index,b_index | NULL | NULL  | NULL | 100332 | Using where |
+----+-------------+-------+------+-----------------+------+---------+------+--------+-------------+
1 row in set (0.00 sec)

这条 sql 仅仅是把等号改成了大于号,也就是说返回的结果集是一个区间集,mysql 在这里又放弃了索引,走的全表扫描,不过有看文章说在 mysql5.7 版本后优化了这个问题,即在区间查询中也支持使用 index_merge,我的版本是 5.6 ,暂未验证这个优化,有兴趣的可以去验证下。

其实在 mysql 中很多东西都是不绝对的,对于同一条 sql 不同 mysql 版本的内部处理方式有可能是不太一样的,同时也可以看到 mysql 一直在不断优化升级,一些老旧的知识点很容易就会不再适用了。

希望文章对你有帮助,欢迎关注,点个赞是对我最好的支持,感谢。

另外,关于 mysql 的底层数据结构,大家可以参考我前面写的其他文章,对你理解这篇文章或许有帮助。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。

标签:
sql索引失效,sql索引失效的情况,sql索引

免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
评论“这种sql写法真的会导致索引失效吗”
暂无“这种sql写法真的会导致索引失效吗”评论...

《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线

暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。

艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。

《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。