mysql-索引及执行计划

一: 索引作用:

提供类似书中目录的作用,目的是为了优化查询

二:索引 的种类:

B树索引

Hash索引

R树

full text

GIS


三: B树基于不同的查找算法分类介绍


mysql-索引及执行计划_第1张图片

'''

B-tree:

B+tree 在范围查询方面提供了更好的性能(> < >= <=)

B*Tree

'''


四: 在功能上的分类

44.1 辅助索引(S)怎么构建B树结构的?

(1). 索引是基于表中,列(索引键)的值生成的B树结构

(2). 首先提取此列所有的值,进行自动排序

(3). 将排好序的值,均匀的分布到索引树的叶子节点中(16K)

(4). 然后生成此索引键值所对应得后端数据页的指针

(5). 生成枝节点和根节点,根据数据量级和索引键长度,生成合适的索引树高度

id  name  age  gender

select  *  from  t1 where id=10;

问题: 基于索引键做where查询,对于id列是顺序IO,但是对于其他列的查询,可能是随机IO.

alter table t1 add index idx(id)

4.2 聚集索引(C)

4.2.1 前提

(1)表中设置了主键,主键列就会自动被作为聚集索引.

(2)如果没有主键,会选择唯一键作为聚集索引.

(3)聚集索引必须在建表时才有意义,一般是表的无关列(ID)

4.2.2 辅助索引(S)怎么构建B树结构的?

(1) 在建表时,设置了主键列(ID)

(2) 在将来录入数据时,就会按照ID列的顺序存储到磁盘上.(我们又称之为聚集索引组织表)

(3) 将排好序的整行数据,生成叶子节点.可以理解为,磁盘的数据页就是叶子节点

4.2.3 聚集索引和辅助索引构成区别

聚集索引只能有一个,非空唯一,一般是主键

辅助索引,可以有多个,时配合聚集索引使用的

聚集索引叶子节点,就是磁盘的数据行存储的数据页

MySQL是根据聚集索引,组织存储数据,数据存储时就是按照聚集索引的顺序进行存储数据

辅助索引,只会提取索引键值,进行自动排序生成B树结构

mysql-索引及执行计划_第2张图片

五:辅助索引细分:

1:普通的单列辅助索引

2:联合索引: 多个列作为索引条件,生成索引树,理论上设计是好的,可以减少大量额回表查询

3:唯一索引: 索引的值都是唯一的。

六:关于索引树的高度受什么影响:

1:数据量级,解决方法:分表,分库,分布式

2:索引列值过长:解决方法; 前缀索引

3:数据类型: 变长长度字符串,使用了char, 解决方案:变长字符串使用varchar

enum类型的使用enum(’山东‘,’河北‘.....)

七:索引的基本管理

7.1 索引建立前

db01 [world]>desc 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      |                |

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

5 rows in set (0.00 sec)

Field :列名字

key  :有没有索引,索引类型

PRI: 主键索引

UNI: 唯一索引

MUL: 辅助索引(单列,联和,前缀)

7.1 单列普通辅助索引

7.1.1 创建索引

alter table city add index idx_name(name);

create index idx_name1 oncity(name);

show indexfromcity;

alter table city drop index idx_name1; 删除索引

7.2 覆盖索引(联合索引)

lter table city add index  idx_co_po(countrycode,population);

7.3 前缀索引:

alter table city add index idx_di(district(5));

注意:数字列不能用作前缀索引。

7.4 唯一索引

db01 [world]>alter table city add unique index idx_uni1(name);

ERROR 1062 (23000): Duplicate entry 'San Jose' for key 'idx_uni1'

selectdistrict,count(id)fromcitygroupby district;

需求:找到world下,city表中 name列有重复值的行,最后删掉重复的行

db01[world]>selectname,count(id)ascidfromcitygroupby name having cid>1order by cid desc;

db01[world]>select*fromcitywherename='suzhou';


通过存储过程 创建一张100w数据量的表(mysql函数):

use school;

create table t_100w(id int,num int, k1 char(2),k2 char(4),dt TIMESTAMP);

create procedure rand_data(in num int)

BEGIN

declare str char(62) default 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';

declare str2 char(2);

declare str4 char(4);

declare i int default 0;

while i< num do

set

str2=concat(substring(str,1+floor(rand()*61),1),substring(str,1+floor(rand()*61),1));

set

str4=concat(substring(str,1+floor(rand()*61),1),substring(str,1+floor(rand()*61),2));

set i=i+1;

insert into t_100w values(i, floor(rand()*num),str2,str4,NOW());

end while;

end;

call rand_data(1000000);    

8:执行计划及获取

8.0 介绍:

