Mysql基础学习(十九)--- 索引优化

创建了索引如何避免索引失效

首先创建一个表 staffs

CREATE TABLE staffs(
	id INT PRIMARY KEY AUTO_INCREMENT,
	`NAME` VARCHAR(20) NOT NULL ,
	age INT NOT NULL DEFAULT 0 ,
	pos VARCHAR(20) NOT NULL,
	add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
)CHARSET utf8 COMMENT '员工记录表';

INSERT INTO staffs (name,age,pos,add_time) VALUES ('z3',22,'manager',NOW());
INSERT INTO staffs (name,age,pos,add_time) VALUES ('l4',23,'dev',NOW());
INSERT INTO staffs (name,age,pos,add_time) VALUES ('2000',23,'dev',NOW());

ALTER TABLE ADD INDEX idx_staffs_nameAgePos(name,age,pos);

索引优化避免失效法则
在这里插入图片描述
以下挨个解释以下。
首先查看我们创建得索引

mysql> show index from staffs;
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table  | Non_unique | Key_name              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| staffs |          0 | PRIMARY               |            1 | id          | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            1 | NAME        | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            2 | age         | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| staffs |          1 | idx_staffs_nameAgePos |            3 | pos         | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
+--------+------------+-----------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
4 rows in set (0.00 sec)

现在可以看到 id 是主键, name,age,pos 是复合索引。

  1. 全值匹配我最爱
使用 EXPLAIN 分析SQL
EXPLAIN SELECT * FROM staffs WHERE name = "July";
# 返回结果
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 62      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+

EXPLAIN SELECT * FROM staffs WHERE name = "July" AND age = 23 AND pos = "dev";
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 128     | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)


EXPLAIN SELECT * FROM staffs WHERE name = "July" AND age = 23;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref         | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 66      | const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

EXPLAIN SELECT * FROM staffs WHERE name = "July"  AND pos = "dev";
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra                 |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 62      | const |    1 |    33.33 | Using index condition |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

EXPLAIN SELECT * FROM staffs WHERE  pos = "dev";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+

通过上面EXPLAIN 分析得出,如果使用符合索引,那么想要避免索引失效。 就需要遵守最佳左前缀法则: 如果索引了多列,要遵守最左前缀法则,指的是查询从索引最左前列开始并且不跳过索引中得列。
打个比方就是,正常火车都是一节一节得,这里得 name 就比如一个火车头,在最前面。如果你得火车头都没有,那你得火车怎么往前面跑呢?你有了火车头, 这个时候。你得索引顺序应该是 name, age , pos, 但是你现在没有 age,那么 你得火车就好比 火车得第二节断掉了, 那么火车头自然也拉不到 第三节第四节。 不过 火车头得第二节第三节调换顺序好像是没有问题得。但是你使用得多列必须一个不能少。

EXPLAIN SELECT * FROM staffs WHERE name = "July"  AND pos = "dev" AND age = 23;
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 128     | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
  1. 在索引列上使用计算,函数,以及类型转换,会导致索引失效而变为全表扫描
# 使用小写函数进行查询然后分析SQL
SELECT * FROM staffs WHERE LOWER(name) = "july";
+----+------+-----+-----+---------------------+
| id | NAME | age | pos | add_time            |
+----+------+-----+-----+---------------------+
|  3 | July |  23 | dev | 2019-10-08 10:19:42 |
+----+------+-----+-----+---------------------+
# 分析使用函数
 EXPLAIN SELECT * FROM staffs WHERE LOWER(name) = "july";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |   100.00 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+

# 分析不适用函数
 EXPLAIN SELECT * FROM staffs WHERE name = "July";
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 62      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

发现使用函数得扫描类型为 all 。没有使用索引。 没有使用函数得为 ref , 并且使用了索引。

  1. 存储引擎不能使用索引中范围条件右边的列
