uu2018-pic-banner

Mysql性能优化之Mysql慢查询的意义和如何分析慢查询

时间:2021-12-09

优化方向

硬件和OS调优 、MySql调优 、架构优化。对架构的优化对mysql的性能优化收益最高。本文主要讨论架构优化。

架构优化

在系统设计时首先需要充分考虑业务的实际情况,例如排行榜的相关业务可以考虑迁移到redis中去做, 地理位置相关的需求放到mongodb中去做,有搜索需求放到ElasticSearch中去做,大数据相关的放到HBase中去做, 对数据一致性和事物有要求的,优先选择MySql。采用更适合业务场景的架构能最大程度地提 升系统的扩展性和可用性。

MySql调优流程

确认业务表结构设计是否合理,SQL 语句优化是否足 够,该添加的索引是否都添加了,是否可以剔除多余的索引等等。

确定系统、硬件有哪些地方需要优化,系统瓶颈在哪里,哪些系统参数 需要调整优化,进程资源限制是否提到足够高;在硬件方面是否需要更换为具有 更高 I/O 性能的存储硬件,是否需要升级内存、CPU、网络等。

查询性能优化

线上环境发现数据库卡顿,需要首先分析慢查询log, 定位慢查询的sql, 优化对应的sql。

什么是慢查询  

顾名思义,就是查询花费大量时间的日志,是指 mysql 记录所 有执行超过 long_query_time 参数设定的时间阈值的 SQL 语句的日志。该日志能 为 SQL 语句的优化带来很好的帮助。

慢查询日志

默认情况下,慢查询日志是关闭的,要使用 慢查询日志功能,首先要开启慢查询日志功能。

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

查询性能低下最基本的原因是访问的数据太多。大部分性能低下的查询都可 以通过减少访问的数据量的方式进行优化。对于低效的查询,

一般通过下面两个 步骤来分析总是很有效:

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

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

请求了不需要的数据

有些查询会请求超过实际需要的数据,然后这些多余的数据会被应用程序丢 弃。这会给 MySQL 服务器带来额外的负担,并增加网络开销,

另外也会消耗应 用服务器的 CPU 和内存资源。

查询不需要的记录

一个常见的错误是常常会误以为 MySQL 会只返回需要的数据,实际上 MySQL 却是先返回全部结果集再进行计算。我们经常会看到一些了解其他数据库系统的人会设计出这类应用程序。这些开发者习惯使用这样的技术,先使用 SELECT 语句查询大量的结果,然后获取前面的 N 行后关闭结果集(例如在新闻 网站中取出 100 条记录,但是只是在页面上显示前面 10 条)。他们认为 MySQL 会执行查询,并只返回他们需要的 10 条数据,然后停止查询。实际情况是 MySQL 会查询出全部的结果集,客户端的应用程序会接收全部的结果集数据,然后抛弃 其中大部分数据。最简单有效的解决方法就是在这样的查询后面加上 LIMIT。.

总是取出全部列

每次看到 SELECT*的时候都需要用怀疑的眼光审视,是不是真的需要返回全 部的列?很可能不是必需的。取出全部列,会让优化器无法完成索引覆盖扫描这 类优化,还会为服务器带来额外的 I/O、内存和 CPU 的消耗。因此,一些 DBA 是 严格禁止 SELECT *的写法的,这样做有时候还能避免某些列被修改带来的问题。 什么时候应该允许查询返回超过需要的数据?如果这种有点浪费数据库资 源的方式可以简化开发,因为能提高相同代码片段的复用性,如果清楚这样做的 性能影响,那么这种做法也是值得考虑的。如果应用程序使用了某种缓存机制, 或者有其他考虑,获取超过需要的数据也可能有其好处,但不要忘记这样做的代 价是什么。获取并缓存所有的列的查询,相比多个独立的只获取部分列的查询可 能就更有好处。

重复查询相同的数据

不断地重复执行相同的查询,然后每次都返回完全相同的数据。比较好的方 案是,当初次查询的时候将这个数据缓存起来,需要的时候从缓存中取出,这样 性能显然会更好。

是否在扫描额外的记录

在确定查询只返回需要的数据以后,接下来应该看看查询为了返回结果是否 扫描了过多的数据。对于 MySQL,最简单的衡量查询开销的三个指标如下: 响应时间、扫描的行数、返回的行

响应时间

