1、压测

  1. /data/app/mysql5728/bin/mysqlslap --defaults-file=/data/3307/conf/my.cnf --concurrency=100 --iterations=1 --create-schema='test' \
  2. --query="select * from test.t100w where k2='780P'" engine=innodb --number-of-queries=2000 -uroot -p123 -verbose
  3. #未添加索引压测结果
  4. Benchmark
  5. Running for engine rbose
  6. Average number of seconds to run all queries: 873.674 seconds
  7. Minimum number of seconds to run all queries: 873.674 seconds
  8. Maximum number of seconds to run all queries: 873.674 seconds
  9. Number of clients running queries: 100
  10. Average number of queries per client: 20
  11. #给k2字段添加索引
  12. alter table t100w add index idx_k2(k2);
  13. #添加索引压测结果
  14. Benchmark
  15. Running for engine rbose
  16. Average number of seconds to run all queries: 0.288 seconds
  17. Minimum number of seconds to run all queries: 0.288 seconds
  18. Maximum number of seconds to run all queries: 0.288 seconds
  19. Number of clients running queries: 100
  20. Average number of queries per client: 20

2、查询表的索引

desc test.t100w;
Key
PRI  :主键 (聚簇索引)     
MUL  :辅助索引   
UNK  :唯一索引

show index from world.city;
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| city  |          0 | PRIMARY     |            1 | ID          | A         |        4188 |     NULL | NULL   |      | BTREE      |         |               |
| city  |          1 | CountryCode |            1 | CountryCode | A         |        4188 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+

3、创建索引

3.1、单列辅助索引

select * from test.t100w where k2='780P'
#优化方式: 给k2字段添加索引

语法:
alter table 表名 add index 索引名(列名);    
alter table t100w add index idx_k2(k2);

3.2、创建联合索引

语法:
alter table 表名 add index 索引名(字段1,字段2,...);
alter table t100w add index idx_k1_num(k1,num);

3.3、创建前缀索引

desc world.city;
+-------------+----------+------+-----+---------+----------------+
| Field       | Type     | Null | Key | Default | Extra          |
+-------------+----------+------+-----+---------+----------------+
| ID          | int(11)  | NO   | PRI | NULL    | auto_increment |
| Name        | char(35) | NO   |     |         |                |
| CountryCode | char(3)  | NO   | MUL |         |                |
| District    | char(20) | NO   |     |         |                |
| Population  | int(11)  | NO   |     | 0       |                |
+-------------+----------+------+-----+---------+----------------+

#需求:
需要给 city表 name列创建索引, 但是 name字段的数据类型时 char(35), 数据类型占用空间大, 枝节点就会增多, 从而索引的优化效果不会很好

#解决办法:
取 name的值前5个字符创建索引, 较少枝节点的数量, 提升优化效果

#生产中如何判断, 前缀索引的取值长度:
(1). 取 name字段去重复的行数
select count(distinct name)  from city;
(2). 取 name字段5个前缀去重复的行数
select count(distinct(left(name,5)))  from city;
(3). 用取前缀的行数 / 总行数, 不大于20%为合理范围

语法:
alter table city add 索引名(字段(前缀字符个数));
alter table city add index idx_n(name(5));

4、删除索引

语法:
alter table 表名 drop 索引名;
alter table city drop index idx_n;

注意:
创建索引, 删除索引, 属于DDL操作, 避开业务繁忙期间进行, 如果一定要再业务繁忙进行, 一定要使用 pt工具进行, 减少锁表时间

5、执行计划获取和分析

5.1、介绍

功能:
用于查看, 分析创建的索引, 编写的SQL语句合理性的分析

命令: 任选一个, 两个命令执行结果一样
explain
desc

原理:
在MySQL体系结构中, SQL层执行SQL之前会进行分析,选择合理的优化算法, 预估执行结果

5.2、使用方法