EXPLAIN SELECT * FROM staffs WHERE name="July" AND age > 20;
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys         | key                   | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | staffs | NULL       | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 66      | NULL |    1 |   100.00 | Using index condition |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

 EXPLAIN SELECT * FROM staffs WHERE name="July" AND age > 20 AND pos="dev";
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys         | key                   | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | staffs | NULL       | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 66      | NULL |    1 |    33.33 | Using index condition |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

这两个explain SQL语句比较后发现,在索引列 age > 20 比较之后。右侧还有索引列进行条件检索, 虽然 key 使用了创建的索引,但是 key_len并没有发生变化。

  1. 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致))减少SELECT *
EXPLAIN SELECT NAME , age, pos FROM staffs WHERE name="July" AND age = 23 AND pos = "dev";
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 128     | const,const,const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------------+

EXPLAIN SELECT * FROM staffs WHERE name="July" AND age = 23 AND pos = "dev";
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref               | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 128     | const,const,const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------------------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

对比两个EXPLAIN SQL 发现Extra 中 一个use index, 一个为Null

  1. 使用不等于( != 和 <>) 会导致全表扫描
EXPLAIN SELECT * FROM staffs where name != 'July';
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys         | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 |   100.00 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+

EXPLAIN SELECT * FROM staffs where name <> 'July';
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys         | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 |   100.00 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到possibles_keys 虽然说,可用的索引有 idx_staffs_nameAgePos, 但是实际使用到的 key 没有使用到索引/。

  1. is null , 和 is not null 也无法使用索引。
EXPLAIN SELECT * FROM staffs where name IS NULL;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra            |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+
|  1 | SIMPLE      | NULL  | NULL       | NULL | NULL          | NULL | NULL    | NULL | NULL |     NULL | Impossible WHERE |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------+

EXPLAIN SELECT * FROM staffs where name IS NOT NULL;
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys         | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 |    66.67 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

可以看到以上 使用 IS NULL 和 IS NOT NULL,实际使用索引 key 中 都为NULL

  1. Like 以通配符开头("%abc…")mysql 索引失效会变成全表扫描的操作
# % 在两边
 EXPLAIN SELECT *FROM staffs where name LIKE "%Ju%";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+

# % 在左边
EXPLAIN SELECT *FROM staffs where name LIKE "%July";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+

# %在右边
 EXPLAIN SELECT * 
 FROM staffs where name LIKE "July%";
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
| id | select_type | table  | partitions | type  | possible_keys         | key                   | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | staffs | NULL       | range | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 62      | NULL |    1 |   100.00 | Using index condition |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)

从以上结果可以看出。只有 % 在右边的 才使用了索引。

那么如何解决使用 LIKE %abc% 时索引不被使用的问题呢

举例子

EXPLAIN SELECT name,age,pos FROM staffs WHERE name  LIKE "%July%";
+----+-------------+--------+------------+-------+---------------+-----------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key                   | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+-----------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_staffs_nameAgePos | 128     | NULL |    3 |    33.33 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+-----------------------+---------+------+------+----------+--------------------------+

 EXPLAIN SELECT id,name,age,pos FROM staffs WHERE name  LIKE "%July%";
+----+-------------+--------+------------+-------+---------------+-----------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys | key                   | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+---------------+-----------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | NULL          | idx_staffs_nameAgePos | 128     | NULL |    3 |    33.33 | Using where; Using index |
+----+-------------+--------+------------+-------+---------------+-----------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

当我们指定搜索字段为 id,name,age,pos 的时候,发现 type 是 Index,并且key实际使用索引为 idx_staffs_nameAgePos

使用 指定不为覆盖索引的索引列 add_time 或者 使用 * 的时候

 EXPLAIN SELECT id,name,age,pos,add_time FROM staffs WHERE name  LIKE "%July%";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

EXPLAIN SELECT * FROM staffs WHERE name  LIKE "%July%";
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+---------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

这个时候就会发现 type 为 all, 并且实际使用索引为null。 发现并没有使用到索引

  1. 字符串不加单引号索引失效
