MySQL数据库--索引优化分析

索引优化分析

性能下降(执行时间长,等待时间长)

查询语句写的烂

索引失效

	create index idx_user_name on user(name)  做索引之前是杂乱无章的,做之后是有顺序的

	单值索引   某表的某个字段做的索引

	create index idx_user_nameEmail on user(name,email)

	复合索引   某表的多个字段做的索引

关联查询有太多的join (设计缺陷或不得已的需求)

服务器调优及各个参数的配置(缓冲,线程数等)	

常见的join查询

sql的执行顺序

手写:

机读 : from..on..   ..join..  where... group by.... having....select distinct ... order by ... limit....

什么是索引

索引的本质是一种数据结构,帮助mysql高效获取数据

索引的目的在于提高查询效率

排好序的快速查找数据结构,索引会影响where后面的查找,和order by 后面的排序的两大功能

在数据之外,数据库系统还维护着特定的查找算法的数据结构,这种数据结构以某种方式指向数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引

维护一个二叉查找树

数据删除没有真正delete,只是修改为未激活状态。尽量不删,为了数据分析,第二 为了索引。

数据频繁修改,索引需要重建。

数据修改不仅要改数据本身,而且要修改索引的指向,所以会慢

一般来说,索引本身也很大,不可能全部存储在内存中,因此索引通常以文件形式存储在磁盘上。
优点

提高了检索的效率,降低了io成本

对数据排序,降低了数据排序成本,降低了运算的CPU损耗

缺点

索引也是一张表,保存了主键与索引字段,并指向实体表的记录,索引列也是占空间的

会降低更新表的速度

若mysql有大数据量的表,就需要花时间建立最优秀的索引,索引不是一朝一夕的建立,需要根据业务不断变更。

索引分类

一张表建的索引最多不要超过五个
单值索引
一个索引只包含单个列,一个表可以有多个单列索引
唯一索引
索引列的值必须唯一,可以允许有空值

复合索引
一个索引包含多个列
创建索引
create [unique] index 索引名 on  表名(字段,字段)

alter 表名 add [unique] index 索引名 on  (字段,字段) 
删除索引
drop index 索引名 on mytable;
查看索引
show index from 表名

mysql索引结构

BTree索引

初始化介绍:

一颗B+树,一个磁盘块可以有集合数据项和指针

真实的数据存在于叶子节点

非叶子节点值不存储真实 的数据,值存储指引搜索方向的数据项

查找过程:

若想查询28,则先把磁盘一加载到内存,发生一次io,在内粗中二分查找确定数据位置,锁定指针(内存时间很短)

磁盘一的指针所指向的磁盘地址吧磁盘3由磁盘加载到内存中,发生第二次io,二分查找并锁定指针。

磁盘三的指针所指向的此岸加载到内存,发生第三次io,同时二分查找

结束查询,3次io

真实情况:3层的B+树,可以表示上百万的数据

适合的情况建立索引

  1. 主键字段建立唯一索引
  2. 频繁作为查询条件的字段应该建立索引
  3. 查询中与其他表关联的字段,外建关系自动建立索引
  4. 频繁更新的字段不适合建立索引
  5. where条件里用不到的字段不要建立索引
  6. 在高并发情况下,尽量倾向于创建组合索引
  7. 查询中排序的字段,符合索引的顺序尽量和order by的顺序一致
  8. 查询中统计或分组字段

不适合的情况建立索引

  1. 表记录太少(三百万左右性能开始下降)
  2. 经常增删改的表
  3. 数据重复且分布平均的表字段,若数据列包含太多的重复内容,为他建立索引没有太大的实际效果(数据差异率不高)
  4. 索引的选择性:字段下的不同值/字段下的所有值 这个数值越接近1效率越高

性能分析

mysql query optimizer

1.负责优化select 语句的优化器,通过分析统计信息,为客户端提供他认为的最优的执行计划

2.首先对整条query进行优化,处理一些常量表达式的预算转化为常量,并对查询条件进行简化和转换,去除一些显著且无用的条件,结构调整等

mysql常见瓶颈
  1. cpu: CPU饱和时一般发生在数据装入内存或从磁盘上读取数据的时候
  2. io:磁盘i/o瓶颈一般在装入数据远大于内存容量时
  3. 服务器硬件

explain

使用explain关键字可以模拟优化器查询语句,从而知道mysql时如何处理你的sql语句。

使用方法

explain select* from books;

能干什么

表的读取顺序

数据读取操作的操作类型

哪些索引可以使用

哪些索引被实际使用

表之间的引用

每张表多少行被优化器查询

怎么使用

explain + sql

执行计划包含的信息:

id select_type table type possible_keys key key_len ref rows extra

id