(1)获取到的是优化器选择完成的,他认为代价最小的执行计划.作用:语句执行前,先看执行计划信息,可以有效的防止性能较差的语句带来的性能问题.如果业务中出现了慢语句,我们也需要借助此命令进行语句的评估,分析优化方案。

(2)select获取数据的方法

1.全表扫描(应当尽量避免,因为性能低)

2.索引扫描

3.获取不到数据

8.1 执行计划获取

获取优化器选择后的执行计划

desc select * from t_100w WHERE k2='780p';

explain select * from t_100w WHERE k2='780p';

两种获取方式都可以

结果:

mysql-索引及执行计划_第3张图片

主要关注点:

table: 查询的表

type: 查询的类型:全表, 索引

possible_keys: 可能会用到的索引

key: 使用到的索引

key_len: 应用索引的长度

rows: 查询结果集的长度

extra:  额外的信息

8.1.1: type 查询的类型:全表, 索引

ALL: 全表扫描,不走索引

有哪些情况:

在 where 语句后面查询 <>(不等于) , not  in, like '%..'  在辅助索引中不走索引, 在聚集索引列还是会走索引

1:index: 全索引扫描

    1:查询需要获取整个索引树种的值时:

        desc select countcode from city;

       2:联合索引中:任何一个非最左列作为查询条件时:

        idx_a_b_c(a, b,c)  ---> a, ab, abc

        select * from t1 where b

         use world;

        desc city;

        alter table city add index idx_c_p(countrycode, population);

        desc select * FROM city where countrycode="CHN"

2: range: 索引范围扫描 

辅助索引:> < >= <= like in or

主键索引: not in

1: desc select * from city where id<5;

3: ref: 非唯一性索引, 等值查询

desc select * from city where countrycode='chn'

4: eq_ref: 在多表连接时, 连接条件使用了唯一索引(uk, pk)

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

5:system, const  唯一索引的等值查询

desc select * from city where id=10;

索引性能递增

8.2.2 extra:  filesort, 文件排序

desc select * from city where countrycode='chn' order by population;

ALTER TABLE city ADD INDEX idx_c_p(countrycode,population);

desc select * from city where countrycode='chn' order by population; 

结论:
 1.当我们看到执行计划extra位置出现filesort,说明由文件排序出现
 2.观察需要排序(ORDER BY,GROUP BY ,DISTINCT )的条件,有没有索引
3. 根据子句的执行顺序,去创建联合索引


8.2.3 explain(desc)使用场景(面试题)

题目意思:  我们公司业务慢,请你从数据库的角度分析原因

1.mysql出现性能问题,我总结有两种情况:

(1)应急性的慢:突然夯住

应急情况:数据库hang(卡了,资源耗尽)

处理过程:

1.show processlist;  获取到导致数据库hang的语句

2. explain 分析SQL的执行计划,有没有走索引,索引的类型情况

3. 建索引,改语句

(2)一段时间慢(持续性的):

(1)记录慢日志slowlog,分析slowlog

(2)explain 分析SQL的执行计划,有没有走索引,索引的类型情况

(3)建索引,改语句

9. 索引应用规范

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

9.1.0 说明

为了使索引的使用效率更高,在创建索引时,必须考虑在哪些字段上创建索引和创建什么类型的索引。那么索引设计原则又是怎样的?

9.1.1 (必须的) 建表时一定要有主键,一般是个无关列

9.1.2 选择唯一性索引

唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。

例如,学生表中学号是具有唯一性的字段。为该字段建立唯一性索引可以很快的确定某个学生的信息。

如果使用姓名的话,可能存在同名现象,从而降低查询速度。

9.1.3(必须的) 为经常需要where 、ORDER BY、GROUP BY,join on等操作的字段

为其建立索引,优化查询

注:如果经常作为条件的列,重复值特别多,可以建立联合索引

9.1.4 尽量使用前缀来索引:

如果索引字段的值很长,最好使用值的前缀来索引。

9.1.5 限制索引的数目

索引的数目不是越多越好。

可能会产生的问题:

(1) 每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。

(2) 修改表时,对索引的重构和更新很麻烦。越多的索引,会使更新表变得很浪费时间。

(3) 优化器的负担会很重,有可能会影响到优化器的选择.

percona-toolkit中有个工具,专门分析索引是否有用

9.1.6 删除不再使用或者很少使用的索引(percona toolkit)

pt-duplicate-key-checker

表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理

员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。

9.1.7 大表加索引,要在业务不繁忙期间操作

9.1.8 尽量少在经常更新值的列上建索引

9.1.9 建索引原则