# 更新一个数据
update staffs set name='2000' where id = 2
# 查询 姓名为 2000 的, name 的类型为 VARCHAR
EXPLAIN SELECT * FROM staffs WHERE name=2000;
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys         | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 |    33.33 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
1 row in set, 3 warnings (0.00 sec)
# 加上单引号进行检索
EXPLAIN SELECT * FROM staffs WHERE name='2000';
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
| id | select_type | table  | partitions | type | possible_keys         | key                   | key_len | ref   | rows | filtered | Extra |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | staffs | NULL       | ref  | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 62      | const |    1 |   100.00 | NULL  |
+----+-------------+--------+------------+------+-----------------------+-----------------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

通过以上看到进行字符串搜索的时候,如果不加上 单引号,会导致索引失效。 这是因为mysql 会在你搜索2000的时候因为你没有加单引号,所以会自动把 2000 这个转换为字符串在进行搜索。这个就相当于进行了一次类型转换。所以 实际使用索引 key 这里为NULL

  1. 少用 or , 用它连接时会索引失效。
EXPLAIN select * from staffs where name='July' or age = 23;
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys         | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | staffs | NULL       | ALL  | idx_staffs_nameAgePos | NULL | NULL    | NULL |    3 |    55.56 | Using where |
+----+-------------+--------+------------+------+-----------------------+------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
# 解决使用 or 索引失效的办法
EXPLAIN select id,name,age,pos from staffs where name='July' or age = 23;
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+--------------------------+
| id | select_type | table  | partitions | type  | possible_keys         | key                   | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | staffs | NULL       | index | idx_staffs_nameAgePos | idx_staffs_nameAgePos | 128     | NULL |    3 |    55.56 | Using where; Using index |
+----+-------------+--------+------------+-------+-----------------------+-----------------------+---------+------+------+----------+--------------------------+
1 row in set, 1 warning (0.00 sec)

通过以上分析可以看出, 使用 or ,并且查询的不是覆盖索引中的索引列的时候,查询 type 为 all, 全表扫描。并且实际使用索引 key 为 null.
但是如果查询的 为 name,age,pos 是覆盖索引的索引列。查询 type 为 index, 并且使用了 覆盖索引。 idx_staffs_nameAgePos

Order By 优化

先创建一张表 tbA

CREATE TABLE tbA(
	id INT PRIMARY NOT NULL AUTO_INCREMENT,
	age INT,
	birth TIMESTAMP NOT NULL
);
INSERT INTO tbA(age,birth) VALUES (22, NOW());
INSERT INTO tbA(age,birth) VALUES (23, NOW());
INSERT INTO tbA(age,birth) VALUES (24, NOW());
# 创建索引
CREATE INDEX idx_A_ageBirth ON tbA(age,birth);

使用explain 分析sql 语句

# 使用索引列 age 并对 索引列 age 进行排序
EXPLAIN SELECT * FROM tbA WHERE age > 20 ORDER BY age
    -> ;
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys  | key            | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | tbA   | NULL       | index | idx_A_ageBirth | idx_A_ageBirth | 9       | NULL |    3 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+--------------------------+
# using where  和  using index

# 使用索引列 age  并对索引列 birth 进行排序
EXPLAIN SELECT * FROM tbA WHERE age > 20 ORDER BY birth;
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
| id | select_type | table | partitions | type  | possible_keys  | key            | key_len | ref  | rows | filtered | Extra                                    |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
|  1 | SIMPLE      | tbA   | NULL       | index | idx_A_ageBirth | idx_A_ageBirth | 9       | NULL |    3 |   100.00 | Using where; Using index; Using filesort |
+----+-------------+-------+------------+-------+----------------+----------------+---------+------+------+----------+------------------------------------------+
1 row in set, 1 warning (0.00 sec)
# Using Where , Using Index, Using filesort

# 仅对索引列 age 进行排序
EXPLAIN SELECT * FROM tbA ORDER BY age;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key            | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | tbA   | NULL       | index | NULL          | idx_A_ageBirth | 9       | NULL |    3 |   100.00 | Using index |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
# Using index