语法:
desc SQL语句;
desc select * from city where countrycode='CHN';
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | city  | NULL       | ref  | CountryCode   | CountryCode | 3       | const |  363 |   100.00 | NULL  |
+----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+

5.3、执行计划信息介绍

table         : 此次查询访问的表 ★
type          : 索引查询的类型(ALL、index、range、ref、eq_ref、const(system)、NULL)
possible_keys : 可能会应用的索引 ★
key           : 最终选择的索引
key_len       : 索引覆盖长度, 主要是用来判断联合索引应用长度
rows          : 需要扫描的行数
Extra         : 额外信息 ★

5.4、type列信息详解

以下结果按从上到下, 越下面的优化结果越好

5.4.1、ALL: 全表扫描-没有创建索引

1. 查询条件没建立索引
#district字段没有创建索引
desc select * from world.city where district='shandong';

2. 有索引不走, SQL语句问题
#例如 like '%%', '%xxx', !=, not in
desc select * from city where countrycode!='CHN';
desc select * from city where countrycode not in ('CHN','USA');
desc select * from city where countrycode like '%CH%';

5.4.2、index 全索引扫描

#index: 走索引条件查询, 但是需要扫描整个B树结构才能拿到结果, 一般是由SQL语句查询条件造成的
desc select countrycode from city;

5.4.3、range 索引范围扫描

会受到: B树额外优化, 叶子节点双向指针
mysql> desc select * from city where id<10;
mysql> desc select * from city where countrycode like 'CH%';

以下两种查询, 大几率受不到叶子节点双向指针优化
mysql> desc select * from city where countrycode in ('CHN','USA');
mysql> desc select * from city where countrycode='CHN' or countrycode='USA';

建议: 如果查询列重复值少的话, 我们建议改写为 union all 
desc
select * from city where countrycode='CHN'
union all
select * from city where countrycode='USA';

5.4.4、ref 辅助索引等值查询

desc select * from city where countrycode='CHN';

5.4.5、eq_ref : 多表连接查询中、非驱动表的连接条件是主键或唯一键时

#多表连接查询时, 最好的结果

desc select city.name,country.name 
from city 
left join country
on city.countrycode=country.code where city.population<100;

5.4.6、const(system): 主键或唯一键等值查询

select * from city where id=1;

5.4.7、NULL、结果不存在

desc select * from city where id=1000000000000000;

5.5、possible_keys列说明

代表可能会用哪些索引

5.6、key_len

5.6.1、作用

用来判断联合索引应用的部分
例如:
alter table db01.tb01 add index idx(a,b,c);
我们希望应用联合索引的部分越多越好

5.6.2、如何计算

#列的key_len长度, 按照每列的最大预留长度来做的计算
key_len=a+b+c 

create table t1 (
id int,
a int ,
b char(10),
c varchar(10))

#最大存储预留长度 (字节): 
-------------------------------------------------------------------------------
    数据类型 :        占用字节量                有not null           没有Not Null 
-------------------------------------------------------------------------------    
    数字类型 :  
    tinyint      :  1字节                    1                   1+1
    int          :  4字节                    4                   4+1
    bigint       :  8字节                    8                   8+1
-------------------------------------------------------------------------------
    字符串类型:
    utf8:
    char(10)     :  10*3字节 =30             30                  30+1
    varchar(10)  :  10*3+2字节=32            32                  32+1
-------------------------------------------------------------------------------                
    utf8mb4:
    char(10)     : 10*4字节 =40              40                  40+1
    varchar(10)  : 10*4字节+2 =42            42                  42+1
-------------------------------------------------------------------------------

#创建测试表
use test;    
create table test (
id int not null primary key auto_increment,
a  int not null ,                            # 4  
b  int ,                                     # 5
c  char(10) not null ,                       # 40
d  varchar(10),                              # 43
e  varchar(10) not null                      # 42
)engine=innodb charset=utf8mb4;

#添加联合索引
alter table test add index idx(a,b,c,d,e);

