# 索引合并

MySQL 引入了一种叫“<mark style="color:blue;">**索引合并**</mark><mark style="color:blue;">”</mark>（index merge）的策略，它在一定程度上可以使用表中的多个单列索引来定位指定的行。在这种情况下，查询能够同时使用两个单列索引进行扫描，并将结果进行合并。这种算法有三个变种：

* <mark style="color:blue;">**OR 条件的联合(union)**</mark>
* <mark style="color:blue;">**AND 条件的相交(intersection)**</mark>
* <mark style="color:blue;">**组合前两种情况的联合及相交**</mark>

下面的查询就使用了两个索引扫描的联合，通过 EXPLAIN 中的 Extra 列可以看到这点：

```sql
EXPLAIN
SELECT *
FROM film
WHERE film_id = 1
   OR language_id = 1;
```

```sql
+-------------+--------------------------+
|id           |1                         |
+-------------+--------------------------+
|select_type  |SIMPLE                    |
+-------------+--------------------------+
|table        |film                      |
+-------------+--------------------------+
|partitions   |NULL                      |
+-------------+--------------------------+
|type         |ALL                       |
+-------------+--------------------------+
|possible_keys|PRIMARY,idx_fk_language_id|
+-------------+--------------------------+
|key          |NULL                      |
+-------------+--------------------------+
|key_len      |NULL                      |
+-------------+--------------------------+
|ref          |NULL                      |
+-------------+--------------------------+
|rows         |1000                      |
+-------------+--------------------------+
|filtered     |100                       |
+-------------+--------------------------+
|Extra        |Using where               |
+-------------+--------------------------+
```

索引合并策略有时候效果非常不错，但更多的时候，它<mark style="color:orange;">**说明了表中的索引建得很糟糕**</mark>：

* 当优化器需要对多个索引做**相交操作**时（通常有多个 AND 条件），通常意味着需要一个包含所有相关列的多列索引。
*   当优化器需要对多个索引做联合操作时（通常有多个 OR 条件），通常需要在算法的缓存、排序和合并操作上耗费大量 CPU 和内存资源，尤其是当其中有些索引的选择性不高，需要合并扫描返回的大量数据的时候。

    更重要的是，<mark style="color:orange;">**优化器不会把这些操作计算到“查询成本”（cost）中，优化器只关心随机页面读取**</mark>。这会使得查询的成本被“低估”，导致该执行计划还不如直接进行全表扫描。这样做不但会消耗更多的 CPU 和内存资源，还可能会影响并发的查询，但如果单独运行这样的查询则往往会忽略对并发性的影响。通常来说，使用 UNION 改写查询，往往是最好的办法。

如果在 EXPLAIN 中看到有索引合并，那么就应该好好检查一下查询语句的写法和表的结构，看是不是已经是最优的。也可以通过参数 <mark style="color:blue;">**optimizer\_switch**</mark> 来关闭索引合并功能，还可以使用 IGNORE INDEX 语法让优化器强制忽略掉某些索引，从而避免优化器使用包含索引合并的执行计划。
