索引使用场景
哪些情况需要创建索引
主键自动
建立主键唯一索引频繁
作为where查询条件
的字段应该创建索引- 多表关联查询中,
关联on两边的字段
应该创建索引 - 查询中
排序的字段
,应该创建有顺序的索引B+Tree - 利用组合索引完成
索引覆盖
避免回表 统计或分组字段
,应该创建索引
哪些情况不需要创建索引
表记录太少
,因为索引也是有存储开销的频繁更新
,因为索引也会更新维护- 查询字段
使用频率不高
为什么使用组合索引
由多个字段组成的索引,使用顺序就是创建时的顺序。
ALTER TABLE 'table_name' ADD INDEX index_name(col1,col2,col3)
组合索引在一个索引树上存在多个字段,这样的优势是容易形成索引覆盖
从而提高查询效率。
组合索引在使用时,遵循最左前缀原则:
- 前缀索引
LIKE '常量%'
(‘hello%’)使用索引
LIKE '%常量'
(’%hello’)不使用索引
- 最左前缀
从左向右匹配直到遇到返回查询(>、<、between)后索引失效
例如:索引(idx_a_b_c),其中a、b、c分别代表表中的三个字段- where a=1 and b=1 and c=1 会使用索引(abc)
- where a=1 and b=1 and c>1 会使用索引(abc)
- where a=1 and b>1 and c=1 会使用索引(ab)
- where a=1 and c=1 and b=1 会使用索引(abc)
- where b=1 and c=1 不会使用索引
注:组合索引的使用必须按照创建时的顺序才能匹配,mysql会根据where条件自动优化顺序以匹配组合索引。
索引失效分析
示例表结构:
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NULL,
`sex` tinyint(3) unsigned NULL DEFAULT '0' COMMENT '0 :男 1:女',
`age` tinyint(3) unsigned NULL,
`time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_name_sex_age` (`name`,`sex`,`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8;
示例在使用全索引的情况下,key_length长度应为154;只使用了name的情况为152,name和sex为153。
- 全值匹配
mysql> explain select * from `user` where `name`='ym' and sex=1 and age=20;
+----+-------------+-------+------+------------------+------------------+---------+-------------------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------------------+---------+-------------------+------+-----------------------+
| 1 | SIMPLE | user | ref | idx_name_sex_age | idx_name_sex_age | 154 | const,const,const | 1 | Using index condition |
+----+-------------+-------+------+------------------+------------------+---------+-------------------+------+-----------------------+
条件与索引一一对应的情况,可以使用索引
- 最左前缀法则
组合索引:带头索引不能死,中间索引不能断
错误示例:
- 带头索引死:
mysql> explain select * from `user` where sex=0 and age=20;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 1000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
- 中间索引断(带头索引生效,其他索引失效)
mysql> explain select * from `user` where `name`='ym' and age=20;
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | user | ref | idx_name_sex_age | idx_name_sex_age | 152 | const | 1 | Using index condition |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
- 不要在索引上做计算
不要进行这些操作:
计算、函数、自动/手动类型转换
,不然会导致索引失效而转向全表扫描
mysql> explain select * from `user` where SUBSTRING(`name`,2)='ym';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 1000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
- 范围条件右边的列失效
不能继续使用索引中范围条件(bettween、<、>、in等)右边的列
mysql> explain select * from `user` where `name`='ym' and sex>0 and age=20;
+----+-------------+-------+-------+------------------+------------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------+------------------+---------+------+------+-----------------------+
| 1 | SIMPLE | user | range | idx_name_sex_age | idx_name_sex_age | 153 | NULL | 1 | Using index condition |
+----+-------------+-------+-------+------------------+------------------+---------+------+------+-----------------------+
- 尽量使用索引覆盖
尽量使用覆盖索引(只查询索引的列),也就是索引列和查询列一致,减少select *
mysql> explain select * from `user`;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 1000 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
mysql> explain select `name`,sex,age,time from `user`;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 1000 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
mysql> explain select id,`name`,sex,age from `user`;
+----+-------------+-------+-------+---------------+------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------------------+---------+------+------+-------------+
| 1 | SIMPLE | user | index | NULL | idx_name_sex_age | 154 | NULL | 1000 | Using index |
+----+-------------+-------+-------+---------------+------------------+---------+------+------+-------------+
- 索引字段上不要使用不等于
索引字段上使用(!= 或者 < >)判断时,会导致索引失效而转向全表扫描
注:主键索引会使用范围索引,辅助索引会失效
mysql> explain select * from `user` where `name`!='ym';
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| 1 | SIMPLE | user | ALL | idx_name_sex_age | NULL | NULL | NULL | 1000 | Using where |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
mysql> explain select * from `user` where id != 1;
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | user | range | PRIMARY | PRIMARY | 4 | NULL | 999 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
- 辅助索引使用null判断时
使用is null判断时可以使用索引,而使用is not null时不使用索引
mysql> explain select * from `user` where `name` is null;
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | user | ref | idx_name_sex_age | idx_name_sex_age | 153 | const | 1 | Using index condition |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
mysql> explain select * from `user` where `name` is not null;
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| 1 | SIMPLE | user | ALL | idx_name_sex_age | NULL | NULL | NULL | 1000 | Using where |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
- 索引字段使用like时不以通配符开头
索引字段使用like以通配符开头(‘%字符串’)时,会导致索引失效而转向全表扫描
mysql> explain select * from `user` where `name` like 'ym%';
+----+-------------+-------+-------+------------------+------------------+---------+------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------+------------------+---------+------+------+-----------------------+
| 1 | SIMPLE | user | range | idx_name_sex_age | idx_name_sex_age | 153 | NULL | 1 | Using index condition |
+----+-------------+-------+-------+------------------+------------------+---------+------+------+-----------------------+
mysql> explain select * from `user` where `name` like '%ym';
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 1000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
解决like通配符开头索引失效问题,可以使用覆盖索引解决。
mysql> explain select id,`name`,sex,age from `user` where `name` like '%ym';
+----+-------------+-------+-------+---------------+------------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------------------+---------+------+------+--------------------------+
| 1 | SIMPLE | user | index | NULL | idx_name_sex_age | 156 | NULL | 1000 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------------+---------+------+------+--------------------------+
- 索引字段为字符串时要加引号
索引字段是字符串,但查询时不加单引号,会导致索引失效而转向全表扫描
mysql> explain select * from `user` where `name`=123 and age=20;
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| 1 | SIMPLE | user | ALL | idx_name_sex_age | NULL | NULL | NULL | 1000 | Using where |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
mysql> explain select * from `user` where `name`='123' and age=20;
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
| 1 | SIMPLE | user | ref | idx_name_sex_age | idx_name_sex_age | 153 | const | 1 | Using index condition |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
- 索引字段不要使用or
索引字段使用 or 时,会导致索引失效而转向全表扫描
mysql> explain select * from `user` where `name`='ym' or sex=0;
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
| 1 | SIMPLE | user | ALL | idx_name_sex_age | NULL | NULL | NULL | 1000 | Using where |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
总结
假设索引 index(a,b,c)
Where | 索引使用 |
---|---|
where a=3 | Y,使用到a |
where a=3 and b=4 | Y,使用到a,b |
where a=3 and b=4 and c=5 | Y,使用到a,b,c |
where b=4 或 where b=4 and c=5 或 where c=5 | N |
where a=3 and c=5 | Y,使用到a |
where a=3 and b>4 and c=5 | Y,使用到a,b |
where a=3 and b like ‘kk%’ and c=5 | Y,使用到a,b,c |
where a=3 and b like ‘%kk’ and c=5 | Y,使用到a |
where a=3 and b like ‘%kk%’ and c=5 | Y,使用到a |
where a=3 and b like ‘k%kk%’ and c=5 | Y,使用到a,b,c |
优化口诀:
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE百分写最右,覆盖索引不写星;
不等空值还有OR,索引失效要少用;