MySQL优化篇:单表索引失效


1、准备环境

建表并插入数据

CREATE TABLE `dept` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
    `deptName` VARCHAR(30) DEFAULT NULL, 
    `address` VARCHAR(40) DEFAULT NULL, 
     ceo INT NULL , PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `emp` (
	`id` INT(11) NOT NULL AUTO_INCREMENT, 
    `empno` INT NOT NULL , 
    `name` VARCHAR(20) DEFAULT NULL, 
    `age` INT(3) DEFAULT NULL, 
    `deptId` INT(11) DEFAULT NULL, 
    PRIMARY KEY (`id`)
	#CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) 		REFERENCES `t_dept` (`id`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2、全值匹配我最爱

执行以下SQL语句

EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30;
EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=4;
EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=4 AND emp.name = 'abcd';

以上语句执行情况如下所示:

mysql> explain select SQL_NO_CACHE * from emp where age=30;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL | 500493 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set

mysql> explain select SQL_NO_CACHE * from emp where age=30 and deptid=4;
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL | 500493 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set

mysql> explain select SQL_NO_CACHE * from emp where age=30 and deptid=4 and name='abcd';
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows   | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
|  1 | SIMPLE      | emp   | ALL  | NULL          | NULL | NULL    | NULL | 500493 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+
1 row in set

建立索引

CREATE INDEX idx_age_deptid_name ON emp(age,deptid,NAME);

再次执行上述SQL语句

mysql> explain select SQL_NO_CACHE * from emp where age=30;
+----+-------------+-------+------+---------------------+---------------------+---------+-------+-------+-------------+
| id | select_type | table | type | possible_keys       | key                 | key_len | ref   | rows  | Extra       |
+----+-------------+-------+------+---------------------+---------------------+---------+-------+-------+-------------+
|  1 | SIMPLE      | emp   | ref  | idx_name_deptid_age | idx_name_deptid_age | 5       | const | 47248 | Using where |
+----+-------------+-------+------+---------------------+---------------------+---------+-------+-------+-------------+
1 row in set

mysql> explain select SQL_NO_CACHE * from emp where age=30 and deptid=4;
+----+-------------+-------+------+---------------------+---------------------+---------+-------------+------+-------------+
| id | select_type | table | type | possible_keys       | key                 | key_len | ref         | rows | Extra       |
+----+-------------+-------+------+---------------------+---------------------+---------+-------------+------+-------------+
|  1 | SIMPLE      | emp   | ref  | idx_name_deptid_age | idx_name_deptid_age | 10      | const,const |    5 | Using where |
+----+-------------+-------+------+---------------------+---------------------+---------+-------------+------+-------------+
1 row in set

mysql> explain select SQL_NO_CACHE * from emp where age=30 and deptid=4 and name='abcd';
+----+-------------+-------+------+---------------------+---------------------+---------+-------------------+------+-------------+
| id | select_type | table | type | possible_keys       | key                 | key_len | ref               | rows | Extra       |
+----+-------------+-------+------+---------------------+---------------------+---------+-------------------+------+-------------+
|  1 | SIMPLE      | emp   | ref  | idx_name_deptid_age | idx_name_deptid_age | 73      | const,const,const |    1 | Using where |
+----+-------------+-------+------+---------------------+---------------------+---------+-------------------+------+-------------+
1 row in set

mysql>

结论:全值匹配我最爱指的是,查询的字段按照顺序在索引中都可以匹配到。

在这里插入图片描述

SQL中查询字段的顺序,跟使用索引中字段的顺序,没有关系。优化器会在不影响SQL执行结果的前提下,进行自动优化。

2、最佳作前缀法则

在这里插入图片描述

查询字段与索引字段顺序的不同会导致,索引无法充分使用,甚至索引失效。

原因:使用复合索引,需要遵循最佳左前缀法则,即如果索引包含多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并不跳过索引中的列

**结论:**过滤条件要使用索引必须按照索引建立时的顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用。

3、不要在索引列上做任何计算

不在索引列上做任何操作(计算、函数(自动or手动)类型转换),会导致索引失效而转向全表扫描。

3.1 在查询列上使用了函数

执行以下SQL语句

EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE age=30;
EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE LEFT(age,3)=30;

在这里插入图片描述

结论:等号左边无计算

3.2 在查询列上做了转换

首先建立索引,执行以下SQL语句

CREATE INDEX idx_name onemp(name);
explain select sql_no_cache * from emp where name='30000';
explain select sql_no_cache * from emp where name=30000;

字符串不加单引号,则会在name列上做一次转换。

在这里插入图片描述

结论:等号右边无转换!

4、索引列上不能有范围查询

explain SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=5 AND emp.name = 'abcd';
explain SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid<=5 AND emp.name = 'abcd';

在这里插入图片描述

建议:将可能做范围查询的字段的索引顺序放在最后。

5、尽量使用覆盖索引

即查询列和索引列一致,不要写select *

explain SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptId=4 and name='XamgXt';
explain SELECT SQL_NO_CACHE age,deptId,name FROM emp WHERE emp.age=30 and deptId=4 and name='XamgXt';

在这里插入图片描述

6、使用不等于(!=或者<>)的时候

MySQL在使用不等于(!=或者<>)时,有时无法使用索引会导致全表扫描。

在这里插入图片描述

7、字段的is not null和is null

当字段允许为NULL的条件时:

在这里插入图片描述

结论:is not null用不到索引,is null可以用到索引

8、like的前后模糊匹配

在这里插入图片描述

结论:前缀不能出现模糊匹配!

9、减少使用or

在这里插入图片描述

使用union all或者union来替代:

在这里插入图片描述

union和union all的区别

UNION去重且排序;UNION ALL不去重不排序

  • union:对两个或多个结果集进行并集操作,不包括重复行,同时进行默认规则的排序
  • union all:对两个或多个结果集进行并集操作,包括重复行,不进行排序
  • union 和 union all都可以将多个结果集合并,而不仅仅是两个,你可以将多个结果集串起来
  • 使用union和union all必须保证各个select 集合的结果有相同个数的列,并且每个列的类型是一样的
  • union因为要进行重复值扫描,所以效率低。如果合并没有刻意要删除重复行,那么就使用union all

10、索引使用口诀

  1. 全值匹配我最爱,最左前缀要遵守;
  2. 带头大哥不能死,中间兄弟不能断;
  3. 索引列上少计算,范围之后全失效;
  4. LIKE百分写最右,覆盖索引不写*;
  5. 不等空值还有OR,索引影响要注意;
  6. VAR引号不可丢,SQL优化有诀窍。

11、索引示例巩固

假设建立索引index(a,b,c)

Where语句索引是否被使用
where a = 3Y,使用到a
where a = 3 and b=5Y,使用到a,b
where a = 3 and b=5 and c=4Y,使用到a,b,c
where b=3或者where b=3 and c=4
或者 where c=5
N
where a=3 and c=5使用到a,但是c不可以,b中间断了
where a=3 and b>4 and c =5使用到a,b
c不能用在范围之后,b断了
where a is null and b is not nullis null支持索引,但是is not null不支持,所以a可以使用索引,b不可以使用
where a <> 3不能使用索引
where abs(a) = 3不能使用索引
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%k%’ and c=5Y,使用到a,b,c
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值