接手任何一个数据库实例,我最先翻的往往不是监控大盘,而是一份完整的巡检记录。前段时间整理历史归档时,翻到一份标注为“dballgts02e48-1”的报告文件,名字看着像随手生成的编号,里面却是对一个核心业务库做全量巡检的完整过程记录,从连接数、慢查询、索引失效到备份链路验证全都有。这份材料给了我一个很好的切入点,把数据库全量巡检这件事从“该看什么”到“怎么落地”完整梳理一遍,也顺便说说那些光看监控看不出来的坑。
先说明白一点,这里的“全量巡检”指的不是监控告警平台那种实时盯指标,而是以一定周期(比如每周、每月)对数据库做一次全面体检:检查实例运行状态、连接与会话、慢查询与执行计划、索引与统计信息、备份与复制链路、账号权限,以及那些容易在长期运行中悄悄恶化的隐性风险。监控解决的是“现在有没有问题”,巡检解决的是“接下来可能出什么问题”。两者配合,才能把一个库的底细摸清楚。
1. 拆解“dballgts02e48-1”这种编号背后的巡检场景
这种编号大概率是内部任务单或者工具自动生成的批次号,db代表数据库,all代表全量巡检,gts可能是分组或者任务类型,后面的02e48-1是日期序列或节点编号。不管它具体代表什么,核心信息就一个:这是一次针对全部实例、全部数据库的整体检查,不是只盯某个慢SQL或某个故障。很多团队没有专职DBA,数据库巡检总是被“没出故障就不管”的思路带着走,等到线上出问题才去救火,往往已经晚了。全量巡检的价值恰好在这里:它把排查动作提前到了故障发生之前。
全量巡检最典型的应用场景有三类。第一类是核心业务库的周期性体检,比如订单、支付、用户中心这类库,每次大促前做一轮全量巡检已经是标配动作。第二类是数据库版本升级或迁移前的基线评估,先把现有问题摸清楚,再决定要不要动。第三类是接手一个陌生实例时的摸底排查——检查完才知道这个库有哪些历史包袱、哪些配置不合理、哪些账号权限过大。每一类场景的侧重点不同,但整体思路一致:先收集信息,再判断风险,最后给出可执行的处置建议。
我在实际巡检中习惯按“实例层—数据库层—对象层”三个维度展开。实例层看运行状态、参数配置、内存与连接、日志错误;数据库层看空间使用、表增长趋势、碎片程度、复制延迟;对象层看索引使用情况、统计信息新鲜度、无效对象、权限配置。这样分层的好处是逻辑清晰,不会漏项,而且能快速定位问题属于哪个层面。比如连接数打满,先判断是实例层配置问题还是应用层连接泄漏,再往下排查具体是哪个会话占着不释放——分层思路能帮我们少走弯路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 巡检项设计:全量检查到底该盯哪些指标
很多刚接触巡检的人容易陷入两个极端:要么只查几个基础指标(CPU、内存、磁盘),要么把几百个监控项全部拉出来看一遍。前者漏掉太多隐性风险,后者信息过载,反而看不到重点。我自己的经验是,全量巡检的指标设计应该围绕“数据安全、可用性、性能、容量、权限”五个维度来定,每个维度挑出最核心的十几项,总计控制在五六十项左右,既能覆盖风险,又不至于淹没在数据里。
2.1 连接与会话:最容易暴露应用问题的入口
连接数异常是数据库最常见的前兆信号,可能是应用连接池配置不当、慢查询堆积导致会话不释放,也可能是有脚本在批量跑大事务。巡检时不能只看当前连接数是否打满,要看几个趋势类指标:过去一周连接数的峰值、平均值、波动曲线,以及当前会话按状态(active、idle in transaction、sleep)的分布情况。特别要注意“idle in transaction”状态的会话,这种会话持有事务锁但又不干活,是锁等待和事务日志膨胀的主要来源。
2.2 慢查询与执行计划:性能恶化的核心信号
慢查询日志是性能巡检的核心数据源。但仅仅统计“慢查询有多少条”远远不够,关键是看慢查询的同比和环比变化——同样的SQL,上周执行100毫秒,这周变成5秒,哪怕日志里只有一条,也说明执行计划或数据分布发生了重大变化。我每次巡检都会挑出TOP 20的慢查询,逐条看执行计划的变化,重点关注三类问题:全表扫面、索引失效、行估计误差过大。日常经验是,绝大多数慢查询恶化都跟统计信息过期或索引选择变化有关。
2.3 索引与统计信息:隐形的性能杀手
索引问题是最容易被巡检发现、又最容易被日常监控忽略的一类风险。上线时间越长的系统,索引问题越严重,常见的有:长期未使用的冗余索引、重复索引(两三个索引字段几乎一样)、选择性极低的低效索引,以及统计信息新鲜度超过阈值。冗余索引不仅占用磁盘空间,还拖慢DML写入性能,因为每次插入更新都要同步维护所有索引。巡检时我一般会生成索引使用报告,找出那些“读次数极少但写入却要一直维护”的索引,列为待确认清理对象。
2.4 备份链路与复制拓扑:最后一道防线
备份有效性验证是巡检中绝对不可跳过的一环。很多团队做了备份,但从没真正演练过恢复,备份文件能不能用、恢复到什么时间点、恢复需要多久,全是未知数。我见过最典型的案例:某实例每天做全量备份,但备份脚本在磁盘写满后连续三个月静默失败,因为监控只看备份任务是否执行成功,没看备份文件大小是否有变化,最终只能从更早的备份中恢复,丢了将近两个月的增量数据。巡检中必须核对备份文件的大小、时间戳、校验信息,并且至少每季度做一次真实恢复演练。
复制拓扑方面,除了主从延迟,还要检查复制链路是否单点、binlog/归档日志保留周期是否足够、从库是否被业务直连写入。遇到过不止一次:从库被某个报表任务写入了数据,主从一旦切换,数据就出现分叉,恢复起来非常痛苦。
3. 巡检阈值怎么定:经验值只是起点,不是标准答案
巡检脚本运行起来很简单,难的是怎么判断“这个值算不算异常”。不同的业务、不同的实例规格、不同的数据模型,正常的基线完全不一样。拿连接数来说,一个日活百万的业务库和一个内部OA库,同样的连接数指标含义完全不同。我的做法是分三层设定阈值:
第一层是硬性资源阈值。CPU使用率超过85%、内存使用率超过90%、磁盘使用率超过80%(尤其数据盘)、连接数使用率超过80%,这些属于不管什么业务都该关注的警戒水位。第二层是性能指标阈值。慢查询数量较上周翻倍、平均查询耗时上升超过50%、缓存命中率下降到95%以下、锁等待次数显著增多,这类数据需要结合历史趋势判断。第三层是业务定制阈值。比如订单表单日增长量超过预估的30%、用户登录接口的数据库耗时超过200毫秒,需要业务方参与设定。
不建议直接照搬网上流传的“最佳实践阈值”,因为实例规格和业务负载差异太大。更靠谱的方式是:先收集四周以上的历史数据,计算出各项指标的P95或P99分位值作为动态基线,再结合人工经验微调。比如某个库的CPU使用率长期在40%到60%之间波动,连续几天冲到80%以上就该标记告警;但如果一个库换了新硬件,CPU使用率长期只有10%,旧阈值就完全失去意义。
还有一个容易踩的坑是阈值只设上限不设下限。连接数过低有时候也是问题——一个业务高峰时段连接数只有峰值的四分之一,大概率是应用连接池配置有问题,或者业务量下跌需要确认是否正常。空间使用率同理,某些表长期不增长可能意味着写入链路中断,数据没进来,这比空间满更可怕。
4. 把全量巡检变成可复用流程:从手工到自动化的落地路径
很多团队巡检还在靠人肉执行:登录服务器执行几个命令,把结果复制进Excel,再人工判断有没有异常。这种做法不仅效率低,而且容易漏项——某个命令忘了执行、某行输出看走眼、某次结果忘记归档,都很常见。把巡检脚本化和自动化并不需要很复杂的平台支撑,一个简单的调度任务加一个报告生成脚本就能解决大部分问题。
4.1 巡检脚本的核心结构
我常用的巡检脚本分四步:采集、计算、比对、输出。采集阶段从information_schema、performance_schema、pg_stat_statements(如果用的PostgreSQL)或系统视图里拉取原始数据;计算阶段做聚合统计,算出均值、峰值、环比变化;比对阶段把当前值和基线值做对比,超过阈值就标记为异常;输出阶段生成结构化的Markdown或HTML报告,列出所有巡检项、实测值、基线值、判定结果和处理建议。
以MySQL为例,几个高频使用的巡检SQL包括:
sql复制-- 连接数使用率
SELECT
VARIABLE_VALUE AS max_connections
FROM performance_schema.global_variables
WHERE VARIABLE_NAME = 'max_connections';
SELECT
COUNT(*) AS current_connections,
SUM(command = 'Sleep') AS sleep_connections,
SUM(state = 'active') AS active_connections
FROM information_schema.processlist;
-- 慢查询数量(按天统计最近7天)
SELECT
DATE(start_time) AS query_date,
COUNT(*) AS slow_query_count
FROM performance_schema.events_statements_summary_by_digest
WHERE timer_wait / 1000000000 > 1000000
AND start_time >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE(start_time);
这里有个细节:统计慢查询时,不要把单位弄混了。performance_schema里的timer_wait单位是皮秒,需要除以10的12次方才是秒;而events_statements_summary_by_digest表里没有start_time字段,需要结合events_statements_history_long来统计。写成脚本的时候先做一轮数据抽样验证,确认单位换算正确再投入生产使用。
4.2 报告生成与归档
巡检报告的格式不用花哨,但必须包含三块:巡检范围和时间、每个巡检项的判定结果、异常项的详细数据和处置建议。我会在报告头部加一个汇总表——绿色表示正常、黄色表示关注、红色表示异常,这样打开报告第一眼就能知道这轮巡检的整体健康度。异常项除了给出当前值,还要附上近四期的历史值,方便判断是突变还是渐变。
报告归档同样重要。每轮巡检报告建议至少保留一年,因为排查某些疑难问题时,需要回溯几个月前的参数配置和数据指标。遇到过一种情况:某SQL三个月前一直走索引,某次变更后开始走全表扫描,但没人记得那次变更。后来翻历史巡检报告,发现在变更后那周的巡检记录里,索引使用报告已经有变化,只是当时没有深入排查。如果巡检数据经常被丢弃,这种追查根本无从谈起。
5. 一次真实巡检的复盘:当连接数平稳但业务却变慢
前面讲的都是方法论,这一节用一个真实案例把整个排查链路串起来。去年年中,某个核心业务库遇到一次诡异故障——从监控上看,CPU、内存、磁盘、连接数所有基础指标都在正常范围内,但业务方反馈接口越来越慢,平均响应时间从200毫秒涨到了3秒。如果不做全量巡检,光靠监控面板几乎无法定位问题。
5.1 从会话状态找到突破口
接到反馈后,我第一件事不是看慢查询日志,而是先看当前活跃会话的状态分布。结果发现一个反常现象:连接数虽然不高,但相当一部分会话处于“Waiting for table metadata lock”状态。这意味着有DDL操作在等待元数据锁,而持有锁的会话一直没释放,后续所有涉及该表的查询都在排队等待。
顺着持有锁的会话往上追,发现一个设置了autocommit=0的脚本连接,在凌晨执行一批更新操作后,事务没有提交也没有回滚,一直挂在那边。从应用日志看,这个脚本因为一次网络超时报错退出,但数据库端的连接没有断开,事务就一直悬着。
5.2 全量巡检如何提前发现这个隐患
事后复盘时,我翻了巡检记录,发现其实有两处信号在几天前就已经出现,只是当时没当回事:一是事务持续时间超过1小时的会话数比上周多了几个,但绝对数值很小,没有触发告警阈值;二是information_schema.innodb_trx里能看到有几个事务的trx_started时间很早,但当时没手动关联查询。
这个教训后来直接变成了巡检项:每次巡检增加“长事务会话”检查,直接查information_schema.innodb_trx,筛选出持续超过10分钟的事务,逐一确认是否有对应业务在跑。同时增加“全局锁等待”检查,通过performance_schema的等待事件统计,查看是否存在长时间未释放的MDL锁。这两个巡检项成了后续所有实例的标配。
5.3 根因处置与预防
当时的处置过程分三步:先找出持有MDL锁的会话ID,通过KILL命令终止异常连接;再脚本批量查出了所有未提交的孤儿事务,逐个确认后回滚;最后修改了该脚本的配置,在数据库连接参数中增加wait_timeout和interactive_timeout的约束,让异常连接能自动断开。
但更关键的是流程修复:在应用侧的批处理框架里增加事务超时强制回滚机制,避免因为网络抖动导致的事务悬挂。数据库侧再怎么巡检,也只能发现已经存在的问题,真正解决这类问题需要巡检发现之后,推动应用开发把异常路径彻底堵上。这也是巡检报告不能只停在“发现了什么”,还要写清“建议谁在什么时间点去改什么”的原因。
6. 巡检报告的解读逻辑:三项优先级排序
巡检执行完、报告生成之后,最怕的是“什么都标红,什么都处理”。如果一份巡检报告里有几十个异常项,管理层和研发都会失去判断重心,最终就是每一项都没人真正跟进。我习惯在报告里把异常项按三个优先级排序,顺便给出处理建议。
P0级是必须立即处理的,通常包含:备份链路异常(备份失败、备份文件校验不过)、复制中断且延迟持续增长、数据文件损坏、权限漏洞(如弱密码、超级管理员权限外泄)、磁盘即将写满。这类问题一旦发酵,直接影响数据安全和可用性,必须当天处理。P1级是近期需要处理的,比如慢查询恶化、长事务频繁出现、统计信息严重过期、冗余索引影响写入性能。这类问题暂时不影响运行,但会持续消耗系统资源,通常在一到两周内安排处理。P2级是持续优化的,比如某些参数配置可以更合理、部分表的数据类型设计不佳、某些历史数据可以归档。这类问题放在常规迭代节奏里推进即可。
有个容易被忽略的点:巡检报告不仅给DBA看,还要给应用研发和运维看。不同角色的关注角度不同,DBA关注实例和资源指标,研发关注慢SQL和线上问题,运维关注容量与扩缩容。如果报告只写技术指标而不提业务影响,研发很难排期处理。所以我在每个P1及以上异常项后都会补一句业务影响描述,比如“该慢查询导致商品列表接口P95延迟从300ms升到2.1秒,影响用户在活动页的浏览体验”,这样更可能获得其他团队的支持。
7. 一些踩过坑之后总结出来的巡检经验
做巡检这件事,方法可以学,但真正的经验往往来自踩坑。这里分享几个我在实际巡检中反复遇到过、也是网上文档里很少提及的细节。
第一,巡检脚本本身要纳入版本管理,且每次变更后要做基准测试。更新过一个巡检SQL的过滤条件,结果新版本把未提交事务全部漏掉了,整整一个月没发现任何异常。巡检脚本虽然不直接改业务数据,但它负责“发现问题”,一旦本身出错,相当于数据库裸奔。建议每次修改脚本后,先在测试实例上跑一遍,用旧版结果和新版结果做diff,确认差异符合预期再上线。
第二,全量巡检不要只盯高峰期数据,低峰期数据同样有参考价值。很多自动化巡检默认在凌晨跑,这本身没问题,但凌晨的数据不能代表白天高峰期的状态。比较好的方式是一天内多时段采样,比如早高峰、午间、傍晚各采一次,对比不同时段的关键指标差异,这样更容易发现“哪类任务把哪个时间段拖慢了”。
第三,巡检发现的问题必须闭环跟踪。见过太多巡检报告“周周发现同样的问题,月月挂在同一项”,时间长了大家都麻木了。我给团队定的规矩是:每轮巡检结束后七天内,逐一确认上一轮P0、P1级问题的处理状态;连续两轮出现的同一问题,直接升级,不再等着慢慢排期。发现问题只是起点,推动整改才是巡检真正的价值所在。
第四,存储过程和定时任务往往是巡检容易漏掉的盲区。很多业务逻辑不在应用代码里,而是藏在数据库的存储过程和事件调度器里。这些定时任务一旦写得不严谨(例如没有做幂等控制、没有异常捕获),会留下很多隐性风险。巡检时建议把每个实例的事件任务列表拉出来,逐条确认执行时间、执行时长、失败次数、最近一次执行结果,把异常任务纳入重点跟踪。
第五,也是我觉得最重要的一条:巡检的最终产出不是报告,而是对业务连续性的理解。一个负责任的巡检,应该在报告里回答几个问题:这个库能扛住当前业务的增长吗?数据安全是否有保障?有没有潜在的性能瓶颈会在特定时间点爆发?备份到底能不能恢复?把这些问题的答案写清楚,这份巡检才真正完成了它的使命。
就拿备份恢复来说,我始终建议把“每季度做一次真实的整库恢复演练”写进巡检流程。第一次做的时候,团队里有人觉得多此一举,直到某次恢复演练中发现备份文件因为加密密钥换过而无法解密,才意识到这个动作有多重要。如果那次不是演练而是真正的灾难恢复,后果可想而知。
整体来说,全量巡检是一项“平时看不出效果、关键时刻救命”的工作,它的效果不是上线一个功能那样立竿见影,而是靠每一轮认真执行逐渐建立起来的安全边界。把巡检当作一门规范流程来做,比临时抱佛脚的排查要省心太多。
