Mysql笔记(三)索引使用场景及索引失效分析

索引使用场景

哪些情况需要创建索引
  • 主键自动建立主键唯一索引
  • 频繁作为where查询条件的字段应该创建索引
  • 多表关联查询中,关联on两边的字段应该创建索引
  • 查询中排序的字段,应该创建有顺序的索引B+Tree
  • 利用组合索引完成索引覆盖避免回表
  • 统计或分组字段,应该创建索引
哪些情况不需要创建索引
  • 表记录太少,因为索引也是有存储开销的
  • 频繁更新,因为索引也会更新维护
  • 查询字段使用频率不高
为什么使用组合索引

由多个字段组成的索引,使用顺序就是创建时的顺序。

ALTER TABLE 'table_name' ADD INDEX index_name(col1,col2,col3)

组合索引在一个索引树上存在多个字段,这样的优势是容易形成索引覆盖从而提高查询效率。
组合索引在使用时,遵循最左前缀原则:

  1. 前缀索引
    LIKE '常量%'(‘hello%’) 使用索引
    LIKE '%常量'(’%hello’) 不使用索引
  2. 最左前缀
    从左向右匹配直到遇到返回查询(>、<、between)后索引失效
    例如:索引(idx_a_b_c),其中a、b、c分别代表表中的三个字段
    1. where a=1 and b=1 and c=1 会使用索引(abc)
    2. where a=1 and b=1 and c>1 会使用索引(abc)
    3. where a=1 and b>1 and c=1 会使用索引(ab)
    4. where a=1 and c=1 and b=1 会使用索引(abc)
    5. 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。

  1. 全值匹配
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 |
+----+-------------+-------+------+------------------+------------------+---------+-------------------+------+-----------------------+

条件与索引一一对应的情况,可以使用索引

  1. 最左前缀法则

组合索引:带头索引不能死,中间索引不能断

错误示例

  • 带头索引死:
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 |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
  1. 不要在索引上做计算

不要进行这些操作:计算、函数、自动/手动类型转换,不然会导致索引失效而转向全表扫描

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 |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
  1. 范围条件右边的列失效

不能继续使用索引中范围条件(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 |
+----+-------------+-------+-------+------------------+------------------+---------+------+------+-----------------------+
  1. 尽量使用索引覆盖

尽量使用覆盖索引(只查询索引的列),也就是索引列和查询列一致,减少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 |
+----+-------------+-------+-------+---------------+------------------+---------+------+------+-------------+
  1. 索引字段上不要使用不等于

索引字段上使用(!= 或者 < >)判断时,会导致索引失效而转向全表扫描
注:主键索引会使用范围索引,辅助索引会失效

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 |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
  1. 辅助索引使用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 |
+----+-------------+-------+------+------------------+------+---------+------+------+-------------+
  1. 索引字段使用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 |
+----+-------------+-------+-------+---------------+------------------+---------+------+------+--------------------------+
  1. 索引字段为字符串时要加引号

索引字段是字符串,但查询时不加单引号,会导致索引失效而转向全表扫描

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 |
+----+-------------+-------+------+------------------+------------------+---------+-------+------+-----------------------+
  1. 索引字段不要使用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=3Y,使用到a
where a=3 and b=4Y,使用到a,b
where a=3 and b=4 and c=5Y,使用到a,b,c
where b=4 或 where b=4 and c=5 或 where c=5N
where a=3 and c=5Y,使用到a
where a=3 and b>4 and c=5Y,使用到a,b
where a=3 and b like ‘kk%’ and c=5Y,使用到a,b,c
where a=3 and b like ‘%kk’ and c=5Y,使用到a
where a=3 and b like ‘%kk%’ and c=5Y,使用到a
where a=3 and b like ‘k%kk%’ and c=5Y,使用到a,b,c

优化口诀:
全值匹配我最爱,最左前缀要遵守;
带头大哥不能死,中间兄弟不能断;
索引列上少计算,范围之后全失效;
LIKE百分写最右,覆盖索引不写星;
不等空值还有OR,索引失效要少用;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值