MySQL索引优化分析

为何你写的sql查询慢?为何你建的索引常失效?经过本章内容,你将学会MySQL性能降低的缘由,索引的简介,索引建立的原则,explain命令的使用,以及explain输出字段的意义。助你了解索引,分析索引,使用索引,从而写出更高性能的sql语句。还在等啥子?卷起袖子就是干!html

 

 

咱们先简单了解一下非关系型数据库关系型数据库的区别。java

MongoDB是NoSQL中的一种。NoSQL的全称是Not only SQL,非关系型数据库。它的特色是性能高扩张性强模式灵活,在高并发场景表现得尤其突出。但目前它还只是关系型数据库的补充,它在数据的一致性,数据的安全性,查询的复杂性问题上和关系型数据库还存在必定差距。mysql

MySQL是关系性数据库中的一种,查询功能强数据一致性高数据安全性高支持二级索引。但性能方面稍逊与MongoDB,特别是百万级别以上的数据,很容易出现查询慢的现象。这时候须要分析查询慢的缘由,通常状况下是程序员sql写的烂,或者是没有键索引,或者是索引失效等缘由致使的。ios

公司ERP系统数据库主要是MongoDB(最接近关系型数据的NoSQL),其次是Redis,MySQL只占不多的部分。如今又从新使用MySQL,归功于阿里巴巴的奇门系统和聚石塔系统。考虑到订单数量已是百万级以上,对MySQL的性能分析也就显得格外重要。git

 

咱们先经过两个简单的例子来入门。后面会详细介绍各个参数的做用和意义。程序员

说明:须要用到的sql已经放在了github上了,喜欢的同窗能够点一下star,哈哈。https://github.com/ITDragonBlog/daydayup/tree/master/MySQL/github

 

业务逻辑:订单导入时,为了不重复导单,通常会经过交易号去数据库中查询,判断该订单是否已经存在。算法

 

最基础的sql语句

 

 

 

查询的自己没有任何问题,在线下的测试环境也没有任何问题。但是,功能一旦上线,查询慢的问题就迎面而来。几百上千万的订单,用全表扫描?啊?哼!sql

怎么知道该sql是全表扫描呢?经过explain命令能够清楚MySQL是如何处理sql语句的。打印的内容分别表示:数据库

由于数据库中只有三条数据,因此rows和filtered的信息做用不大。这里须要重点了解的是type为ALL,全表扫描的性能是最差的,假设数据库中有几百万条数据,在没有索引的帮助下会异常卡顿。

 

初步优化:为transaction_id建立索引

 

 

 

这里建立的索引是惟一索引,而非普通索引。
惟一索引打印的type值是const。表示经过索引一次就能够找到。即找到值就结束扫描返回查询结果。
普通索引打印的type值是ref。表示非惟一性索引扫描。找到值还要继续扫描,直到将索引文件扫描完为止。(这里没有贴出代码)
显而易见,const的性能要远高于ref。而且根据业务逻辑来判断,建立惟一索引是合情合理的。

 

再次优化:覆盖索引

 

 

 

这里将改成了后
Extra 显示 Using index,表示该查询使用了覆盖索引,这是一个很是好的消息,说明该sql语句的性能很好。若提示的是Using filesort(使用内部排序)和Using temporary(使用临时表)则代表该sql须要当即优化了。
根据业务逻辑来的,查询结构返回transaction_id 是能够知足业务逻辑要求的。

 

业务逻辑:优先处理订单级别高,录入时间长的订单。
既然是排序,首先想到的应该是order by, 还有一个可怕的 Using filesort 等着你。

 

最基础的sql语句

 

 

 

首先,采用全表扫描就不合理,还使用了文件排序Using filesort,更加拖慢了性能。
MySQL在4.1版本以前文件排序是采用双路排序的算法,因为两次扫描磁盘,I/O耗时太长。后优化成单路排序算法。其本质就是用空间换时间,但若是数据量太大,buffer的空间不足,会致使屡次I/O的状况。其效果反而更差。与其找运维同事修改MySQL配置,还不如本身乖乖地建索引。

 

初步优化:为order_level,input_date 建立复合索引

 

 

 

建立复合索引后你会惊奇的发现,和没建立索引同样???都是全表扫描,都用到了文件排序。是索引失效?仍是索引建立失败?咱们试着看看下面打印状况

 

 

 

将 换成了 后。type从all升级为index,表示(full index scan)全索引文件扫描,Extra也显示使用了覆盖索引。但是不对啊!!!!检索虽然快了,但返回的内容只有order_level和input_date 两个字段,让业务同事怎么用?难道把每一个字段都建一个复合索引?
MySQL没有这么笨,可使用force index 强制指定索引。在原来的sql语句上修改 便可。

 

 

 

再次优化:订单级别真的要排序么?

 