# 只对索引列 birth 进行排序
 EXPLAIN SELECT * FROM tbA ORDER BY birth;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type  | possible_keys | key            | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | tbA   | NULL       | index | NULL          | idx_A_ageBirth | 9       | NULL |    3 |   100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
# Using index, Using filesort

# 对索引列 age 进行排序
EXPLAIN SELECT * FROM tbA WHERE birth < NOW() ORDER BY age;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key            | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | tbA   | NULL       | index | NULL          | idx_A_ageBirth | 9       | NULL |    3 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+--------------------------+
# Using where , Using index

# 对age 进行 ASC 排序, 对 birth 进行 DESC 排序
EXPLAIN SELECT * FROM tbA ORDER BY age ASC,birth DESC;
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type  | possible_keys | key            | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | tbA   | NULL       | index | NULL          | idx_A_ageBirth | 9       | NULL |    3 |   100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+----------------+---------+------+------+----------+-----------------------------+
1 row in set, 1 warning (0.00 sec)
# Using index, Using filesort
  1. OrderBy 子句,尽量使用 index 方式排序,避免使用 FileSort 方式排
  2. Order By 满足两种情况,会使用 index 排序, filesort的方式效率较低
    • Order By 语句使用索引最左前缀
    • 使用 WHERE 子句和 ORDER BY子句条件列组合满足索引前列 (如果where使用的索引最左前缀定义为常量,则order by 可以使用索引)
  3. 尽可能在索引列上完成排序操作,遵照索引建的最佳左前缀
  4. 如果不能在索引列上,filesort 两种算法: Mysql 就要启动双路排序和单路排序
    • 双路排序A:Mysql 4.1 之前是使用双路排序,字面意思是两次扫描磁盘,最终得到数据,读取行指针和 orderby列, 对他们进行排序,然后扫描已经排序好的列表,按照列表中的值重新从列表中读取去数据,从磁盘中去排序字段,在 buffer 中进行排序,在从磁盘中取其他字段
    • 单路排序B:从磁盘中读取查询需要的所有列,然后按照 order by 列在buffer对他们进行排序,然后扫面描排序后的列表进行输出,它的效率更快一些,避免了第二次读取数据,并且把随机 IO 变成了顺序 IO,但是它会使用更多的空间,因为他把每一行都保存到内存中了。
    • 单路算法问题:在sort_buffer中,方法B比方法A要多占用空间,因为方法B是把所有字段都取出来,所以有可能取出的数据总大小超出了 sort_buffer的容量,导致每次只能取sort_buffer容量的大小的数据,进行排序,创建 tmp 文件,进行多路合并,排序再取sort_buffer 容量大小,在排 从而导致多次 IO,本来是只有一次IO操作,结果反而导致了大量的IO操作,得不偿失
  5. 优化策略
    • 增大 sort_buffer_size 参数的设置
    • 增大 max_length_for_sort_data 参数的设置
    • Order By 的时候,使用 SELECT * ,是一个大忌,因为当Query的字段大小总和小于 max_length_for_sort_data 而且排序的字段不是 text|blob的时候,会用改进的算法 单路排序,否则使用 多路排序,两种算法的数据,都有可能超出 sort_buffer的容量,超出之后,会创建 tmp 文件并进行合并排序,导致多次IO,但是使用单路排序的算法的风险会更大一些,所以要提高 sort_buffer_size.
    • 尝试提高 sort_buffer_size
    • 尝试提高 max_length_for_sort_data,提高这个参数们会增加用改进算法的概率。如果设置的太高,数据总量超出 sort_buffer _size 的概率就会增大,明显症状是 高的磁盘IO,和 低的处理器使用率。
GROUP BY 关键字优化
  1. group by 实质是先排序后分组,遵照索引的最左前缀
  2. 当无法使用索引列。增大 max_length_for_sort_data 参数的设置, + 增大 sort_buffer_size 参数的设置
  3. where 高于 having , 能写在 where 限定的条件就不要去 having 限定了。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值