寒江夜钓

Gopher & linuxer, 关注基础架构,分布式,高并发,高可用。

0%

MySQL 多列索引

说说 mysql 联合索引, 解读 mysql 文档, 看看什么时候会利用 联合索引,什么时候 mysql server 不使用。

例子

建表语句

1
2
3
4
5
6
7
CREATE TABLE test (
id INT NOT NULL,
last_name CHAR(30) NOT NULL,
first_name CHAR(30) NOT NULL,
PRIMARY KEY (id),
INDEX name (last_name,first_name)
);

会利用索引的查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

// last_name 是索引最左字段,所以可以利用
SELECT * FROM test WHERE last_name='Widenius';

// 两个AND等值查询,可以利用
SELECT * FROM test
WHERE last_name='Widenius' AND first_name='Michael';

// 和上面等价,sql优化器会处理顺序问题
SELECT * FROM test
WHERE first_name='Michael' AND last_name='Widenius';

// first_name 可以是等值OR, 或者 范围条件
SELECT * FROM test
WHERE last_name='Widenius'
AND (first_name='Michael' OR first_name='Monty');

SELECT * FROM test
WHERE last_name='Widenius'
AND first_name >='M' AND first_name < 'N';

不会利用联合索引

1
2
3
4
5
6
7
// 条件中没有包含最左字段 last_name
SELECT * FROM test WHERE first_name='Michael';

// 个人认为, last_name 的条件会用到索引,因为最左原则;
// 最后和另一个条件合并结果
SELECT * FROM test
WHERE last_name='Widenius' OR first_name='Michael';

Index Merge Optimization

1
2
SELECT * FROM tbl_name
WHERE col1=val1 AND col2=val2;

对于这类的请求,如果有 multiple-column index, 则直接使用此索引。如果有单独索引 col1 或者 col2, 那么优化器尝试 Index Merge optimization, 或者尝试找到限制最多的索引(排除的行数最多,这样效率较高)。

Index Merge 是指,对于多个范围扫描,把合并结果。合并结果的类型可以是 并集,交集,等。索引不能跨表。
以下例子可能会利用 Index Merge.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// key1, key2 有单独索引, 分别利用索引,结果合并
SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20;

// 和上面一样, 得到合集后,扫描合集筛选 non-key 字段
SELECT * FROM tbl_name
WHERE (key1 = 10 OR key2 = 20) AND non_key = 30;

// 时利用两个索引scan,取交集
SELECT * FROM tbl_name
WHERE (key1_part1 = 1 AND key1_part2 = 2) AND key2 = 2;

// 同上,主键可以是范围查询
SELECT * FROM innodb_table
WHERE primary_key < 10 AND key_col1 = 20;

// 取并集
SELECT * FROM t1
WHERE key1 = 1 OR key2 = 2 OR key3 = 3;

最左前缀

假设有 (col1, col2, col3) 联合索引,最左前缀为 col1, 不包含col1条件的查询,不会使用此联合索引。

1
2
3
4
5
6
7
// 会使用
SELECT * FROM tbl_name WHERE col1=val1;
SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;

// 不会使用
SELECT * FROM tbl_name WHERE col2=val2;
SELECT * FROM tbl_name WHERE col2=val2 AND col3=val3;