高情商说法: 数据库的可观测性
低情商说法: 数据库的监控告警
背景
今天帮同事排查客户一周前遇到的“web 前台登录卡死的问题”,客户在应用层没有找到原因,但在数据库监控仪表盘上看到一个异常的指标——Threads_running 高,当天晚上 20:00 - 21:20 之间监控曲线图有一个尖峰,从 0 涨到了 218,之后客户重启应用服务器,数据库恢复正常,问数据库 Threads_running 高的原因。
实际上,这是一个苦差事,分析当前发生的问题比较简单,但排查一个一周前发生的问题,这非常依靠监控系统有没有留有足够的线索。
目前,我们已知,一些辅助的线索是没有的,例如审计日志或者 general log 没有开启,无法知道案发现场的所有 SQL。
分析
在开始分析前,我先凭经验猜想 Threads_running 高可能有以下这几种情况:
当时并发线程数确实高,有流量高峰 当时有大量慢sql,引起雪崩效应 系统资源层有瓶颈,或者 innodb 层有瓶颈。 锁等待
1. 关于“当时并发线程数确实高,有流量高峰”
查看了当时监控的 TPS、QPS 曲线,没有明显的增长,TPS 压力甚至远低于白天,所以不是这个原因。
这里有个吐槽点,当时我看业务系统的 TPS、QPS 的采集频率是 2小时一次,这个对于性能分析没啥意义,我认为更合理的值是 10s,财力雄厚要求高的公司甚至可以设置 1s 一次
2. 关于“当时有大量慢sql,引起雪崩效应”
查看了慢查询日志,慢查询使用的阈值是记录执行两秒以上的慢查询,没有明显的慢查询现象,所以不是这个原因,系统也没有雪崩。
3. 关于“系统资源层有瓶颈,或者 innodb 层有瓶颈”
CPU 占用 20%,内存充裕没有使用到 swap,iops 非常低,客户的监控没有 iowait、io 饱和度监控指标(吐槽),但这么低的 iops 没理由有磁盘瓶颈,我这里就认为系统资源层没有遇到瓶颈。innodb 层简单了看了"open file"、"open table"等监控,没有遇到瓶颈。而且资源或数据库层有瓶颈,一般容易引发雪崩,我们当时并没有雪崩现象,所以不是这个原因。
4. 关于“锁等待”
排除了以上三个原因,锁等待这个原因导致的 Threads_running 高就非常大可能了。
官方文档对 Threads_running 的解释如下:
Threads_running
The number of threads that are not sleeping.
也就是非 sleep 状态的线程都属于 Threads_running 状态。
一旦一些查询遇到锁等待了,那他其实是不消耗 CPU 和 IO 资源的。所以完全符合我们 3 中的判断。
其中,锁等待一般又分为行锁等待和元数据锁等待(MDL锁等待)。
是锁等待?
不是。监控有通过show global status like 'Innodb_row_lock_time'
采集总的行锁时间,通过分析当时发生问题的 11 分钟内,只有 0.172 秒的行锁等待,这影响非常小,所以不是这个问题。
是元数据锁等待?
这个我无法确认。首先监控系统没有做长事务的监控告警,其次没做元数据锁等待的监控告警,再则我没有办法确认他们是否执行过 DDL 等需要争抢元数据锁的 SQL。MySQL 参数没有设置 log_slow_admin_statements =1
,设置了的话,慢的 DDL 或者 DBA 管理语句,例如 flush table with read lock
会记录进 slow.log。当然我们还是可以通过解析 binlog 确认是否有这些 DDL 行为,但无法确认flush table with read lock
(FTWRL)行为,所以意义也不大。问过业务方有没有执行过 DDL 或者备份程序(FTWRL),答案是没有,我信了,没有必要去调查了。
所以,我并没有找到根本原因。
结论
总之,巧妇难为无米之炊,这套业务系统的监控告警目前是有一些可优化的空间的,这些优化有助于我们日后通过监控系统回溯排查历史问题。
建议添加以下:
监控——iowait、io饱和度、长事务、元数据锁
告警:
io饱和度高 长事务多于XX条 元数据锁等待超过60s 五分钟内慢查询数大于xx个 并发线程数大于200 (建议值为逻辑核数 * 2)
最重要是最后这个"并发线程数大于200",有了他,我们当时会预警到问题,直接上数据库预防故障发生,并能通过 "show processlist" 等简单手段排查到原因。
我这么说后,同事决定添加了"并发线程数大于200" 这种告警,用于预发下次在发生。
最后补充一句,定时对数据库做"show processlist"、"show engine innodb status"快照保存下来,也是日后分析问题的利器,我自己运维是有这么干的。
Enjoy MySQL!