mysql的索引合併並非什麼新特性。早在mysql5.0版本就已經實現。之因此還寫這篇博文,是由於好多人還一直保留着一條sql語句只能使用一個索引的錯誤觀念。本文會經過一些示例來講明如何使用索引合併。mysql
下面咱們看下mysql文檔中對索引合併的說明:
The Index Merge method is used to retrieve rows with several range scans and to merge their results into one. The merge can produce unions, intersections, or unions-of-intersections of its underlying scans. This access method merges index scans from a single table; it does not merge scans across multiple tables.
根據官方文檔中的說明,咱們能夠了解到:
一、索引合併是把幾個索引的範圍掃描合併成一個索引。
二、索引合併的時候,會對索引進行並集,交集或者先交集再並集操做,以便合併成一個索引。
三、這些須要合併的索引只能是一個表的。不能對多表進行索引合併。sql
簡單的說,索引合併,讓一條sql可使用多個索引。對這些索引取交集,並集,或者先取交集再取並集。從而減小從數據表中取數據的次數,提升查詢效率。數據結構
在使用explain對sql語句進行操做時,若是使用了索引合併,那麼在輸出內容的type列會顯示 index_merge,key列會顯示出全部使用的索引。以下:
優化
在explain的extra字段中會如下幾種:
Using union 索引取並集
Using sort_union 先對取出的數據按rowid排序,而後再取並集
Using intersect 索引取交集3d
你會發現並無 sort_intersect,由於根據目前的實現,想索引取交集,必須保證經過索引取出的數據順序和rowid順序是一致的。因此,也就不必sort了。code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
mysql> show create table test\G
*************************** 1. row ***************************
Table: test
Create Table: CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`key1_part1` int(11) NOT NULL DEFAULT '0',
`key1_part2` int(11) NOT NULL DEFAULT '0',
`key2_part1` int(11) NOT NULL DEFAULT '0',
`key2_part2` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `key1` (`key1_part1`,`key1_part2`),
KEY `key2` (`key2_part1`,`key2_part2`)
) ENGINE=MyISAM AUTO_INCREMENT=18 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
mysql> select * from test;
+----+------------+------------+------------+------------+
| id | key1_part1 | key1_part2 | key2_part1 | key2_part2 |
+----+------------+------------+------------+------------+
| 1 | 1 | 1 | 1 | 1 |
| 2 | 1 | 1 | 2 | 1 |
| 3 | 1 | 1 | 2 | 2 |
| 4 | 1 | 1 | 3 | 2 |
| 5 | 1 | 1 | 3 | 3 |
| 6 | 1 | 1 | 4 | 3 |
| 7 | 1 | 1 | 4 | 4 |
| 8 | 1 | 1 | 5 | 4 |
| 9 | 1 | 1 | 5 | 5 |
| 10 | 2 | 1 | 1 | 1 |
| 11 | 2 | 2 | 1 | 1 |
| 12 | 3 | 2 | 1 | 1 |
| 13 | 3 | 3 | 1 | 1 |
| 14 | 4 | 3 | 1 | 1 |
| 15 | 4 | 4 | 1 | 1 |
| 16 | 5 | 4 | 1 | 1 |
| 17 | 5 | 5 | 1 | 1 |
| 18 | 5 | 5 | 3 | 3 |
| 19 | 5 | 5 | 3 | 1 |
| 20 | 5 | 5 | 3 | 2 |
| 21 | 5 | 5 | 3 | 4 |
| 22 | 6 | 6 | 3 | 3 |
| 23 | 6 | 6 | 3 | 4 |
| 24 | 6 | 6 | 3 | 5 |
| 25 | 6 | 6 | 3 | 6 |
| 26 | 6 | 6 | 3 | 7 |
| 27 | 1 | 1 | 3 | 6 |
| 28 | 1 | 2 | 3 | 6 |
| 29 | 1 | 3 | 3 | 6 |
+----+------------+------------+------------+------------+
29 rows in set (0.00 sec)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from test where (key1_part1=4 and key1_part2=4) or (key2_part1=4 and key2_part2=4)\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: test
type: index_merge
possible_keys: key1,key2
key: key1,key2
key_len: 8,4
ref: NULL
rows: 3
Extra: Using sort_union(key1,key2); Using where
1 row in set (0.00 sec)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from test where (key1_part1=1 and key1_part2=1) or key2_part1=4\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: test
type: ALL
possible_keys: key1,key2
key: NULL
key_len: NULL
ref: NULL
rows: 29
Extra: Using where
1 row in set (0.00 sec)
|
從上面的兩個案例你們能夠發現,相同模式的sql語句,可能有時能使用索引,有時不能使用索引。是否能使用索引,取決於mysql查詢優化器對統計數據分析後,是否定爲使用索引更快。
所以,單純的討論一條sql是否可使用索引有點片面,還須要考慮數據。cdn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
mysql> show create table test\G
*************************** 1. row ***************************
Table: test
Create Table: CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`key1_part1` int(11) NOT NULL DEFAULT '0',
`key1_part2` int(11) NOT NULL DEFAULT '0',
`key2_part1` int(11) NOT NULL DEFAULT '0',
`key2_part2` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `key1` (`key1_part1`,`key1_part2`,`id`),
KEY `key2` (`key2_part1`,`key2_part2`,`id`)
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
|
數據結構和以前有所調整。主要調整有以下兩方面:
一、引擎從myisam改成了innodb。
二、組合索引中增長了id,並把id放在最後。blog
數據和上面的數據同樣。排序
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from test where (key1_part1=4 and key1_part2=4) or (key2_part1=4 and key2_part2=4)\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: test
type: index_merge
possible_keys: key1,key2
key: key1,key2
key_len: 8,8
ref: NULL
rows: 2
Extra: Using union(key1,key2); Using where
1 row in set (0.00 sec)
|
相同的數據,相同的sql語句,只是數據表結構有所調整,就從sort_union變爲了union。有如下幾個緣由:
一、只要經過索引取出的數據已經按rowid進行了排序,就可使用union。
二、組合索引中在最後加id字段,目的就是經過索引前兩個字段取出的數據是按id排序。
三、把引擎從myisam改成innodb,目的就是讓id和rowid的順序一致。索引
數據結構和數據和union案例中的一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mysql> explain select * from test where (key1_part1=1 and key1_part2=1) and (key2_part1=1 and key2_part2=1)\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: test
type: index_merge
possible_keys: key1,key2
key: key2,key1
key_len: 8,8
ref: NULL
rows: 3
Extra: Using intersect(key2,key1); Using where; Using index
1 row in set (0.02 sec)
|