其实给订单级别排序意义并不大,给订单级别添加索引意义也不大。由于order_level的值可能只有,低,中,高,加急,这四种。对于这种重复且分布平均的字段,排序和加索引的做用不大。
咱们可否先固定 order_level 的值,而后再给 input_date 排序?若是查询效果明显,是能够推荐业务同事使用该查询方式。

 

 

 

和以前的sql比起来,type从index 升级为 ref(非惟一性索引扫描)。索引的长度从68变成了5,说明只用了一个索引。ref也是一个常量。Extra 为Using index condition 表示自动根据临界值,选择索引扫描仍是全表扫描。总的来讲性能远胜于以前的sql。

 

上面两个案例只是快速入门,咱们需严记一点:优化是基于业务逻辑来的。绝对不能为了优化而擅自修改业务逻辑。若是能修改固然是最好的。

 

 

官方定义:索引(Index) 是帮助MySQL高效获取数据的数据结构。
你们必定很好奇,索引为何是一种数据结构,它又是怎么提升查询的速度?咱们拿最经常使用的二叉树来分析索引的工做原理。看下面的图片:
640

建立索引的优点:

1. 提升数据的检索速度,下降数据库IO成本:使用索引的意义就是经过缩小表中须要查询的记录的数目从而加快搜索的速度。

2. 下降数据排序的成本,下降CPU消耗:索引之因此查的快,是由于先将数据排好序,若该字段正好须要排序,则真好下降了排序的成本。

 

建立索引的劣势:
1. 占用存储空间:索引实际上也是一张表,记录了主键与索引字段,通常以索引文件的形式存储在磁盘上。
2. 下降更新表的速度:表的数据发生了变化,对应的索引也须要一块儿变动,从而减低的更新速度。不然索引指向的物理数据可能不对,这也是索引失效的缘由之一。
3. 优质索引建立难:索引的建立并不是一日之功,也并不是一直不变。须要频繁根据用户的行为和具体的业务逻辑去建立最佳的索引。

 

 

咱们常说的索引通常指的是BTree(多路搜索树)结构组织的索引。其中还有聚合索引,次要索引,复合索引,前缀索引,惟一索引,统称索引,固然除了B+树外,还有哈希索引(hash index)等。

 

单值索引:一个索引只包含单个列,一个表能够有多个单列索引
惟一索引:索引列的值必须惟一,但容许有空值
复合索引:一个索引包含多个列,实际开发中推荐使用

实际开发中推荐使用复合索引,而且单表建立的索引个数建议不要超过五个

 

基本语法:

 

建立:

 

 

删除:

 

 

查看:

 

 

哪些状况须要建索引:
1. 主键,惟一索引
2. 常常用做查询条件的字段须要建立索引
3. 常常须要排序、分组和统计的字段须要创建索引
4. 查询中与其余表关联的字段,外键关系创建索引

 

哪些状况不要建索引:
1. 表的记录太少,百万级如下的数据不须要建立索引
2. 常常增删改的表不须要建立索引
3. 数据重复且分布平均的字段不须要建立索引,如 true,false 之类。
4. 频发更新的字段不适合建立索引
5. where条件里用不到的字段不须要建立索引

 

 

 

MySQL自身参见的性能问题有磁盘空间不足,磁盘I/O太大,服务器硬件性能低。
1. CPU:CPU 在饱和的时候通常发生在数据装入内存或从磁盘上读取数据时候
2. IO:磁盘I/O 瓶颈发生在装入数据远大于内存容量的时候
3. 服务器硬件的性能瓶颈:top,free,iostat 和 vmstat来查看系统的性能状态

 

使用explain关键字能够模拟优化器执行sql查询语句,从而得知MySQL 是如何处理sql语句。

 

 

 

id

 

select 查询的序列号,包含一组能够重复的数字,表示查询中执行sql语句的顺序。通常有三种状况:
第一种:id所有相同,sql的执行顺序是由上至下;
第二种:id所有不一样,sql的执行顺序是根据id大的优先执行;
第三种:id既存在相同,又存在不一样的。先根据id大的优先执行,再根据相同id从上至下的执行。

 

select_type

 

select 查询的类型,主要是用于区别普通查询,联合查询,嵌套的复杂查询
simple:简单的select 查询,查询中不包含子查询或者union
primary:查询中若包含任何复杂的子查询,最外层查询则被标记为primary
subquery:在select或where 列表中包含了子查询
derived:在from列表中包含的子查询被标记为derived(衍生)MySQL会递归执行这些子查询,把结果放在临时表里。
union:若第二个select出如今union以后,则被标记为union,若union包含在from子句的子查询中,外层select将被标记为:derived
union result:从union表获取结果的select

 

partitions

 

表所使用的分区,若是要统计十年公司订单的金额,能够把数据分为十个区,每年表明一个区。这样能够大大的提升查询效率。

 

type

 

这是一个很是重要的参数,链接类型,常见的有:all , index , range , ref , eq_ref , const , system , null 八个级别。
性能从最优到最差的排序:system > const > eq_ref > ref > range > index > all
对java程序员来讲,若保证查询至少达到range级别或者最好能达到ref则算是一个优秀而又负责的程序员。

