網上常常能看到一些文章總結在 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 的底層數據結構,你們能夠參考我前面寫的其餘文章,對你理解這篇文章或許有幫助。