#可能出现的情况
应用5个列: a,b,c,d,e
4+5+40+43+42=134

应用4个列:a,b,c,d
4+5+40+43=92

应用3个列: a,b,c
4+5+40=49 

应用2个列: a,b
4+5=9 

应用1个列: a
4

5.6.3、验证联合索引-key_len

mysql> desc select * from test where a=10 and b=10 and  c='a' and d='a' and e='a';
mysql> desc select * from test where a=10 and b=10 and  c='a' and d='a';
mysql> desc select * from test where a=10 and b=10 and  c='a';
mysql> desc select * from test where a=10 and b=10;

5.7、联合索引细节

5.7.1、条件

联合索引应用要满足最左原则
1. 建立联合索引时, 选择重复值最少的列作为最左列, 因为索引排序会按照从左到右, 然后创建叶子节点
2. 使用联合索引时, 查询条件中, 必须包含最左列, 才有可能应用到联合索引, 否则是全索引扫描 (type: index)

5.7.2、联合索引不同覆盖场景

ALTER TABLE test.t100w ADD INDEX idx(num,k1,k2);
SHOW INDEX FROM test.t100w;

Column_name
num :  5
k1  :  9
k2  :  17

5.7.2.1、完全覆盖

#最左选择:
创建联合索引时, 靠左边的选择唯一值多的字段 

desc select * from t100w where num=913759  and k1='ej' and k2='EFfg';
desc select * from t100w where k1='ej' and k2='EFfg' and  num=913759;
#优化器会自动按照创建索引的顺寻, 对where条件的字段进行排序, 根据最左原则排序, 所以where条件的字段不用完全跟索引顺序完全一致
说明:
index idx (a,b,c);
where a = and b = and c =;  
where b = and c = and a =;

#非等值的条件, 创建索引时放在最后一位, 也会走全列索引, 回表次数少
desc select * from t100w where num=913759  and k1='ej' and k2 in('EFfg','abcd');
desc select * from t100w where num=913759  and k1='ej' and k2 like 'EF%'

5.7.2.2、部分覆盖

index idx (a,b,c) 

#只查询部分内容
where a = and b = 
where b = and a = 
where a =

#创建索引时, 非等值查询的字段没有在最后
where a =  and   b> < >= <= in like between and   and  c=

如何优化? 使它走全列索引 , 调整索引最左原则的顺序
index idx (num,k1,k2)
顺序改为:
index idx (num,k2,k1)

desc select * from t100w where num=913759  and k2='EFfg' and k1>'zz' ;

5.7.2.3、完全不覆盖

index idx (a,b,c) 
where  b  c  
where  b   
where  c

#没有最左条件

5.8、extra 额外的信息

xtra 列如果结果显示: using filesort
#代表以下针对语句, 排序规则没有运用到索引, 因为创建索引时, 生成叶子之前是进行排序操作的
group by \ order by \ distinct \ union all 

desc select * from world.city where countrycode='CHN' order by population;
ExtraUsing 
index condition; Using file

#优化方案: 针对where 条件 + 排序子句时, 一定要创建联合索引, 给2列创建单列索引, 依然不会生效
底层原理: 根据where字段进行和排序字段一起排序, 而不是单独排序

#验证
1.分别给2列创建单例索引
desc select * from world.city where countrycode='CHN' order by population;
#显示排序没有走索引
ExtraUsing 
index condition; Using file

2.创建联合索引
alter table world.city add index idx_cp(countrycode,population);
desc select * from world.city where countrycode='CHN' order by population;
#显示走索引
ExtraUsing 
Using index condition

6、应用场景

数据库慢:
1. 应急性慢, 以前一致稳定, 突然一下很慢
排查方法:
show full processlist; 找出慢语句, 使用 explain分析SQL语句, 优化索引、改写语句

2. 间歇性慢
slowlog 找出慢语句, 使用 explain 分析SQL语句, 优化索引、改写语句