《高性能MYSQL》-- 查询性能优化

时间:2024-04-15 08:38:30

查询性能优化

深刻地理解MySQL如何真正地执行查询,并明白高效和低效的原因何在

为什么查询速度会慢

查询的生命周期(不完整):从客户端到服务器,然后服务器上进行语法解析,生成执行计划,执行,并给客户端返回结果。

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

一条查询,如果查询得很慢,原因大概率是访问的数据太多

对于低效的查询,通过下面两个步骤来分析总是很有效:

1. 确认应用程序是否在检索大量且不必要的数据。这通常意味着访问了多的行,但有时候也可能是访问了太多的列。

2. 确认MySQL服务器层是否在分析大量不需要的数据行。

一些导致慢查询的操作:

  1. 查询了不需要的记录
  2. 多表联接时返回全部列
  3. 总是取出全部列
  4. 重复查询相同的数据

MySQL最简单的衡量查询开销的三个指标如下:

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

如何判断慢查询

1.判断响应时间
概念

响应时间其实有两部分:服务时间和排队时间。服务时间是指数据库处理这个查询真正花了多长时间。排队时间是指服务器因为等待某些资源而没有真正执行查询的时间——可能是等I/O操作完成,也可能是等待行锁,等等。

影响响应时间的因素:

存储引擎的锁 (表锁、行锁)、高并发资源竞争、硬件响应等

快速上限估计法:

用来估计响应时间,了解这个查询需要哪些索引以及它的执行计划是么,然后计算大概需要多少个顺序和随机I/O,再用其乘 以在具体硬件条件下一次I/O的消耗时间。最后把这些消耗都加起来,就可以获得一个大概参考值来判断当前响应时间是不是一个合理的值。

2.扫描的行数和返回的行数比例

理想情况下扫描的行数和返回的行数应该是相同的,这种情况不多。

扫描的行数与返 回的行数的比率通常很低,一般在1:1到10:1之间。

3.扫描的行数和访问类型

EXPLAIN语句中的type列反映了访问类型。访问类型有很多种,从全表扫描到索引扫描、 范围扫描、唯一索引查询、常数引用等。这里列出的这些,速度从慢到快,扫描的行数从 多到少。不需要记住这些访问类型,但需要明白扫描表、扫描索引、范围访问和单值访问的概念。

4.MySQL能够使用如下三种方式应用WHERE条件,从好到坏依次为:

● 在索引中使用WHERE条件来过滤不匹配的记录。这是在存储引擎层完成的。

● 使用索引覆盖扫描(在Extra列中出现了Using index)来返回记录,直接从索引中过滤不需要的记录并返回命中的结果。

● 从数据表中返回数据,然后过滤不满足条件的记录

