-- 创建表 CREATE TABLE `test` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `a` int(11) NOT NULL, `b` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB;
-- 批量插入 10w 数据 DROPPROCEDURE IF EXISTS batchInsert; DELIMITER $ CREATEPROCEDURE batchInsert() BEGINDECLARE i INTDEFAULT1; START TRANSACTION; WHILE i<=100000 DO INSERT INTO test (a,b) VALUES (i,i); SET i=i+1; END WHILE; COMMIT; END $
CALL batchInsert();
-- 得到100000 selectcount(*) from test;
3.2 单个索引
全表扫描 ALL
1
explain select*from test;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
partitions
filtered
1
SIMPLE
test
ALL
NULL
NULL
NULL
NULL
100098
NULL
NULL
100.00
其中 type 值为 ALL,表示全表扫描了,我们看到 rows 这个字段显示有 100098 条,实际上我们一共才 10w 条数据,说明这个字段只是 mysql 的一个预估,不总是准确的。
ref
1 2 3
alter table test add index idx_a(a);
explain select*from test where a =10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
ref
idx_a
idx_a
4
const
1
NULL
100
全表扫描 ALL,Using where
1 2 3
-- 因为这条语句会从索引中查出 9w 条数据,全表扫描都才 10w 条数据,所以 mysql 决策是还不如直接全表扫描得了。
explain select*from test where a >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
ALL
idx_a
NULL
NULL
NULL
100098
Using where
50.00
range
1 2 3
-- 使用了 a 索引树, 因为满足索引只有 10000 条数据,mysql 认为 10000 条数据就算回表也要比全表扫描的代价低,因而决定查索引,但还是需要回表。
explain select*from test where a >90000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
10000
Using index condition
100.00
range 不回表
1 2 3
-- 只select 索引字段, 注意这次 Extra 的值为 `Using where; Using index`,表示查询用到了索引,且要查询的字段在索引中就能拿到,所以不需要回表。 -- 显然这种效率比上面的要高,这也是日常开发中不建议写 select * 的原因,尽量只查询业务所需的字段。 explain select a from test where a >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
50049
Using where; Using index
100.00
3.3 order by
range 不回表
1 2
-- 走 a 索引树, 不回表 explain select a from test where a >90000orderby a;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
10000
Using where; Using index
100.00
range 回表
1 2 3
-- 走 a 索引树, 需要回表 explain select*from test where a >90000orderby a; explain select b from test where a >90000orderby a;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
10000
Using index condition
100.00
range + 内存排序
1 2 3
-- Extra中返回了一个 Using filesort,说明无法利用索引完成排序,需要从内存或磁盘进行排序。就算 b 有索引, 也无法避免, 因为也会走 a 的索引树。
explain select a from test where a >90000orderby b;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
10000
Using index condition; Using filesort
100.00
3.4 多个字段索引
目前a, b 有自己的单个索引。
1 2 3
alter table test drop index idx_a_b; alter table test add index idx_a(a); alter table test add index idx_b(b);
没走索引,是因为无论如何都需要回表, 如果把10000改成90000, 会变成走索引加回表.
1 2 3
explain select*from test where a >10000; explain select a,b from test where a >10000; explain select b from test where a >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
ALL
idx_a
NULL
NULL
NULL
100098
Using where
50
直接走a 的索引树, 不回表
1
explain select a from test where a >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a
idx_a
4
NULL
50049
Using where; Using index
100
走 b 的索引树更好,回表
1
explain select a,b from test where a >90000and b =90000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
ref
idx_a,idx_b
idx_b
4
const
1
Using where
9.99
走 a 的索引树更好,回表
1
explain select a,b from test where a =90000and b >90000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
ref
idx_a,idx_b
idx_a
4
const
1
Using where
9.99
3.5 复合索引
一个复合索引,(a,b)复合索引
1 2 3
alter table test drop index idx_a; alter table test drop index idx_b; alter table test add index idx_a_b(a,b);
走 (a,b) 的索引树, 不回表
1 2 3 4
explain select*from test where a >10000; explain select a,b from test where a >10000; explain select b from test where a >10000; explain select a from test where a >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a_b
idx_a_b
4
NULL
50049
Using where; Using index
100
不满足最左匹配原则
1
explain select*from test where b >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
index
NULL
idx_a_b
8
NULL
100098
Using where; Using index
33.33
走 (a,b) 的索引树, 不回表, key_len=4
1
explain select a,b from test where a >90000and b =90000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a_b
idx_a_b
4
NULL
18142
Using where; Using index
10
走 (a,b) 的索引树, 不回表, key_len=8
1
explain select a,b from test where a =90000and b >90000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a_b
idx_a_b
8
NULL
1
Using where; Using index
100
普通索引和复合索引同时存在(2个普通索引,1个复合索引。)
1 2 3
alter table test add index idx_a(a); alter table test add index idx_b(b); alter table test add index idx_a_b(a,b);
走 (a,b) 的索引树更好, 不回表
1 2 3
explain select*from test where a >10000; explain select a,b from test where a >10000; explain select b from test where a >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a_b,idx_a
idx_a_b
4
NULL
50049
Using where; Using index
100
走 a 的索引树更好, 不回表
1
explain select a from test where a >10000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
range
idx_a_b,idx_a
idx_a
4
NULL
50049
Using where; Using index
100
走 b 的索引树, 需要回表. 其实此时走复合索引更好, 所以不建议复合索引和普通索引一起用
1
explain select a,b from test where a >90000and b =90000;
id
select_type
table
type
possible_keys
key
key_len
ref
rows
Extra
filtered
1
SIMPLE
test
ref
idx_a_b,idx_a,idx_b
idx_b
4
const
1
Using where
18.12
走 (a,b) 的索引树, 不回表
1
explain select a,b from test where a =90000and b >90000;