妙博客

主机评测 香港服务器 洛杉矶VPS测评

mysql之查询优化

下面我们将讲述如何对mysql进行优化分析


一、mysql执行流程

image.png

mysql分为客户端、服务端、存储引擎

  1. 客户端

    客户端一般是java应用,或者其他可视化连接工具

  2. 服务端

    连接器:负责管理客户端的连接以及验证权限

    分析器:做语法和此法的分析,查询哪张表,什么条件    

    优化器:生成执行计划

    执行器:真正执行sql调用引擎接口返回数据. MYISAM:不支持事务,也不支持行级锁,但它会存储表的行数,所以查行数较快。    

    查询缓存:8.0之后废弃了,由于数据一旦变动,缓存都要失效,收益并不明显


mysql在做更新操作时,会写日志,它采用write-ahead-log,写数据之前先写日志。日志分为两个部分:redo log和bin log

image.png


左边是redolog的格式,环形的,只有4G空间,write point是写的点位,check point是擦的点位。整个流程是顺时针循环的,当写的点位达到擦的点位时,就不能再写入了,需要等待擦的点位顺时针移动移。

binlog是磁盘上的全量日志,只要磁盘足够大,binlog只管追加写。


mysql崩溃是如何恢复的?

        由上图,假设要把1变成2,它涉及到一个两阶段提交,首先会记录一个redolog,这个阶段redolog是一个就绪阶段,并没有提交,接着记录一个binlog,binlog记录完成后,再把redolog变成commit的状态,数据就变更完成了。

        这个流程中如果redo repare过中断了,是无法恢复的,因为binlog没有任何日志。如果是binlog完成了,后面崩溃了都可以恢复。


二、mysql优化

explain分析

例如:EXPLAIN select * from order_info where period = 12 order by modified desc limit 0,10

将会查出如下结果:

image.png

各字段解释

    id: 要执行的id,id越大越先执行。

    select_type: 查询类型,分为四种

SIMPLE:查询语句只有一个 select,没有其余子查询
PRIMARY:复杂查询最前面的 select 查询,是最外层的查询
SUBQUERY:from 关键字前的子查询
DERIVED:from 关键字后的子查询,也叫派生查询
UNION:在 union 查询中,第二个及其后面的 select 语句,都是 UNION类型

    table: 这一列表示 explain 的一行正在访问哪个表

当 from 子句中有子查询时,table列是 格式,表示当前查询依赖 id=N 的查询,于是先执行 id=N 的查
询。
当有 union 时,UNION RESULT 的 table 列的值为<union1,2>,1和2表示参与 union 的 select 行id。

    type: 表示join的类型

依次从好到差:system,const,eq_ref,ref,fulltext,ref_or_null,unique_subquery,index_subquery,range,index_merge,index,ALL,除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一个索引
A:system:表中只有一行数据或者是空表,且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index
B:const:使用唯一索引或者主键,返回记录一定是1行记录的等值where条件时,通常type是const。其他数据库也叫做唯一索引扫描
C:eq_ref:出现在要连接过个表的查询计划中,驱动表只返回一行数据,且这行数据是第二个表的主键或者唯一索引,且必须为not null,唯一索引和主键是多列时,只有所有的列都用作比较时才会出现eq_ref
D:ref:不像eq_ref那样要求连接顺序,也没有主键和唯一索引的要求,只要使用相等条件检索时就可能出现,常见与辅助索引的等值查找。或者多列主键、唯一索引中,使用第一个列之外的列作为等值查找也会出现,总之,返回数据不唯一的等值查找就可能出现。
E:fulltext:全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引
F:ref_or_null:与ref方法类似,只是增加了null值的比较。实际用的不多。
G:unique_subquery:用于where中的in形式子查询,子查询返回不重复值唯一值
H:index_subquery:用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。
I:range:索引范围扫描,常见于使用>,<,is null,between ,in ,like等运算符的查询中。
J:index_merge:表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取所个索引,性能可能大部分时间都不如range
K:index:索引全表扫描,把索引从头到尾扫一遍,常见于使用索引列就可以处理不需要读取数据文件的查询、可以使用索引排序或者分组的查询。
L:all:这个就是全表扫描数据文件,然后再在server层进行过滤返回符合要求的记录。

    possible_keys: 查询可能使用到的索引都会在这里列出来

    key: 查询真正使用到的索引,select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。

    key_len: 用于处理查询的索引长度

