Mysql 查询性能优化

注:本篇是《高性能Mysql》第三版的读书笔记

 

查询性能优化

查询性能低下的基本原因就是访问的数据量太多,而有些数据根本就用不着,或者mysql在进行分析时存在大量超过结果行的数据

一些经典案例对性能造成损失

1、查询不需要的记录,返回没有limit

2、多表关联时返回全部列。

3、总是取出全部列。这个需要作出取舍。

4、重复查询相同的数据,这种最好将这种数据进行缓存。

对于mysql最简单衡量查询开销的三个指标如下

  • 响应时间
  • 扫描的行数
  • 返回的行数

响应时间是两个的和:服务时间和排队时间。排队时间指的是获取锁或者IO等到的时间etc.

扫描行数和返回行数这两个就像是hash&链表取数据一样  肯定希望像hash这样  o1的复杂度不需要扫描额外的数据行。

explain 中的type反应了访问类型,访问类型有很多,包括从全表扫描all到范围到索引等等。所代表的访问行数就完全不一样了。

rows代表预估扫描的行数

extra 表示mysql将通过哪种方式筛选存储引擎返回的记录。   using where 表示使用where条件。

mysql可以以三种方式应用where条件,下面为分别好到坏

  1. 在索引中使用where条件来过滤不匹配的记录,这是在存储引擎层完成的。
  2. 使用索引覆盖扫描,(using index)来返回记录,直接从索引中过滤了不需要的数据并命中返回的结果,这是在mysql服务器层完成,但无需再回表查询记录。
  3. 从数据表中返回数据,然后过滤不满足条件的记录(using where)这是在mysql服务器层完成的,mysql需要先从数据表读取记录然后过滤。

 

select a_id,count(*) from tab group by a_id; 这中分组会扫描大量的数据行并返回少量数据,有很大优化空间

  • 使用索引覆盖扫描,把所有需要用到的列放到索引中,这样存储引擎就无须回表获取对应行就可以返回结果了。
  • 改变表结构,使用汇总表。这个对于不需要实时展示的数据(接受延迟)很好用。响应时间性能会提高很多倍。
  • 重写这个查询。

重构查询的方式

  1. 可以将一个复杂查询拆分成多个小查询(如果合理的话)
  2. 切分查询,例如删除旧数据,可以写一个定时任务,每次删点,大大减少了一次删除大量数据对服务器造成的影响。
  3. 分解关联查询
select * from tag 

left join tag_post on tag_post.tag_id = tag.id 

left join post on tag_post.id = post.id  

where tag.tag = 'mysql'

可以拆解成

select * from tag where tag = 'mysql'

select * from tag_post where tag_id = 123

select * from post where post.id in (1,2,3,4,5)
  • 分解关联查询会让缓存的效率更高,如果变化较小就可以很好的服用mysql的查询缓存。用到缓存之后一些步骤就秒执行成了
  • 将查询分解后,执行单个查询可以减少锁的竞争。
  • 在应用层做关联,可以更容易对数据库进行拆分,提高性能和扩展性。
  • 查询本身的效率可能也会提升,in 会按照顺序进行查询,比随机关联更高效。
  • 可以减少冗余的记录。
  • 相当于在应用层做的哈希关联,效率要比在mysql的嵌套循环高。

 

一个查询的过程如下

mysql客户端和服务端之间的通讯协议是半双工的,任意时刻,客户端与服务端只能单向发送数据。

一端开始发送消息,另一端接收完整消息才可以响应。

当使用mysql的库函数取数据时,看起来像是从mysql服务器取数据,但是实际上是从库函数缓存取数据。将数据缓存到内存来提高性能。这个查询缓存在不同的编程语言调用mysql的时候都可以设置和指定要不要使用缓存。

对于取大量结果集的操作不适合用查询缓存,库函数会花很多时间和内存存储所有的结果集。

 

查询状态

对于一个mysql连接或者说一个线程,任何时刻都有一个状态。该状态表示mysql当前正在做什么。command表示状态。

查询缓存

在解析一个查询语句之前,如果查询缓存是打开的,mysql会优先检查这个查询是否命中查询缓存中的数据,这个检查是通过一个对大小写敏感的哈希查找实现的。

 

查询优化处理

包括:解析sql,预处理,优化sql执行计划。

mysql通过关键字将sql语句进行解析并生成一颗相应的解析树。

下面是一些mysql能够处理的优化类型:

  1. 重新定义关联表的顺序
  2. 将外连接转化成内连接
  3. 使用等价变换规则
  4. 优化count() min() max()
  5. 预估并转化为常数表达式
  6. 覆盖索引扫描
  7. 子查询优化
  8. 提前终止查询
  9. 等值传播
  10. 列表in()的比较  in会先进行排序。

 

mysql如何执行关联查询

mysql执行关联查询的策略很简单:mysql对任何关联都执行嵌套循环关联操作,mysql先在一个表中循环取出单条的数据,然后在嵌套循环到下一个表中寻找匹配的行,直到所有表中的行匹配为止。可想而知,被连接表的列有索引是多么关键的!

 

mysql优化器最重要的一部分就是关联查询优化器,它决定了多个表关联时候的顺序,不同的顺序意味着不同的扫描行数。优化器会将sql的关联表顺序进行优化使其有更少的扫描行数。

 

排序优化

mysql在排序时是可以使用索引的,不过要遵循索引的使用规则,例如联合索引,要保证排序字段出现顺序与联合索引保持最左匹配法则。

mysql在排序的时候可以使用index或者file sort  尽量保证使用index 性能好, filesort会大量使用内存数据量大则更会使用磁盘。

extra 中包括 using filesort 表示使用了file sort进行排序,  using temporary 表示使用了临时表。

5.6版本以后的mysql 在limit和order by 的时候不再对所有结果进行排序,刨除不满足的结果再进行排序,可想而知效率提高了。

 

索引合并优化

注⚠️:当where条件字句中包含多个复杂条件的时候,mysql能够访问单个表的多个索引以合并和交叉过滤的方式来定位行数据。

 

优化特定类型的查询

count函数多用来计算列的非null数量或者行数count(*)

对于myisam对count(*)有优化,当然只局限于没有进行where条件查询时,或者对于非null的列。其他列就没有优化了。

 

优化关联查询

  1. 确保on或者using子句中的列有索引
  2. 确保任何group by order by 中的表达式尽量只有一个列,这样更好使用索引进行优化
  3. 升级mysql时,关联语法、运算符优先级可能会变化

 

优化group by&&distinct

当无法使用索引时,group by 有两种优化策略,分别为使用临时表或者文件排序来分组。

注:如果没有通过order by 显示指定了排序的列,当查询使用group by 的时候,结果集会按照分组的字段进行排序。这种排序又导致了需要文件排序。也可以group by 时指定 asc  desc

优化limit

可以使用上次查询的id  来做where筛选进而达到优化limit的效果。

 

优化union

union  是去重的,取数据的时候用了临时表,把数据扔到临时表里,再从临时表取数据返回给客户端,
union会对临时表中所有数据进行distinct操作(实际上distinct会对select的所有列去重,因此union是所有列去重)
distinct是遍历比较进行排序去重的,因此数据量大的时候很恐怖,有很大的性能问题。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章