查询性能优化【《高性能mysql第三版》笔记】

前言

即使库表结构再合理、索引再合适,如果查询写得很糟糕,也无法实现高性能。
查询优化,索引优化,库表结构优化要齐头并进。

1. 为什么查询速度会慢

如果把查询看作是一个任务,那么它由一系列子任务组成,每个子任务都会消耗一定的时间。如果要优化查询,实际上要优化其子任务,要么消除其中一些子任务,要么减少子任务的执行次数,要么让子任务运行得更快。

通常来说, 查询的生命周期大致可以按照顺序来看:从客户端到服务器,然后在服务器上进行解析,生成执行计划,执行,并返回结果给客户端。其中“执行”可以是整个生命周期中最重要的阶段,这其中包括了大量为了检索数据到存储引擎的调用以及调用后的数据处理,包括排序、分组等。

在完成这些任务的时候,查询需要在不同的地方花费时间,包括网络,CPU计算,生成统计信息和执行计划、锁等待(互斥等待)等操作,尤其是向底层存储引擎检索数据的调用操作,这些调用需要在内存操作、CPU操作和内存不足时导致的I/O操作上消耗时间。根据存储引擎不同,可能还会产生大量的上下文切换以及系统调用。

2. 慢查询基础:优化数据访问

查询性能低下最基本的原因是访问的数据太多,某些查询可能不可避免的需要筛选大量数据,但并不常见。大部分性能低下的查询都可以通过减少访问的数据量的方式进行优化。对于低效的查询,我们发现通过下面两个步骤来分析总是很有效:

    1. 确认应用程序是否在检索大量超过了需要的数据,这通常意味着访问了太多的行或者列;
    1. 确认MySQL服务器层是否在分析大量超过需要的数据行。

3. 重构查询的方式

1. 一个复杂查询还是多个简单查询
2. 切分查询
image.png

image.png
3. 分解关联查询
image.png
4. 查询执行的基础
image.png

image.png
  • 4.1 MySQL客户端/服务器通信协议
    MySQL客户端和服务器之间的通信协议是“半双工”的,这意味着,在任何一个时刻,要么是由服务器向客户端发送数据,要么是有客户端向服务器发送数据,这两个动作不能同时发生。这种协议让MySQL通信简单快速,但是也从很多地方限制了MySQL。一个明显的限制是,这意味着没法进行流量控制,一旦一端开始发生消息,另一端要接收完整个消息才能响应它。


    image.png

    image.png
  • 4.2 查询缓存
    在解析一个查询语句之前,如果查询缓存是打开的,那么MySQL会优先检查这个查询是否命中查询缓存的数据,这个检查是通过一个对大小写敏感的哈希查找实现的。如果当前的查询恰好命中了缓存,那么在返回查询结果之前MySQL会检查一次用户权限。

  • 4.3 查询优化处理
    查询的生命周期的下一步是将一个sql转换成一个执行计划,MySQL再依照这个执行计划和存储引擎进行交互。这包括多个子阶段:解析sql、预处理、优化sql执行计划。这个过程中任何错误都可能终止查询。

MySQL使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。MySQL的查询优化器是一个非常复杂的部件,优化策略简单地分为两种,一种是静态优化,一种是动态优化。静态优化可以直接对解析树进行分析,并完成优化。

静态优化可以通过简单的代数变换将where转换成另一种等价形式,它不依赖于特别的数值,第一次完成后就一直有效,即使使用不同的参数重复执行查询也不会发生变化。可以认为这是一种“编译时优化”。

动态优化则和查询的上下文有关,也可能和很多其它因素有关,例如where条件中的取值,索引中条目对应的数据行数等。这需要在每次查询的时候都重新评估,可以认为这是“运行时优化”。

  • 4.4 查询执行引擎
    在解析和优化阶段,MySQL将生成查询对应的执行计划,MySQL的查询执行引擎则根据这个执行计划来完成整个查询。执行只是简单地根据执行计划给出的指令逐步执行。
5 MySQL查询优化器的局限性
  • 1 关联子查询
    MySQL的子查询实现非常糟糕。最糟糕的一类查询是where条件中包含IN()的子查询语句。
    大部分情况下应该以连表代替子查询。

  • 2 UNION的限制


    image.png
  • 3 索引合并优化
    当where子句中包含多个复杂条件的时候,MySQL能否访问单个表的多个索引以合并和交叉过滤的方式来定位需要查找的行。

  • 4 等值传递

  • 5 并行执行
    不支持。

  • 6 哈希关联
    不支持。

  • 7 松散索引扫描
    不支持

  • 8 最大值和最小值优化
    MIN()和MAX()查询,MySQL的优化做得并不好。

  • 9 在同一个表上查询和更新
    不允许。

6 查询优化器的提示(略)
7 优化特定类型的查询
  • 1 优化COUNT()查询
    只适用MyISAM的:


    image.png
  • 2 优化关联查询
    -- 确保ON或者USING子句的列上有索引。在创建索引是时候要考虑到关联的顺序。当表A和B用c列关联的时候,如果优化器的关联顺序是B,A,那么就不需要在B表的对应列上建上索引。没有用到的索引只会带来额外的负担。一般来说,除非有其它理由,否则只需要在关联顺序中的第二个表的相应列上创建索引。

-- 确保任何的GROUP BY和ORDER BY中的表达式只涉及到一个表的列,这样MySQL才有可能使用索引来优化这个过程;

-- 当升级MySQL的时候注意:关联语法、运算符优先级等其它可能发生变化的地方。因为以前是普通关联的地方可能会变成笛卡儿积,不同类型的关联可能会生成不同的结果等。

  • 3 优化子查询
    尽量用关联查询替代。

  • 4 优化GROUP BY和DISTINCT
    在很多场景下,MySQL都使用同样的办法优化这两种查询。事实上,MySQL优化器会在内部处理的时候相互转化这两类查询。它们都可以使用索引来优化,这也是最有效的优化办法。

在MySQL中,当无法使用索引的时候,GROUP BY使用两种策略来完成:使用临时表或者文件排序来做分组。对于任何查询语句,这两种策略的性能都有可以提升的地方。可以通过使用提示SQL_BIG_RESULT和SQL_SMALL_RESULT来让优化器按照你希望的方式运行。


image.png

image.png

你可能感兴趣的:(查询性能优化【《高性能mysql第三版》笔记】)