如果是单列索引,那就整个索引长度算进去,如果是多列索引,那么查询不一定都能使用到所有的列,具体使用到了多少个列的索引,这里就会计算进去,没有使用到的列,这里不会计算进去。
留意下这个列的值,算一下你的多列索引总长度就知道有没有使用到所有的列了。要注意,mysql的ICP特性使用到的索引不会计入其中。另外,key_len只计算where条件用到的索引长度,
而排序和分组就算用到了索引,也不会计算到key_len中。

    ref: 如果是使用的常数等值查询,这里会显示const,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func

    rows: 这里是执行计划中估算的扫描行数,不是精确值

    filtered: 表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数

    Extra: 额外信息,这个列可以显示的信息非常多,有几十种,常用的有

A:distinct:在select部分使用了distinc关键字
B:no tables used:不带from字句的查询或者From dual查询
C:使用not in()形式子查询或not exists运算符的连接查询,这种叫做反连接。即,一般连接查询是先查询内表,再查询外表,反连接就是先查询外表,再查询内表。
D:using filesort:排序时无法使用到索引时,就会出现这个。常见于order by和group by语句中
E:using index:查询时不需要回表查询,直接通过索引就可以获取查询的数据。
F:using join buffer(block nested loop),using join buffer(batched key accss):5.6.x之后的版本优化关联查询的BNL,BKA特性。主要是减少内表的循环数量以及比较顺序地扫描查询。
G:using sort_union,using_union,using intersect,using sort_intersection:
using intersect:表示使用and的各个索引的条件时,该信息表示是从处理结果获取交集
using union:表示使用or连接各个使用索引的条件时,该信息表示从处理结果获取并集
using sort_union和using sort_intersection:与前面两个对应的类似,只是他们是出现在用and和or查询信息量大时,先查询主键,然后进行排序合并后,才能读取记录并返回。
H:using temporary:表示使用了临时表存储中间结果。临时表可以是内存临时表和磁盘临时表,执行计划中看不出来,需要查看status变量,used_tmp_table,used_tmp_disk_table才能看出来。
I:using where:表示存储引擎返回的记录并不是所有的都满足查询条件,需要在server层进行过滤。查询条件中分为限制条件和检查条件,5.6之前,存储引擎只能根据限制条件扫描数据并返回,然后server层根据检查条件进行过滤再返回真正符合查询的数据。5.6.x之后支持ICP特性,可以把检查条件也下推到存储引擎层,不符合检查条件和限制条件的数据,直接不读取,这样就大大减少了存储引擎扫描的记录数量。extra列显示using index condition
J:firstmatch(tb_name):5.6.x开始引入的优化子查询的新特性之一,常见于where字句含有in()类型的子查询。如果内表的数据量比较大,就可能出现这个
K:loosescan(m..n):5.6.x之后引入的优化子查询的新特性之一,在in()类型的子查询中,子查询返回的可能有重复记录时,就可能出现这个
除了这些之外,还有很多查询数据字典库,执行计划过程中就发现不可能存在结果的一些提示信息


优化方法一:强制使用索引进行查询(一般不使用,不同数据库数据分布不一样,不通用)

image.png

可能用到的索引是idx_period,实际使用的索引是idx_modified,由于数据库选错索引,导致了慢查询,我们加上force index(idx_period) 强制使用索引

select * from order_info force index(idx_period) where period = 12 order by modified desc limit 0,10;

但是Extra中可能出现:Using fileSort,这样会非常消耗硬盘资源,应该尽量避免

主键索引是存储整行数据的,其它的二级索引都是只存储索引和主键ID。


优化方法二:创建联合索引进行优化

