索引原理与慢查询优化以及执行计划分析

  1. 索引的分类(算法)

B树 默认使用的索引类型

R树

Hash

FullText

GIS 索

建索引是为了加速查询,索引最好建立在区分度高的字段上

在表中有大量数据的情况下创建索引会很慢,因为要扫描所有的数据

索引建好后,查询速度会大幅度加快,但是写入速度会变慢

使用B+树索引时,对占用空间小的字段建索引,单个磁盘块就可以存储更多数据,树的高度会更低,查询时经历更少的IO次数

Btree索引功能上的分类

4.2 聚集索引

(1)MySQL 会自动选择主键作为聚集索引列,没有主键会选择唯一键,如果都没有会生成隐藏的.

(2)MySQL进行存储数据时,会按照聚集索引列值的顺序,有序存储数据行

(3)聚集索引直接将原表数据页,作为叶子节点,然后提取聚集索引列向上生成枝和根

聚集索引(primary key)的叶子节点存放的是整条记录

由于定义了数据的逻辑顺序,聚集索引能够特别快地访问针对范围值的查询。

InnoDB存储引擎表示索引组织表,即表中数据按照主键顺序存放。

而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子结点存放的即为整张表的行记录数据,也将聚集索引的叶子结点称为数据页。聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。

聚集索引的好处之一:它对主键的排序查找?和范围查找速度非常快,叶子节点的数据就是用户所要查询的数据。如用户需要查找一张表,查询最后的10位用户信息,由于B+树索引是双向链表,所以用户可以快速找到最后一个数据页,并取出10条记录

聚集索引的好处之二:范围查询(range query),即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可

4.3 聚集索引和辅助索引的区别

(1) 表中任何一个列都可以创建辅助索引,在你有需要的时候,只要名字不同即可

(2) 在一张表中,聚集索引只能有一个,一般是主键.

(3) 辅助索引,叶子节点只存储索引列的有序值+聚集索引列值.

(4) 聚集索引,叶子节点存储的是有序的整行数据.

(5) MySQL 的表数据存储是聚集索引组织表

辅助索引(unique,index)的叶子节点存放的是 {索引字段的值: 索引字段所在记录主键的值}

覆盖索引即从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。

4.1 辅助索引

(1) 提取索引列的所有值,进行排序

(2) 将排好序的值,均匀的存放在叶子节点,进一步生成枝节点和根节点

(3) 在叶子节点中的值,都会对应存储主键ID

查询索引

desc city;

PRI ==> 主键索引

MUL ==> 辅助索引

UNI ==> 唯一索引

show index from table_name\G

创建索引

alter table t1 add primary key(id); #加主键

create [unique][cluster] index index _name on 表名(字段名);

alter table city add unique index uidx_dis(district); #唯一索引

select count(distinct district) from city; #distinct 去除重复值

前缀索引只能在字符串列上建,不能在数字列上建

mysql> alter table city add index idx_dis(district(5));#取district字段的前面5个字符

drop index index _name on 表名;

alter table city drop index idx_name;

联合索引

create [unique][cluster] index index _name on 表名(字段名1,字段名2,字段名3,.....);

尽量将区分度高的字段放在前面,将范围查询放在最后

联合索引:

-PRIMARY KEY(id,name):联合主键索引

-UNIQUE(id,name):联合唯一索引

-INDEX(id,name):联合普通索引

hash类型的索引:查询单条快,范围查询慢

btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)

不同的存储引擎支持的索引类型也不一样

InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;

索引的最左匹配特性

8.1 未做优化之前测试

测试没有登录到mysql里面去

mysqlslap --defaults-file=/etc/my.cnf \

--concurrency=100 --iterations=1 --create-schema='db1' \

--query="select * from db1.t100w where k2='MN89'" engine=innodb \

--number-of-queries=2000 -uroot -poldboy123 -verbose

100个用户,2000次查询,test是库名,t100w 是表名,k2是一个字段

2.3 possible_keys:可能会用到的索引

2.4 key: 真正选择了哪个索引

