我有一个位于MySQL 5.5数据库(INNODB)之上的服务。该服务有一个后台工作,应该每周运行一次。在高层次上,后台工作执行以下操作:
UMQ - 丑陋的怪物查询:这是一个讨厌的数据库查询,它连接了一堆表,在其中几个表中的列上有条件,并包含一个带有更多连接和条件的NOT EXISTS子查询。 UMQ包括ORDER BY也有LIMIT 1000.即使查询很糟糕,我已经完成了我在这里所做的事情 - 所有列上的索引都被过滤掉了,并且连接都是外键关系。
我确实希望UMQ很重,需要一些时间,这就是为什么它在后台工作中执行的原因。但是,我所看到的是性能迅速降低,直到最终导致我的服务超时(10次迭代后可能慢50倍)。
首先我认为这是因为UMQ查询的数据发生了变化(参见上面的步骤4),但这不是因为如果我从慢查询日志中获取最后一个查询(导致超时的数据)并执行它本人直接我得到了相同的行为,直到我重申MySQL服务。重新启动之后,对重新开始之前> 30秒的完全相同数据的确切查询现在花费<0.5秒。我每次都可以通过将数据库恢复到初始状态并重新启动过程来重现此行为。
此外,使用此question中描述的技巧,我可以看到查询在重新启动后扫描大约60K行,而不是之前的18M行。 EXPLAIN告诉我应该扫描大约10K行,并且EXPLAIN的结果总是相同的。没有其他进程同时访问数据库,慢查询日志中的lock_time始终为0.重启前后的SHOW ENGINE INNODB状态没有提示。
所以最后一个问题:有没有人知道为什么我会看到这种行为?我该如何进一步分析?
我觉得我需要以某种方式对MySQL进行不同的配置,但我已经疯狂地进行搜索和测试,而没有提出任何有所作为的东西。
答案 0 :(得分:2)
事实证明,我看到的行为是MySQL优化器如何使用InnoDB统计数据来决定执行计划的结果。 This article让我走上正轨(尽管它并没有完全讨论我的问题)。我从中学到的最重要的事情是MySQL在启动时计算统计数据,然后偶尔计算一次。然后使用此统计信息优化查询。
我设置测试数据的方式表 T ,其中大多数写入在步骤4中完成,开始为空。每次迭代后, T 将包含越来越多的记录,但InnoDB统计数据尚未更新以反映这一点。因此,MySQL优化器总是选择UMQ的执行计划(其中包括 T 的JOIN),当 T 为空时效果很好但是越来越差,记录越多<强> T 包含。
为了验证这一点,我在每次执行UMQ之前添加了 ANALYZE TABLE T; ,并且快速降级消失了。没有闪电性能但可以接受。我还看到离开数据库半小时左右(可能有点短但至少超过几分钟)将允许InnoDB统计数据自动刷新。
在实际情况中,UMQ中涉及的表的索引基数的相对差异看起来会有很大不同,并且不会快速变化,所以我决定我不需要对它做任何事情。
答案 1 :(得分:0)
非常感谢您的分析和回答。在mariadb 10.1和bacula服务器9.4(Debian Buster)上的ci期间,我一直在搜索此问题几天。
这种情况是,在CI周期中全新安装服务器后,前两个测试(备份和还原)在未重启的mariadb服务器上可以顺利运行,只有第三个测试表明一个特定的UMQ花费了大约20分钟(在从具有约3万行的表中还原过程。
除非重新启动了mardiadb服务器或已分析表,否则问题不会消失。 ANALYZE TABLE
或重新启动完全按照链接文章中的说明更改了字段的基数和内部查询处理。