这种sql写法会致使索引失效?

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

这种说法实际上是不够正确的,正确的结论应该是,从 mysql5.0 后,若是在 or 链接的字段上都有独立的索引的话,是能够命中索引的,这里就是用到了 index_merge 特性sql

在 mysql5.0 版本之前一条 sql 只能选择使用一个索引,并且若是 sql 中使用了 or 关键字,那么已有的索引就会失效,会走全表扫描。由于不管走哪一个索引,mysql 都不能一次性查找出符合条件的数据,因此只能放弃索引。bash

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

index_merge 核心思想就是先分别使用单个索引查出知足要求的数据,而后再将这些数据合并到一块儿返回。测试

咱们能够看一个的例子。优化

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

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 的查询语句看看。spa

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 认为走全表扫描代价更低一些,由于能够免去回表过程。code

那么咱们给 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 的底层数据结构,你们能够参考我前面写的其余文章,对你理解这篇文章或许有帮助。

相关文章
相关标签/搜索