MySQL 优化实战 - 索引篇

关于SQL优化,这个问题,相信你们过多过少都有过一些了解。最近我也在研究SQL优化方面的东西,分享一些经验。

首先简单介绍下索引,"索引" 是SQL优化中很重要的一部分(可是索引并非优化的惟一选项)html

索引原理简述

如何理解索引?索引其实就是一种数据结构,用于快速定位和访问数据库中的数据。mysql

一般来讲索引使用的数据结构是 B-Tree / B+Tree。以B-Tree为例,假设每一个节点存储100个Key,三层的B-Tree 可存储一百万数据,若是将根节点存入内存中的话,只须要读取两次磁盘就能够从100万数据中找到指定数据web

B-Tree

关于B-Tree 推荐阅读这篇 https://www.geeksforgeeks.org/introduction-of-b-tree-2/ 包含B-Tree的查询,新增,删除操做如何实现算法

MySQL 执行计划

SQL优化中查看执行计划是必不可少的一项,经过 explain 关键字能够查看MySQL中的执行计划sql

image.png

注:G 含义是纵向显示结果

若是以前没有了解的 EXPLAIN 的同窗,看到这个列表确定是一脸懵逼。不要紧咱们先来挑几个重要的属性认识一下。数据库

  • type:ALL 表明全表扫描
  • key:表明使用的索引,NULL 表明没有使用索引
  • rows:扫描行数

关于explain 再扩展一下,先执行 explain extended ...; ,再执行 SHOW WARNINGS 能够看到MySQL优化器对咱们的SQL作了什么优化。以下图所示服务器

image.png

利用索引来优化SQL

使用索引的优势:减小服务器扫描的数据量、避免排序和临时表、将随机I/O变为顺序I/O

经过下图,咱们能够看到,添加了索引以后扫描行数从三十万行降到了1,性能提高可想而知数据结构

image.png

生产环境要注意,建立索引是一个很是耗时的操做,而且会阻塞其余操做。

生产环境添加索引有没有什么完美方案?
有的,若是你的MySQL使用主从策略的时候,能够像Nginx不停机升级web服务那样,先移除一个节点为该节点执行 ALTER TABLE 操做,而后巴拉巴拉,由于具体我也没操做过就不细说了,感兴趣你们能够Google一下,动手尝试一下。若是是单机部署的话,只能用户少的时候在执行这种操做了函数

使用索引链接表

索引也能够提升表链接的性能,下面是个例子,用户表左连订单表,对user_id 添加索引的先后对比性能

未添加索引

添加索引

like优化

image.png

经过上述例子,咱们能够看出,若是模糊查询时以%开头的话,MySQL没法使用索引,可是一般来讲模糊查询时咱们的匹配方式都会是 %xxx%,那么如何优化呢?

这里能够经过存"反值"的方式巧妙的解决这个问题,例如我如今在数据库加一列 reverse_order_no 存储订单号的反值(并添加索引),匹配的时候再经过 REVERSE('%910') 函数将参数取反。

image.png

这里也可使用 or,以下图,查看执行计划会发现Extra 属性返回 "Using sort_union(order_no,reverse_order_no); Using where" 这里表明MySQL发生了索引合并,后文咱们会讲到

image.png

排序以及多列索引

排序须要加索引!相信你们可能知道这个道理,可是以下图所示,user_id 和 addtime 两列都创建了索引,那么下面这条查询排序使用索引了吗?

image.png

答案是:并无!为何?注意 Extra 中的 using filesort,表明MySQL 使用了内部文件排序算法对结果集进行了排序。MySQL 一般在一个表上只选择一个索引(有例外的状况),这种状况若是咱们但愿排序使用索引的话,能够创建一个多列索引,以下图所示

image.png

并且多列索引最左边的列,能够看成单列索引来使用

MySQL 优化器特性

咱们刚刚说过 MySQL 一般在一个表上只选择一个索引,如何理解?例如索引A和索引B 一个须要扫描十万行,一个须要扫描五万行,那么MySQL必定选择开销最小的索引方式。

在一些特殊状况下,MySQL 会选择 Index Merge(索引合并),即在一个表上使用多个索引

  • Union:两个基数很高的索引执行OR操做时

image.png

  • Sort-Union:与上述相似,一旦or的左右两边出现范围查询,会使用该算法,区别是Sort-Union会进行排序

image.png

  • intersect:针对惟一值很少的索引列,例如在 is_pay(0-未支付,1-支付),is_send(0-未发货,1-发货) 两列创建索引,查询已支付而且未发货的订单,以下图所示

image.png

根据MySQL 5.7开发文档所示,还有一种会使用intersect,InnoDB 主键上的任何范围搜索

image.png

关于Index Merge的更多信息,参考MySQL开发文档
https://dev.mysql.com/doc/refman/5.7/en/index-merge-optimization.html

索引的影响

添加索引虽然能够提高咱们的SQL性能,可是随之而来也会带来必定的开销

  • 数据插入和更新的性能,由于须要构建索引的缘由,在数据量大的时候会比较明显,下图是 《Effective MySQL之SQL语句最优化》中对添加索引先后的插入性能对比

image.png

  • 磁盘空间的影响,一样也是来自于书中的测试

image.png

image.png

能够看到在添加了索引以后,空间占用是原来的7倍,在数据量庞大时,这是一个须要关注的点。

还有须要注意的一点是,在MySQL Innodb 中有聚簇索引和二级索引,通常来讲主键就是聚簇索引,而其余的索引都是二级索引。

二级索引所存储的值是聚簇索引。因此当使用二级索引来进行检索时,MySQL 会先经过该索引找到对应的聚簇索引,再经过该聚簇索引找到对应的数据。这时使用占用字节更小的类型来作主键会更好,会节省索引占用空间

参考

Effective MySQL之SQL语句最优化
相关文章
相关标签/搜索