(在Extra列中出现Using

5.如果发现查询需要扫描大量的数据但只返回少数行,要这样改进

● 使用覆盖索引,把所有需要用的列都放到索引中,这样存储引擎无须回表获取对应行就可以返回结果了

● 改变库表结构。例如,使用单独的汇总表(这是我们在第6章中讨论的办法)。

● 重写这个复杂的查询,让MySQL优化器能够以更优化的方式执行这个查询(这是本章后续需要讨论的问题)。

重构查询的方式

一个复杂查询还是多个简单查询

MySQL从设计上让连接和断开连接都很轻量,在返回一个小的查询结果方面很高效。现代的网络速度比以前要快很多,能在很大程 度上降低延迟。

在某些版本的MySQL中,即使在一台通用服务器上,也能够运行每秒超

过10万次的简单查询,即使是一个千兆网卡也能轻松满足每秒超过2000次的查询。

所以运行多个小查询现在已经不是大问题了。

在MySQL内部,每秒能够扫描内存中上百万行的数据

在其他条件都相同的时候,使用尽可能少的查询当然是更好的。但是有时候,将一个大查询分解为多个小查询是很有必要的。

切分查询

定期清除大量数据时,如果用一个大的语句一次性完 成的话,则可能需要一次锁住很多数据、占满整个事务日志、耗尽系统资源、阻塞很多小

的但重要的查询。将一个大的DELETE语句切分成多个较小的查询可以尽可能小地影响MySQL的性能,同时还可以降低MySQL复制的延迟。

分解联接查询

用分解联接查询的方式重构查询有如下优势:

  1. 让缓存的效率更高
  2. 将查询分解后,执行单个查询可以减少锁的竞争
  3. 查询本身的效率也可能会有所提升。比如使用IN()代替联接查询,可以让MySQL按照ID顺序进行查询,这可能比随机的联接要更高效。
  4. 可以减少对冗余记录的访问

查询执行的基础

MySQL执行一个查询的过程

1. 客户端给服务器发送一条SQL查询语句。

2. 服务器端进行SQL语句解析、预处理,再由优化器生成对应的执行计划。

3. MySQL根据优化器生成的执行计划,调用存储引擎的API来执行询。

4. 将结果返回给客户端

MySQL的客户端/服务器通信协议

概念

MySQL的客户端和服务器之间的通信协议是“半双工”的,这意味着,在任 何时刻,要么是由服务器向客户端发送数据,要么是由客户端向服务器发送数据,这两个动作不能同时发生。

理解

这样设计的好处是让MYSQL通信十分简单快速,缺点是一旦一端开始发送消息,另一端要接收完整个消息才能响应它。

缓存

多数连接MySQL的库函数都可以获得全部结果集并将结果缓存到内存里,还可以逐行获 取需要的数据。默认一般是获得全部结果集并将它们缓存到内存中。

MySQL通常需要等所有的数据都已经发送给客户端才能释放这条查询所占用的资源,所以接收全部结果并缓存通常可以减少服务器的压力,让查询能够早点结束、早点释放相应的资源。

查询状态

对于一个MySQL连接,或者一个线程,任何时刻都有一个状态,该状态表示了MySQL当 前正在做什么。有很多种方式能查看当前的状态,最简单的是使用SHOW FULL PROCESSLIST命令(该命令返回结果中的Command列,其就表示当前的状态)。

Sleep

线程正在等待客户端发送新的请求。

Query

线程正在执行查询或者正在将结果发送给客户端。

Locked

在MySQL服务器层,该线程正在等待表锁。在存储引擎级别实现的锁,例如InnoDB 的行锁,并不会体现在线程状态中。

Analyzing and statistics

线程正在检查存储引擎的统计信息,并优化查询。

Copying to tmp table [on disk]

线程正在执行查询,并且将其结果集复制到一个临时表中,这种状态一般要么是在做 GROUP BY操作,要么是在进行文件排序操作,或者是在进行UNION操作。如果这 个状态后面还有“on disk”标记,那表示MySQL正在将一个内存临时表放到磁盘上。

Sorting result

线程正在对结果集进行排序。

了解这些状态的基本含义非常有用,这可以让你很快地了解当前“谁正在持球”。在一个繁 忙的服务器上,可能会看到大量的不正常的状态,例如,statistics正占用大量的时间。这 通常表示,某个地方有异常了。

MySQL如何对查询进行优化

语法解析器和预处理

MySQL通过关键字将SQL语句进行解析,并生成一棵对应的“解析树,MySQL解析器将进行MySQL语法规则验证和解析查询。它将验证是否使用了错误的关键字,使用关键字的顺序是否正确,或者它还会验证引号是否能前后正确匹配。

预处理器检查生成的解析树,以查找解析器无法解析的其他语义,例如,检查数据表和数据列是否存在,还会解析名字和别名,看看它们是否有歧义。

下一步预处理器会验证权限。这通常很快,除非服务器上有非常多的权限配置。

此时解析树是合法的了,然后,查询优化器将解析树转化成查询执行计划

查询优化器

查询优化器会为一个查询找到最好的执行方式.

用什么来衡量"最好"呢?

MySQL使用基于成本的优化器,它将尝试预测一个查询使用某种执行计划时的成本,并

选择其中成本最小的一个。

优化器在评估成本的 时候主要考虑每个表或者索引的页面个数、索引的基 数(索引中不同值的数量)、索引和数据行的长度、索引分布情况。并不考虑任何层面的缓存带来的影响,它假设读取任何数据都需要一次磁盘I/O。