select查询的序列号,包含一组数字,表示查询select 子句或操作表的顺序

三种情况:

id相同,执行顺序从上到下

id不同,如果是子查询,id序号会递增,id越大优先级越高,越先执行

id相同不同,同时存在  数字大先执行,平级顺序执行  

	derived[id] 衍生表
select type

常见值:

  • simple 普通查询,查询中不包含子查询或union
  • primary 主查询,查询中包含任何复杂的子部分,最外层被标记为(最后加载)
  • subquery 子查询,在select或where列表中包含了子查询
  • derived 衍生查询,在from列表中包含的子查询被标记为derived,mysqk会递归执行这些子查询,结———————果放在临时表
  • union 联合,若第二个select出现在union之后,则被标记为union,若union包含在from子句的子查———————询找那个,最外层select被标记为:derived
  • union result 联合结果, 从union表中获取结果的select

查询的类型,主要是用于区别不同查询,联合查询,子查询等复杂查询

table

显示着一行的数据时关于哪张表的

type

访问类型排序

从最好到最差依次是

system const re_ref ref range index all

若type出all,且数据量百万之上,请一定优化,表名检索全部扫描

一般来说,得保证查询至少达到range级别,最好到ef。

  • system :表只有一行数据,等于系统表,const类型的特例,平时不会出现
  • const: 表示通过索引一次就找到了,const用于比较primary 或unique索引,因为只匹配一行,所以很快,若将主键放在where,mysql可以将该查询转化为一个常量
  • eq_ref: 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见primary 或unique索引
  • ref: 非唯一性索引扫描,返回匹配某个但单独值的所有行,可能会找到多个符合条件的行,所有他应该属于查找和扫描的混合体, 如复合索引查询时按单独索引为条件
  • range:只检索给定范围的行,使用一个索引来选择行。一般使用between ,in < > 等,比全表扫描要好,因为他只需要开始于索引一点,而结束于另一点,不会扫描全部索引。
  • index: 只遍历索引树,比all快,因为索引文件比数据文件小。index从索引中读取,all从硬盘中读取
  • all:全表遍历

邮件 红色圈圈标出

这条sql跑了一下explqin分析,会出现全表扫描的情况

经过优化:修改为 效果变成。

possible_keys

显示可能应用在这种表的索引,一个或多个

显示涉及到字段上若存在索引,则该索引将被列出,但不一样被使用

key

实际使用的索引,若为null则没有使用索引或索引失效

查询中若使用了覆盖索引,则该索引仅出现在key列表中

覆盖索引:就是select查询的字段和建的符合索引的个数和顺序吻合。

理论上没用到,实际上也没有。

理论上没用到,实际上用到了。覆盖索引…

理论上应该用,实际是没用到。两个表连接时…

key_len

表示索引使用的字节数,在不损失精度的情况下,长度约断越好,值为索引字段最大可能长度,而不是使用长度,计算得出的。

同样查询结果条件下,精度约小越好

ref

显示索引的那一列被使用了,若是一个常数,哪些列表和常量则被用于查询索引列上的值

rows

根据表统计信息及索引选用情况,大致法估算出找到所需的记录所需要读取的行数,越少越好

Extra

包含不适合在其他列但是又非常重要的信息

  • using filesort 说明mysql会对数据使用一个个外部的索引排序,而不是按照表内的索引顺序进行读取,mysql无法利用索引字段完成的排序称为:文件内排序。比较危险!如果可以,尽快优化
    • 【索引为idx_clo1_clo2_clo3】(where cl01=“a” order by clo3)改为(where cl01=“a” order by clo2,clo3)
  • using temporary 使用了临时表保存了中间结果,mysql在对查询结果排序时使用临时表,常见于order by或group by. 更加危险!
    • 【索引为idx_clo1_clo2】(where clo1 in (…) group y clo2) 修改为 (where clo1 in (…) group y clo1,clo2)
  • using index: 表示相应的select操作中使用了覆盖索引,避免了访问表的数据行,效率不错,若有user where 表示索引用来执行键值的查找,没有则表示索引用来读取数据
    • 【索引为idx_clo1_clo2】
    • (select clo2 from test where clo = a)
    • (select clo1,clo2 from test)
    • 覆盖索引:
      • 就是select 的数据列只用从索引中就能取得,不必读取数据行,mysql可以利用索引返回select列表找那个的字段,而不必根据索引再次读取数据文件,查询列要被所建的索引覆盖
      • 若使用覆盖索引,不要使用select * 一定要只取出所需要的列,但若将索引字段一起做索引,会导致索引文件偏大而导致性能下降
  • using where;使用了where
  • using join buffer;使用了连接缓存
  • impossible where:where老是找不到,一值为false

索引优化分析

