游戏开发

游戏服务端开发中的MySQL进阶知识(三)--慢查询

2021-09-16  本文已影响0人  higher2017

本系列文章主要是本人在游戏服务端开发过程中,遇到的一些不那么为人熟知但我又觉得比较重要的MySQL知识的介绍。希望里面浅薄的文字能为了提供一点点的帮助。

慢查询

从我有限的从业经验来看,大多数数据库导致的游戏服务器问题,十有八九慢查询。比如:SQL写的乱七八糟导致的慢查询、表数据太多导致的慢查询、没有使用索引导致的慢查询、突然大并发量导致的慢查询等等。数据库一出现以上这些问题,开发团队免不了要背大锅。所以我认为数据库慢查询应该成为每个服务器开发人员必知必会的知识点。

慢查询事故分享:

这里我分享一下我参与的第一次慢查询定位和优化经历,这是一次非常惨痛的经历:

当时我所在的项目组上线了一个零点秒杀的活动,秒杀活动开启的第一个晚上有大量玩家投诉卡死在活动界面。我们当时按直觉判断肯定是太多人秒杀导致游戏服务器压力过大处理不过来,然后就决定发公告宣布延迟活动和重启游戏服务器。重启服务器之后,玩家再次进行秒杀问题还是如此。这时有个同事说,压力应该主要在MySQL服务器上应该重启MySQL服务器。然后我们又又发公告宣布延迟活动 + 重启游戏服务器 + 重启MySQL服务器。可能是因为大部分玩家都放弃的原因吧,这次问题终于解决。

后期我们进行了复盘发现问题是:游戏服务器后台会在零点执行一个定时统计任务,其中包含许多复杂的SQL语句(各种join、全表查询等等)。刚好这些SQL所操作的表又和秒杀活动需要操作的表完全重合,最终导致数据库大量慢查询、MySQL服务器假死、线上玩家卡在秒杀界面。而出问题那会项目组成员甚至不知道慢查询是个什么东西,只是单纯认为是并发量太大导致服务器压力太大负载不下,然后在没有准确定位根本原因的情况下就天真地用重启游戏服务器和MySQL服务器来解决问题。下图说明了但是MySQL服务器面临的两方面的压力:

image.png

第一次我们只重启游戏服务器,非但没有减轻MySQL服务器的压力,反而导致其压力变得更大了。这是因为:

  1. 游戏服务器重启有大量在线玩家被强制下线,这些玩家的数据需要刷进数据库,加重服务器压力(本来上面那些慢查询还没执行完,现在又来新的了);
  2. 游戏服务器重启之后,零点执行的那个定时统计任务会自主判断是否要继续执行(由于上一次重启游戏服务器导致今天的任务中断,所以判断结果是还要执行一遍),这样对于MySQL而言就算是同时执行两个这个定时统计任务了。

在知道慢查询日志这个东西之后,我们还尝试去线上找这个日志来看看,但由于数据库默认不开启慢查询日志的打印,结果可想而知。不过我们还是模拟了当时线上的情况重新测试一遍(这次开了慢查询),表现和当时线上的状况一致。最终才将这个问题全部复盘。

有上面这个案例打底之后,接下就开始慢查询的介绍

慢查询是指那些执行时间过长且涉及行数过多的语句(一般超过配置的慢查询时间和涉及行数才算)。慢查询并不是一定是SELECT语句,其实所有的DML类型的SQL(INSERTUPDATEREPLACEDELETE)都可能是慢查询。根据官网的说明,慢查询日志的监听和打印可能会是一个耗时任务,所以MySQL服务器是默认不开启的慢查询。

慢查询配置(官方文档):

参数名 作用 默认值 备注
slow_query_log 是否开启slow log慢日志记录 OFF 默认关闭慢查询功能
slow_query_log_file slow log日志的路径 host_name-slow.log 注意:MySQL服务器要对指定的目录有写权限
long_query_time 当SQL语句执行时间超过该配置时间则算是慢查询,如果有开启慢查询日志的话就会将慢查询SQL记录到慢查询日志中。 10 该值的取值范围是0-10,最小精度单位是微秒(比如可以配成0.001),如果该值配成0那就是所有SQL都是慢查询。
min_examined_row_limit 当一个SQL语句扫描行数小于该值时,不会计入到慢查询中 0 可以屏蔽掉一些偶发性干扰的慢查询(比如一句十分简单且用了索引的select语句也会因为抖动或其他因素变为慢查询,而实际上是没问题的)
log_queries_not_using_indexes 不走索引的查询是否被记录 OFF 不走索引的查询是否被记录,即默认是不走索引就不记录慢查询日志
log_throttle_queries_not_using_indexes 不走索引被记录的语句条数阈值 0 当一分钟不走索引慢查询记录数据超过该值就不再记录,每分钟清0。
log_slow_admin_statements 对服务器管理语句是否进行记录 OFF 管理语句有:ALTER TABLE, ANALYZE TABLE, CHECK TABLE等等,默认不记录
log_output 指定慢查询日志的打印目的地 FILE 该值可选项有FILE,TABLE,NONE(低版本可能没有TABLE这个选项),可以是一个也可以包含多个(多个的话就用逗号分隔,如:TABLE, FILE)。 选择TABLE记录到mysql库中的 slow_log表中; 选择FILE记录到日志文件;NONE禁用日志记录,如果存在NONE则其他均无效。

以上可以通过SHOW VARIABLES LIKE '%***%'这种形式进行查询,并且可以通过SET GLOBAL ***=?来进行动态修改。这些变量均是全局变量,对MySQL所有库所有用户生效。
查询配置示例:

image.png

我在上面介绍information_schema库的时候就有说过,能用SHOW VARIABLES LIKE '%***%'查询到的东西基本上也可以在information_schema库查到。而且上面这些配置都是全局的,所以通过information_schema库的GLOBAL_VARIABLES表也能查到(某些版本的MySQL可能是在performance_schema库中,可以通过:SELECT table_schema FROM information_schema.TABLES WHERE table_name = 'GLOBAL_VARIABLES'来查询到底在哪个库),如下图:

image.png
image.png

慢查询的判断流程:

这里想强调的是:MySQL服务器是否打印慢查询日志,不是只通过SQL语句的执行速度来判断的,还和上面这些参数有关(源码请戳)。这里上一张判断流程图说明一下:

image.png
判断是否要打印慢查询日志的方法在log.cc中:
image.png

日志格式(官方文档):

我把long_query_time设置为0(SET GLOBAL long_query_time=0),打印格式设置为FILE和TABLE两种模式(SET GLOBAL log_output='FILE,TABLE')。下面截图分别是慢查询日志和慢查询表的数据:

慢查询日志.png 慢查询表.png

这里说明一下几个关键的字段:

注意不管是慢查询日志还是慢查询表,记录的信息都是一条一条的慢查询SQL语句。这样的形式直接进行分析会非常麻烦(比如想知道某一条慢查询具体的耗时、锁持有时间,或者对某一张表的慢查询分析)。这里介绍一下业界比较常用的慢查询日志分析工具:

image.png

业界对慢查询的通用的处理流程:

如何避免和解决慢查询:

如何避免:

当数据库压力过大时(CPU和I/O),更容易出现慢查询。本来一些正常的操作也会因为阻塞而变成慢查询。有效降低数据库压力就是一个标本兼治的方案,慢查询的避免倒是顺带的。降低数据库压力有很多种,这里介绍下我认为比较有效的几种给大家参考一下:




如何解决:

上面说了如何预防慢查询,这里说下如果出现慢查询怎么办。

上一篇 下一篇

猜你喜欢

热点阅读