响应时间是两个部分之和:服务时间和排队时间。 服务时间是指数据库处理这个查询真正花了多长时间。 排队时间是指服务器因为等待某些资源而没有真正执行查询的时间—-可能 是等 I/O 操作完成,也可能是等待行锁,等等。当你看到 一个查询的响应时间的时候,首先需要问问自己,这个响应时间是 否是一个合理的值。概括地说,了解这个查询需要哪些索引以及它的执行计划是 什么,然后计算大概需要多少个顺序和随机 I/O,再用其乘以在具体硬件条件下一次 I/O 的消耗时间。最后把这些消耗都加起来,就可以获得一个大概参考值来判 断当前响应时间是不是一个合理的值。

扫描的行数和返回的行数

分析查询时,查看该查询扫描的行数是非常有帮助的。这在一定程度上能够 说明该查询找到需要的数据的效率高不高。 理想情况下扫描的行数和返回的行数应该是相同的。

但实际情况中这种“美 事”并不多。例如在做一个关联查询时,服务器必须要扫描多行才能生成结果集 中的一行。扫描的行数对返回的行数的比率通常很小,一般在 1:1 和10:1 之间, 不过有时候这个值也可能非常非常大。

扫描的行数和访问类型

在评估查询开销的时候,需要考虑一下从表中找到某一行数据的成本。 MySQL 有好几种访问方式可以查找并返回一行结果。有些访问方式可能需要扫描 很多行才能返回一行结果,也有些访问方式可能无须扫描就能返回结果。在 EXPLAIN 语句中的 type 列反应了访问类型。访问类型有很多种,从全表 扫描到索引扫描、范围扫描、唯一索引查询、常数引用等。这里列的这些,速度 是从慢到快,扫描的行数也是从小到大。你不需要记住这些访问类型,但需要明 白扫描表、扫描索引、范围访问和单值访问的概念。

如果查询没有办法找到合适的访问类型,那么解决的最好办法通常就是增加 一个合适的索引,为什么索引对于查询优化如此重要了。索引让 MySQL 以最高 效、扫描行数最少的方式找到需要的记录。 一般 MySQL 能够使用如下三种方式应用 WHERE 条件,从好到坏依次为

在索引中使用 WHERE 条件来过滤不匹配的记录。这是在存储引擎层完成 的。使用索引覆盖扫描(在 Extra 列中出现了 Using index)来返回记录,直接 从索引中过滤不需要的记 录并返回命中的结果。这是在 MySQL 服务器层完成的, 但无须再回表查询记录。从数据表中返回数据,然后过滤不满足条件的记录(在 Extra 列中出现 Using Where)。这在MySQL 服务器层完成,MySQL 需要先从数据表读出记录然 后过滤。

好的索引可以让查询使用合适的访问类型,尽可能地只扫描需要的数据行。 如果发现查询需要扫描大量的数据但只返回少数的行,那么通常可以尝试下 面的技巧去优化它:

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

改变库表结构。例如使用单独的汇总表。

重写这个复杂的查询,让 MySQL 优化器能够以更优化的方式执行这个 查询。

重构查询的方式

在优化有问题的查询时,目标应该是找到一个更优的方法获得实际需要的结 果。 而不一定总是需要从 MySQL 获取一模一样的结果集。有时候,可以将查 询转换一种写法让其返回一样的结果,但是性能更好。一个复杂查询还是多个简单查询。如果一个查询的需求很复杂,Sql语句很长,考虑把查询语句拆分,拆成多个查询语句,在应用层汇总 数据, 这样做的好处提高sql阅读和维护。

分解关联查询  

很多高性能的应用都会对关联查询进行分解。简单地,可以对每一个表进行 一次单表查询,然后将结果在应用程序中进行关联。

1. 让缓存的效率更高。许多应用程序可以方便地缓存单表查询对应的结果对象。 将查询分解后,执行单个查询可以减少锁的竞争。

2. 在应用层做关联,可以更容易对数据库进行拆分,更容易做到高性能和可扩 展。查询本身效率也可能会有所提升。

3. 可以减少冗余记录的查询。在应用层做关联查询,意味着对于某条记录应用 只需要查询一次,而在数据库中做关联查询,则可能需要重复地访问一部分数据。

这样的重构还可能会减少网络和内存的消耗。

4. 更进一步,这样做相当于在应用中实现了哈希关联,而不是使用 MySQL 的 嵌套循环关联。某些场景哈希关联的效率要高很多。

在很多场景下,通过重构查询将关联放到应用程序中将会更加高效,这样的 场景有很多,比如:当应用能够方便地缓存单个查询的结果的时候、当可以将数 据分布到不同的 MySQL 服务器上的时候、当能够使用 IN()的方式代替关联查询 的时候、当查询中使用同一个数据表的时候。