(1)必须要有主键,如果没有可以做为主键条件的列,创建无关列
(2)经常做为where条件列 order bygroupbyjoinon,distinct 的条件(业务:产品功能+用户行为)
(3)最好使用唯一值多的列作为索引,如果索引列重复值较多,可以考虑使用联合索引
(4)列值长度较长的索引列,我们建议使用前缀索引.
(5)降低索引条目,一方面不要创建没用索引,不常使用的索引清理,perconatoolkit(xxxxx)
(6)索引维护要避开业务繁忙期

9.2 不走索引的情况(开发规范)

9.2.1 没有查询条件,或者查询条件没有建立索引

select * from tab;      全表扫描。

select  * from tab where 1=1;

在业务数据库中,特别是数据量比较大的表。

是没有全表扫描这种需求。

1、对用户查看是非常痛苦的。

2、对服务器来讲毁灭性的。

(1)

select * from tab;

SQL改写成以下语句:

select  * from  tab  order by  price  limit 10 ;    需要在price列上建立索引

(2)

select  * from  tab where name='zhangsan'          name列没有索引

改:

1、换成有索引的列作为查询条件

2、将name列建立索引

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

查询的结果集,超过了总数行数25%,优化器觉得就没有必要走索引了。

假如:tab表 id,name    id:1-100w  ,id列有(辅助)索引

select * from tab  where id>500000;

如果业务允许,可以使用limit控制。

怎么改写 ?

结合业务判断,有没有更好的方式。如果没有更好的改写方案

尽量不要在mysql存放这个数据了。放到redis里面。

9.2.3 索引本身失效,统计数据不真实

索引有自我维护的能力。

对于表内容变化比较频繁的情况下,有可能会出现索引失效。

一般是删除重建

现象:

有一条select语句平常查询时很快,突然有一天很慢,会是什么原因

select?  --->索引失效,,统计数据不真实

DML ?  --->锁冲突

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

例子:

错误的例子:select * from test where id-1=9;

正确的例子:select * from test where id=10;

算术运算

函数运算

子查询

9.2.5 隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.

这样会导致索引失效. 错误的例子:

mysql> alter table tab add index inx_tel(telnum);

Query OK, 0 rows affected (0.03 sec)

Records: 0  Duplicates: 0  Warnings: 0

mysql>

mysql> desc tab;

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

| Field  | Type        | Null | Key | Default | Extra |

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

| id    | int(11)    | YES  |    | NULL    |      |

| name  | varchar(20) | YES  |    | NULL    |      |

| telnum | varchar(20) | YES  | MUL | NULL    |      |

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

3 rows in set (0.01 sec)

mysql> select * from tab where telnum='1333333';

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

| id  | name | telnum  |

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

|    1 | a    | 1333333 |

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

1 row in set (0.00 sec)

mysql> select * from tab where telnum=1333333;

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

| id  | name | telnum  |

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

|    1 | a    | 1333333 |

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

1 row in set (0.00 sec)

mysql> explain  select * from tab where telnum='1333333';

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

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

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

|  1 | SIMPLE      | tab  | ref  | inx_tel      | inx_tel | 63      | const |    1 | Using index condition |

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

1 row in set (0.00 sec)

mysql> explain  select * from tab where telnum=1333333;

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

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

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

|  1 | SIMPLE      | tab  | ALL  | inx_tel      | NULL | NULL    | NULL |    2 | Using where |

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

1 row in set (0.00 sec)

mysql> explain  select * from tab where telnum=1555555;

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

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

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

|  1 | SIMPLE      | tab  | ALL  | inx_tel      | NULL | NULL    | NULL |    2 | Using where |

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

1 row in set (0.00 sec)

mysql> explain  select * from tab where telnum='1555555';

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

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

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

|  1 | SIMPLE      | tab  | ref  | inx_tel      | inx_tel | 63      | const |    1 | Using index condition |

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

1 row in set (0.00 sec)

mysql>

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

EXPLAIN  SELECT * FROM teltab WHERE telnum  <> '110';

EXPLAIN  SELECT * FROM teltab WHERE telnum  NOT IN ('110','119');

mysql> select * from tab where telnum <> '1555555';

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

| id  | name | telnum  |

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

|    1 | a    | 1333333 |

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

1 row in set (0.00 sec)

mysql> explain select * from tab where telnum <> '1555555';

单独的>,<,in 有可能走,也有可能不走,和结果集有关,尽量结合业务添加limit

or或in  尽量改成union

EXPLAIN  SELECT * FROM teltab WHERE telnum  IN ('110','119');

改写成:

EXPLAIN SELECT * FROM teltab WHERE telnum='110'

UNION ALL

SELECT * FROM teltab WHERE telnum='119'

9.2.7 like "%_" 百分号在最前面不走

EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'  走range索引扫描

EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110'  不走索引

%linux%类的搜索需求,可以使用elasticsearch+mongodb 专门做搜索服务的数据库产品

你可能感兴趣的:(mysql-索引及执行计划)