all:(full table scan)全表扫描无疑是最差,如果百万千万级数据量,全表扫描会很是慢。

index:(full index scan)全索引文件扫描比all好不少,毕竟从索引树中找数据,比从全表中找数据要快。
range:只检索给定范围的行,使用索引来匹配行。范围缩小了,固然比全表扫描和全索引文件扫描要快。sql语句中通常会有between,in,>,< 等查询。
ref:非惟一性索引扫描,本质上也是一种索引访问,返回全部匹配某个单独值的行。好比查询公司全部属于研发团队的同事,匹配的结果是多个并不是惟一值。
eq_ref:惟一性索引扫描,对于每一个索引键,表中有一条记录与之匹配。好比查询公司的CEO,匹配的结果只多是一条记录,
const:表示经过索引一次就能够找到,const用于比较primary key 或者unique索引。由于只匹配一行数据,因此很快,若将主键至于where列表中,MySQL就能将该查询转换为一个常量。
system:表只有一条记录(等于系统表),这是const类型的特列,平时不会出现,了解便可

 

possible_keys

 

显示查询语句可能用到的索引(一个或多个或为null),不必定被查询实际使用。仅供参考使用。

 

key

 

显示查询语句实际使用的索引。若为null,则表示没有使用索引。

 

key_len

 

显示索引中使用的字节数,可经过key_len计算查询中使用的索引长度。在不损失精确性的状况下索引长度越短越好。key_len 显示的值为索引字段的最可能长度,并不是实际使用长度,即key_len是根据表定义计算而得,并非经过表内检索出的。

 

ref

 

显示索引的哪一列或常量被用于查找索引列上的值。

 

rows

 

根据表统计信息及索引选用状况,大体估算出找到所需的记录所须要读取的行数,值越大越很差。

 

extra

 

Using filesort: 说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中没法利用索引完成的排序操做称为“文件排序” 。出现这个就要马上优化sql。
Using temporary: 使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序 order by 和 分组查询 group by。 出现这个更要马上优化sql。
Using index: 表示相应的select 操做中使用了覆盖索引(Covering index),避免访问了表的数据行,效果不错!若是同时出现Using where,代表索引被用来执行索引键值的查找。若是没有同时出现Using where,表示索引用来读取数据而非执行查找动做。
覆盖索引(Covering Index) :也叫索引覆盖,就是select 的数据列只用从索引中就可以取得,没必要读取数据行,MySQL能够利用索引返回select 列表中的字段,而没必要根据索引再次读取数据文件。
Using index condition: 在5.6版本后加入的新特性,优化器会在索引存在的状况下,经过符合RANGE范围的条数 和 总数的比例来选择是使用索引仍是进行全表遍历。
Using where: 代表使用了where 过滤
Using join buffer: 代表使用了链接缓存
impossible where: where 语句的值老是false,不可用,不能用来获取任何元素
distinct: 优化distinct操做,在找到第一匹配的元组后即中止找一样值的动做。

 

filtered

 

一个百分比的值,和rows 列的值一块儿使用,能够估计出查询执行计划(QEP)中的前一个表的结果集,信件格式从而肯定join操做的循环次数。小表驱动大表,减轻链接的次数。

 

经过explain的参数介绍,咱们能够得知:
1. 表的读取顺序(id)
2. 数据读取操做的操做类型(type)
3. 哪些索引被实际使用(key)
4. 表之间的引用(ref)
5. 每张表有多少行被优化器查询(rows)

 

 

从程序员的角度
1. 查询语句写的很差
2. 没建索引,索引建的不合理或索引失效
3. 关联查询有太多的join

从服务器的角度
1. 服务器磁盘空间不足
2. 服务器调优配置参数设置不合理

 

1. 索引是排好序且快速查找的数据结构。其目的是为了提升查询的效率。
2. 建立索引后,查询数据变快,但更新数据变慢。
3. 性能降低的缘由极可能是索引失效致使。
4. 索引建立的原则,常常查询的字段适合建立索引,频繁须要更新的数据不适合建立索引。
5. 索引字段频繁更新,或者表数据物理删除容易形成索引失效。
6. 擅用 explain 分析sql语句
7. 除了优化sql语句外,还能够优化表的设计。如尽可能作成单表查询,减小表之间的关联。设计归档表等。

 

到这里,MySQL的索引优化分析就结束了,有什么不对的地方,你们能够提出来。若是以为不错能够点一下赞。

 

 

 

推荐阅读:

1,小白专属mysql入门

2,MySQL的索引是什么?怎么优化?

3,第5篇:数据库系统的实现

4,数据仓库③-实现与使用(含OLAP重点讲解)

640?wx_fmt=png


文章来源:https://blog.csdn.net/rlnLo2pNEfx9c/article/details/80731300

相关文章
相关标签/搜索