2.5 key_len 索引覆盖长度(索引列占用的字节数)

有一个额外的字节来标识是否为空,varchar类型还需要额外的两个字节来表示开始和结束

即 key_len(varchar)=字符个数*字节数+1bytes(标识是否为空)+2bytes(表示开始和结束)

如果索引列已经被标识为not null ,就不需要1bytes来标识

一个字符占用的字节数取决于编码方式utf8mb为4字节,utf8为3字节

varchar(20) utf8mb4

  1. 能存20个任意字符

  2. 不管存储的是字母,数字,中文,1个字符的最大预留长度都是4个字节

  3. 1个中文字符占4个字节

  4. ,1个数字字符或字母实际占用1个字节

select length(列名) from test;

已经建立了索引后,更改表

  1. 执行计划分析

9.1 作用

将优化器 选择后的执行计划 截取出来.便于管理判断语句的执行效率.

9.2 获取执行计划

desc SQL语句

explain SQL 语句

mysql>

mysql> desc select * from test.t100w where k2='MN89';

+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |

+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+

| 1 | SIMPLE | t100w | NULL | ALL | NULL | NULL | NULL | NULL | 1027638 | 10.00 | Using where |

+----+-------------+-------+------------+------+---------------+------+---------+------+---------+----------+-------------+

9.3 分析执行计划

9.3.1 table

表名

9.3.2 type

查询的类型:

全表扫描 : ALL

索引扫描 : index,range,ref,eq_ref,const(system),NULL

index: 全索引扫描

mysql> desc select countrycode from city;

range: 索引范围扫描(> < >= <= , between and ,or,in,like )

mysql> desc select * from city where id>2000;

mysql> desc select * from city where countrycode like 'CH%';

对于辅助索引来讲,!= 和not in等语句是不走索引的

对于主键索引列来讲,!= 和not in等语句是走range

===

mysql> desc select * from city where countrycode='CHN' or countrycode='USA';

mysql> desc select * from city where countrycode in ('CHN','USA');

一般改写为 union all

desc

select * from city where countrycode='CHN'

union all

select * from city where countrycode='USA';

ref: 辅助索引等值查询

desc

select * from city where countrycode='CHN'

union all

select * from city where countrycode='USA';

eq_ref : 多表连接时,子表使用主键列或唯一列作为连接条件

A join B

on A.x = B.y

desc select b.name,a.name ,a.population

from city as a

join country as b

on a.countrycode=b.code

where a.population<100;

const(system) : 主键或者唯一键的等值查询

mysql> desc select * from city where id=100;

5.1 建立索引的原则(DBA运维规范)

(1) 建表必须要有主键,一般是无关列,自增长

(2) 经常做为where条件列 order by group by join on, distinct 的条件

(3) 最好使用唯一值多的列作为联合索引前导列,其他的按照联合索引优化细节来做

(4) 列值长度较长的索引列,我们建议使用前缀索引.

(5) 降低索引条目,一方面不要创建没用索引,不常使用的索引清理,percona toolkit(xxxxx)

(6) 索引维护要避开业务繁忙期

(7) 小表不建索引

5.2没有用到索引的情况(开发规范)

(1) 没有查询条件,或者查询条件没有建立索引

select * from city;

select * from city where 1=1;

(2) 查询结果集是原表中的大部分数据,应该是25%以上。

(3) 索引本身失效,统计数据不真实

面试题:同一个语句突然变慢?

统计信息过旧,导致的索引失效

(4) 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等)

mysql> desc select * from city where id-99=1;

(5) 隐式转换导致索引失效.

举例:telphone_number字段为char类型,select * from city where telphone_number=123;

123没有加引号也能查到结果,因为系统自己使用函数把数字类型转换成了字符类型

(6) <> ,not in 不走索引(辅助索引)

(7) like "%aa" 百分号在最前面不走

(8) 联合索引

上一篇:读懂才会用:Redis ZSet 的几种使用场景


下一篇:DBA:这有一份对接 NBU 备份故障排除指南,请查收!