针对语句 select * from order_info where period = 12 order by modified desc limit 0,1000;

当where条件和order by条件不是同一个字段的时候,建立一个联合索引idx_period_modified(period, modified)一般情况下会大大减少执行时间

alter table order_info add index idx_period_modified(period, modified)


优化方法三:尽量减少回表查询次数,比如深翻页场景

让我们查询的字段都在索引上,比如如下sql耗时为1.879秒(其中满足条件的数据量为25万左右)

select * from order_info where period = 12 order by modified desc limit 240000,1000;

我们使用join方式尽量减少回表查询次数,如下优化的sql耗时仅为0.11秒,提升了百倍性能。

SELECT * from (select id from order_info where period=12 ORDER BY modified desc limit 240000,1000) 
as temp join order_info where temp.id=order_info.id;

通过上面这种覆盖索引子查询然后回原表join的方式,优化了一个回表次数,回表是一个很耗费io的操作,数据库的优化其实都是io的优化。数据库是按页存储的,如果我们把数据都集中在一页,一次磁盘io把它全读进来,比多次io节省大量时间。


优化方法四:避免索引失效导致全表扫描

只有两种情况索引失效,一是它没有办法使用索引,二是使用索引不如不使用。

explain分析出来的sql里面的type如果是ALL则表示全表扫描了,它通常出现在以下几种情况:

    1.该表非常小,执行表扫描比费心查找key要快。 这对于行数少于 10 且行长度较短的表很常见。

    2.索引列 不存在 ON 或 WHERE 子句中

    3.您正在将索引列与常量值进行比较,并且 MySQL 已经计算出(基于索引树)常量覆盖表的太大部分并且全表扫描会更快。 

    4.您正在通过另一列使用基数较低的key(许多行与key值匹配)。 在这种情况下,MySQL 假设通过使用key,它可能会执行许多key查找,并且表扫描会更快。


索引合并场景:

索引合并检索具有多个范围扫描的行并将它们的结果合并为一个。  此访问方法仅合并来自单个表的索引扫描,而不是跨多个表的扫描。  合并可以产生其底层扫描的并集、交集或交并集。

下面是可以使用索引合并的场景:

SELECT * FROM tbl_name WHERE key1 = 10 OR key2 = 20;

SELECT * FROM tbl_name
  WHERE (key1 = 10 OR key2 = 20) AND non_key = 30;
  
SELECT * FROM t1, t2
  WHERE (t1.key1 IN (1,2) OR t1.key2 LIKE 'value%')
  AND t2.key1 = t1.some_col;
  
SELECT * FROM t1, t2
  WHERE t1.key1 = 1
  AND (t2.key1 = t1.some_col OR t2.key2 = t1.some_col2);

下图的执行计划type列,表示合并索引

image.png

请注意,所以合并算发具有如下两个限制:

  1. 如果您的查询有一个复杂的 WHERE 子句和深度 AND/OR 嵌套并且 MySQL 没有选择最佳计划,请尝试使用以下恒等转换分布术语

(x AND y) OR z => (x OR z) AND (y OR z)
(x OR y) AND z => (x AND z) OR (y AND z)

    2.索引合并不支持全文索引


优化方法五:order by 排序优化

本节描述 MySQL 何时可以使用索引来满足 ORDER BY 子句,无法使用索引时将使用fileSort文件排序操作,以及优化器提供的有关 ORDER BY 的执行计划信息,带和不带 LIMIT 的 ORDER BY 可能会以不同的顺序返回行数据。

  1. 使用索引满足排序

  2. 如果查询包含 GROUP BY 但您希望避免对结果进行排序,则可以通过指定 ORDER BY NULL 来抑制排序

    如下所示:

INSERT INTO foo
SELECT a, COUNT(*) FROM bar GROUP BY a ORDER BY NULL;

优化器可能仍然选择使用排序来实现分组操作。 ORDER BY NULL 禁止对结果进行排序,而不是通过分组操作确定结果之前进行排序。

Copyright Your 142132.com Rights Reserved. 赣ICP备17010829号-2