慢查询配置

我们已经知道慢查询日志可以帮助定位可能存在问题的 SQL 语句,从而进行 SQL 语句层面的优化。但是默认值为关闭的,需要我们手动开启。

查看慢查询是否开启 show VARIABLES like 'slow_query_log';    如果off 则是关闭 需要开启。

慢查询开启: 

set GLOBAL slow_query_log=1; 再次调用上个命令查看是否开启成功。

show VARIABLES like '%long_query_time%';  运行时间超过该值的所有 SQL 语句都记录到慢查询日志中。

set  long_query_time=0; 设置慢查询阈值,这里设置0是测试值, 实际设置值需要调整。

对于没有运行的 SQL 语句没有使用索引,则 MySQL 数据库也可以将这 条 SQL 语句记录到慢查询日志文件,控制参数是:  

show VARIABLES like '%log_queries_not_using_indexes%';

set GLOBAL log_queries_not_using_indexes=1; 开启

慢查询日志输出位置

show VARIABLES like 'log_output';

小结

slow_query_log 启动停止慢查询日志 

slow_query_log_file 指定慢查询日志得存储路径及文件(默认和数据 文件放一起) 

long_query_time 指定记录慢查询日志 SQL 执行时间得伐值(单位: 秒,默认 10 秒) 

log_queries_not_using_indexes 是否记录未使用索引的 SQL

log_output 日志存放的地方可以是[TABLE][FILE][FILE,TABLE]

慢查询解读分析

日志格式

tail -f /var/lib/mysql/huangbingbing-Shangqi-X4270-slow.log // 这文件时执行slow_query_log_file 取的文件名

# Time: 2021-12-05T05:27:02.408009Z   // 查询执行时间

# [email protected]: root[root] @ localhost [] Id: 14 // 用户名 、用户的 IP 信 息、线程 ID 号

# Query_time: 0.000329 Lock_time: 0.000171 Rows_sent: 0 Rows_examined: 0

// Query_time 执行花费的时长(ms )Lock_time 执行获得锁的时长 Rows_sentc 获 得的结果行数 Rows_examined 扫描的数据行数

SET timestamp=1638682022;//这 SQL 执行的具体时间

select * from student where sid=1000;//具体sql语句

慢查询分析

慢查询的日志记录非常多,要从里面找寻一条查询慢的日志并不是很容易的 事情,一般来说都需要一些工具辅助才能快速定位到需要优化的 SQL 语句,下面介绍慢查询辅助工具 mysqldumpslow 常用的慢查询日志分析工具,汇总除查询条件外其他完全相同的 SQL,并将 分析结果按照参数中所指定的顺序输出。

主要参数如下

语法: sudo mysqldumpslow -s r -t 5 /var/lib/mysql/huangbingbing-Shangqi-X4270-slow.log (sudo是我在ubuntu执行的 其他linux 不需要sudo)

-s order (c,t,l,r,at,al,ar)

c:总次数

t:总时间

l:锁的时间

r :获得的结果行数 at,al,ar :指 t,l,r 平均数 【例如:at = 总时间/总次数】

-s 对结果进行排序,怎么排,根据后面所带的 (c,t,l,r,at,al,ar),缺省为 at

-t NUM just show the top n queries:仅显示前 n 条查询

-g PATTERN grep: only consider stmts that include this string:通过 grep 来 筛选语句。

执行结果

Reading mysql slow query log from /var/lib/mysql/huangbingbing-Shangqi-X4270-slow.log
Count: 2 Time=0.00s (0s) Lock=0.00s (0s) Rows=19.0 (38), root[root]@localhost
show tables
Count: 1 Time=0.00s (0s) Lock=0.00s (0s) Rows=12.0 (12), root[root]@localhost
desc slow_log
Count: 2 Time=0.00s (0s) Lock=0.00s (0s) Rows=5.0 (10), root[root]@localhost
show databases
Count: 6 Time=0.00s (0s) Lock=0.00s (0s) Rows=1.0 (6), root[root]@localhost
show variables like 'S'
Count: 5 Time=0.00s (0s) Lock=0.00s (0s) Rows=0.8 (4), root[root]@localhost
show VARIABLES like 'S'

总结:

掌握了Mysql慢查询的意义和如何分析慢查询,为执行计划学习做准备,下一文章将分析MySql执行计划。

    收藏

    发表评论 快来秀出你的观点