1.单表分析
create TABLE if not EXISTS article(
id int(10) UNSIGNED not null primary key auto_increment,
author_id int(10) UNSIGNED not null,
category_id int(10) UNSIGNED not null,
views int(10) UNSIGNED not null,
comments int(10) UNSIGNED not null,
title VARCHAR(255) not null,
content text not null
);

insert into article(author_id,category_id,views,comments,title,content) VALUES(1,1,1,1,'1','1'),(2,2,2,2,'2','2'),(3,1,1,3,'3','3');

​ #查询category_id=1 且comments 大于1的情况下,views最多的author_id

​ select author_id from article where category_id=1 and comments > 1 order by views desc limit 1;

​ 建立索引,这里comments为范围,若建立ccv索引,会导致comments及以后字段索引失效,故隔过comments

​ create index idx_article_cv on article(category_id,views);

2.两表优化
create table if not exists class(
id int UNSIGNED primary key not null auto_increment,
card int UNSIGNED not null);

create table if not exists book(
bookid int UNSIGNED primary key not null auto_increment,
card int UNSIGNED not null);

-- 随机插入数据
insert into book(card) values(floor(1+(rand()*20)));
insert into class(card) values(floor(1+(rand()*20)));


-- 索引应该是加在左表还是右表
select * from class left join book on class.card = book.card;
-- 左连接相反加,右连接也是相反加
-- 左连接特性,左边全有,则重点就是在右边搜索不满足条件的行。故需要在右边添加索引

create index idx_book_card on book(card);
explain select * from class left join book on class.card = book.card;
3.三表优化
create table if not exists phone(
phoneid int unsigned not null primary key auto_increment,
card int UNSIGNED not null
);

insert into phone(card) values(floor(1+(rand()*20)));

drop index idx_book_card on book;

select * from class left join book

explain select * from class LEFT join book on book.card = class.card left join phone on phone.card =  book.card;
create index idx_book_card on book(card);
create index idx_phone_card on phone(card);
4.join语句的优化总结

尽可能减少join语句中nestedloop的循环总次数,永远用小结果集驱动大结果集

优先优化nestedloop的内层循环

保证join语句中被驱动表上join条件字段已经被索引

当无法保证被签到表的join条件字段被索引且内存资源充足的前提下,不要太吝惜joinbuffer的设置

索引失效(应该避免)

建表sql
create table staffs(
id int primary key auto_increment,
name varchar(24) not null default '' comment "姓名",
age int not null DEFAULT 0 comment'年龄',
pos varchar(20) not null default '' comment "职位",
add_time TIMESTAMP not null default current_timestamp comment "入职时间"
) 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('july',23,'dev',now());
insert into staffs(name,age,pos,add_time) VALUES('2000',23,'dev',now());
select * from staffs;

create index idx_staffs_nameAgePos on staffs(name,age,pos);

1.全值匹配我最爱
2.最佳左前缀法则

​ 如果索引了多列,要遵循此法则,查询从索引的最左前列开始并且不跳过索引中的列。

​ 带头大哥不能死,中间兄弟不能断。

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

​ 索引列上不计算

4.存储引擎不能使用索引中范围条件右边的列

​ 范围之后全失效

5.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
6.mysql在使用不等于的时候无法使用索引而导致全表扫描
7.is null, is not null也无法使用索引
8.like以通配符开头(‘%abc’)导致无法使用索引而导致全表扫描

​ like%加右边

​ 若非使用%%,则使用覆盖索引。

9.字符串不见单引号索引失效

​ 字符串里有引号

​ mysql会对不添加单引号的数字进行隐式转换,违反第三条规则

10.少用or,用它来连接时会索引失效

索引面试题分析

建表语句
create table test03(
id int primary key not null auto_increment,
c1 char(10),
c2 char(10),
c3 char(10),
c4 char(10),
c5 char(10));

insert into test03(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5');
insert into test03(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5');
insert into test03(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5');
insert into test03(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5');
insert into test03(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5');

order by c3 c2;会出现文件内排序

但是 where c2 = ‘1’ order by c3 c2,不会出现因为这里c2为常量值则不参与排序了

group by表面上是分组,但是分组之前必排序,和order by排序的法则和索引优化原则是一致的

group by c3,c2 会导致临时文件产生,还有文件内排序

建议

​ 对于单键索引,尽量选择针对当前query过滤性更好的索引

​ 在选择组合索引的时候,当前query中过滤性最好的字段在索引字段顺序中,位置越靠前越好

​ 在选择组合索引的时候,尽量选择可以能够包含当前query中的where字句中更多的字段的索引

​ 尽可能通过分析统计信息和调整query的写法来达到选择合适索引的目的

你可能感兴趣的:(数据库)