1. 先从两个“地基”说起:为什么数据库巡检必须有一套固定动作
日常维护数据库这件事,很多人会陷入两个极端:要么是“等报警了才上去看”,要么是“天天盯着几十个指标看,看到最后脑子一团浆糊”。我在一线摸爬滚打了十几年,最大的体会是——数据库的日常健康检查,本质上不是“会不会用命令”的问题,而是“有没有一套固定动作”的问题。尤其是同时维护 Oracle 和 MySQL 两套体系的团队,业务请求先打到 MySQL,财务核算、核心总账再落到 Oracle,哪个环节出问题都会引发连锁反应。这时候,一套能快速执行的“运行状态检查清单”就特别重要。
这篇东西适合谁看?我觉得有三类人最需要:第一类是刚接手数据库运维、还没形成自己检查套路的人;第二类是后端开发,自己负责的服务连不上库、或者接口响应突然变慢,希望有办法快速定位是数据库的问题还是代码的问题;第三类是团队里需要定期出巡检报告、但又不想把大量时间耗在重复命令上的人。文章里我不会去讲怎么安装数据库、怎么调参数优化那种大而全的内容,只聚焦一件事:当你站在一台数据库服务器面前,如何在几分钟内判断它到底健不健康,以及问题大概出在哪个方向。
为什么把 Oracle 和 MySQL 放在一起讲?因为这两个库虽然血缘不同、命令不同、设计哲学也不同,但作为“有状态的服务进程”,它们健康检查的关注点其实是共通的:进程在不在、端口通不通、能不能建立新会话、SQL 跑得顺不顺、磁盘空间够不够、日志里有没有报错。把两套命令对照着看,你会发现规律惊人地一致,记起来也轻松很多。
我的建议是,把下面这套检查流程整理成你自己团队的一个文档或脚本模板,每次巡检照做,遇到问题再按“从外到内、从进程到日志、从资源到等待事件”的顺序去排查。这套思路才是真正的核心资产,命令只是工具。
2. MySQL 快速检查:进程、连接与 InnoDB 三件套
2.1 第一件事:确认服务和端口真的活着
MySQL 的检查,第一步永远不是登进数据库里看 SQL,而是先确认最外层的东西——进程和端口。很多人在应用端看到“无法连接数据库”就开始怀疑账号密码、怀疑防火墙,结果一查,进程早就挂了或者端口被占用了。这个最基本的检查一定要养成肌肉记忆,不需要任何工具,两条命令就能完成。
# 检查进程是否存在 ps -ef | grep mysqld | grep -v grep # 检查端口是否在监听 ss -lntp | grep 3306我见过的最尴尬的现场就是:应用报“数据库连接失败”,一堆人围着看网络策略、看账号权限,最后发现 mysqld 进程不知道什么时候被 OOM killer 干掉了,systemd 还没来得及拉起来。所以“进程 + 端口”这两条命令,是所有后续检查的前提,而且顺序不能反——先看进程,再看端口。如果进程没了,端口肯定不会有,这时候应该去看启动日志,而不是在网络上浪费时间。
这里要特别提醒一点:MySQL 的默认端口 3306 是可以改的,不一定就是 3306。实际操作时,最好先看实例的配置文件确认端口,或者用grep port /etc/my.cnf这种命令去查,而不是想当然。我踩过这个坑,在客户现场按默认端口查了半天,发现人家改成了 3307。
如果确认进程在、端口也在监听,接下来才是真正“登库”检查的环节。我喜欢用一行命令把所有基础信息先拉出来:
mysql -uroot -p -e "status;"这一条命令能看到服务端的版本、运行时长、当前连接数、字符集、线程数等信息。特别是看到Uptime那一项,如果显示只有几分钟,说明这个实例刚刚重启过——这是非常重要的信号,意味着系统曾经崩溃过、被手动重启过,或者服务器发生过掉电。这种“重启痕迹”比任何慢查询都值得警惕,因为它往往意味着之前有过一次没有被记录的故障。
2.2 第二件事:连接数卡不卡脖子
MySQL 本身是单进程多线程模型,所有客户端连接都会占用线程和内存资源。连接数一旦被打满,应用端就会报“Too many connections”,而且这个错误往往不是逐渐出现的,是突然就全挂了。所以连接数是日常巡检里最高优先级的指标。
-- 查看当前连接数、最大连接数以及连接使用率 SHOW STATUS LIKE 'Threads_connected'; SHOW VARIABLES LIKE 'max_connections'; -- 查看各客户端 IP 的连接数量排行 SELECT SUBSTRING_INDEX(host, ':', 1) AS ip, COUNT(*) AS cnt FROM information_schema.processlist GROUP BY ip ORDER BY cnt DESC;我在实际运维中常用的一个经验值是:Threads_connected 达到 max_connections 的 80% 就该拉响黄色预警,90% 以上必须马上处理。为什么不是等到 100%?因为连接数是瞬时值,业务高峰可能有抖动,如果你在 100% 的悬崖边才开始处理,往往已经来不及了。另外,不要只看总连接数,还要看看有没有某个 IP 或者某个账号把连接占满了——这种情况通常是应用侧连接池配置过大、或者有请求长时间挂着不释放导致的。
连接打满之后,很多人的第一反应是调大 max_connections。这个思路本身没有错,但要注意:每多一个连接,MySQL 就要多分配一块线程栈和排序缓存等内存。如果你的服务器内存本身就紧张,调大连接数可能会让系统更快进入 swap,反而加剧问题。正确的做法是:先看是不是有连接被 hang 住(比如行锁等待、慢查询堆积),把异常的会话 kill 掉,再从应用侧限制连接池的大小。调参永远排在问题定位之后,而不是之前。
2.3 第三件事:慢查询与 InnoDB 状态怎么看
连接数正常之后,重点就要放在“SQL 跑得快不快”和“存储引擎有没有出问题”上。MySQL 从 5.7 开始默认开启了慢查询日志(long_query_time 默认 10 秒),但实际情况中很多慢查询远不到 10 秒就已经拖垮业务了。我的习惯是:在巡检时把阈值临时调低到 2 秒,观察几分钟,重点抓那些平时不会进慢日志、但确实影响体验的 SQL。
-- 临时调整慢查询阈值(重启失效),只对当前会话后的新查询生效 SET GLOBAL long_query_time = 2; -- 查看当前正在执行的 SQL,重点找 State 为 Sending data 或 Waiting for table lock 的会话 SHOW PROCESSLIST; -- 完整版:按时间排序,找到最耗时的会话 SELECT id, user, host, db, command, time, state, LEFT(info, 200) AS sql_text FROM information_schema.processlist WHERE command != 'Sleep' ORDER BY time DESC;SHOW PROCESSLIST这条命令可以说是 MySQL 巡检里最有价值的一条。它能把所有正在执行的会话状态都列出来,包括卡了多久、在做什么、正在执行什么 SQL。我见过太多次“数据库卡死”的场景,执行这条命令后真相大白——一条忘了带 where 条件的 update 语句正在全表更新,把 InnoDB 的行锁和 undo log 全部打爆,后面所有正常的查询全部堵在锁等待上。
InnoDB 存储引擎的健康状态,也有一个专门的可视化命令——SHOW ENGINE INNODB STATUS。输出内容很长,第一次看的人可能会懵,但实际巡检只需要看几个关键段落:TRANSACTIONS部分有没有大量历史事务没提交,SEMAPHORES部分有没有超过一两秒的等待锁信号量,LOG部分的 log sequence number 是否在正常推进。如果 LSN 长时间不动,说明 redo 写入出了问题,那基本就是磁盘 IO 卡死或者存储故障了。另外,检查一下 Buffer pool 命中率是很多人的习惯,但我个人觉得这个指标日常参考意义不大,命中率低只能说缓存不够热,并不代表实例故障,不需要过度关注。
3. Oracle 快速检查:监听、实例与告警日志
3.1 监听器状态与端口连通性
Oracle 的体系比 MySQL 重得多,从客户端到数据库要经过一层独立的“监听器”进程(Listener),这层是 MySQL 没有的,所以 Oracle 巡检的第一步其实是“双进程检查”:实例进程和监听进程都要看。实例进程通常叫ora_pmon_<SID>,监听进程叫tnslsnr。如果 pmon 不在,实例可能已经崩了;如果 tnslsnr 不在,应用也连不上数据库。
# 查看实例进程(重点确认 pmon / smon / dbwr / lgwr 是否存在) ps -ef | grep ora_pmon # 查看监听进程 ps -ef | grep tnslsnr # 通过监听管理工具查看服务状态 lsnrctl statuslsnrctl status这条命令输出里信息量很大:监听器的启动时间、监听端口、以及注册到这个监听器下的所有服务名(Service Name)。很多应用连不上 Oracle,报的都是 listener 相关的错误,所谓“监听服务无法启动”是热词里出现频率很高的一个问题。这里我分享一下快速定位的思路:先看 listnener.ora 文件里配置的端口是不是被别的进程占用了,再用lsnrctl start看启动日志提示。最常见的两个坑,一个是主机名解析失败导致监听起不来,另一个是/etc/hosts里主机名和hostname命令结果不一致。
既然提到端口,我顺便说一句巡检小技巧:Oracle 的标准端口是 1521,但一台服务器上可能装了多个实例、多个监听,端口可能各不相同。检查时最好用lsnrctl status直接看它在监听哪个端口,然后配套用ss -lntp | grep 1521验证一下系统层的监听情况。我在生产环境遇到过监听进程活着、但系统里端口消失的情况——这种多半是防火墙或者服务器资源异常导致的半死状态,光看ps是发现不了的。
3.2 实例状态与参数健康度
监听没问题之后,就要进入实例层面。这里我用一个简洁的 SQL 检查套路,一句话就能看核心状态:
-- 查看实例状态、启动时间、当前时间、数据库是否可写 SELECT instance_name, status, startup_time, database_status FROM v$instance; -- 查看数据库整体的打开状态 SELECT name, open_mode, log_mode FROM v$database;重点看两处:v$instance.status必须是OPEN,v$database.open_mode必须含有READ WRITE。如果看到MOUNTED或STARTED状态,说明数据库还在恢复或尚未完全打开阶段,这时候应用肯定连不进去。另一种常见情况是open_mode变成了READ ONLY,别急着高兴,这未必是业务想要的,可能是有 someone 手动执行了alter database open read only,也可能是某个灾备库被误操作了。
参数检查方面,我巡检时只关心少数几个“出问题会秒炸”的参数,不会把所有 v$parameter 都看一遍:
-- 查看当前会话数与最大会话数设置 SELECT name, value FROM v$parameter WHERE name IN ('processes', 'sessions', 'open_cursors'); -- 查看实际当前会话数量 SELECT COUNT(*) FROM v$session; -- 查看 PGA 和 SGA 的内存使用概览 SELECT name, ROUND(value/1024/1024, 2) AS size_mb FROM v$parameter WHERE name IN ('sga_max_size', 'sga_target', 'pga_aggregate_target');会话数的检查逻辑跟 MySQL 的连接数类似,v$session里的实际会话数逼近sessions参数上限时,新连接会报 ORA-00018: maximum number of sessions exceeded。内存参数这里我不建议轻易动它,先看数值是否跟服务器物理内存匹配。一个原本 64G 内存的机器,如果 sga_max_size 被设成了 32G,你就要警惕是不是有人改过参数、为什么会改这么大——参数变更是 Oracle 问题的头号来源之一。
3.3 告警日志与等待事件的重点盯防
如果说 MySQL 的核心日志是错误日志(error log)和慢查询日志,那么 Oracle 的核心日志就是告警日志(alert log)。所有重要的错误事件——比如 ORA-01555、ORA-01653(表空间无法扩展)、ORA-00600——都会写到这里。日志文件位置可以通过查询拿到,不需要到处翻路径:
-- 查看告警日志所在路径 SELECT value FROM v$diag_info WHERE name = 'Diag Alert'; SELECT value FROM v$parameter WHERE name = 'background_dump_dest';拿到路径后,最有效的巡检动作是查看最近一天新增的错误信息。用 tail 加 grep 过滤就行,不用打开整个文件:
tail -n 3000 /u01/app/oracle/diag/rdbms/ORCL/ORCL/trace/alert_ORCL.log | grep -E "ORA-|Error" | tail -n 50这里我要说一个很多人容易犯的误区:不是所有 ORA- 错误都代表事故。比如 ORA-01017(invalid username/password),往往是有人在测试错误密码,这种属于“噪声”。真正需要第一时间重视的是 ORA-00600(内部错误,可能是 Bug)、ORA-01653/01654(表空间满,这个和磁盘空间强相关)、以及 ORA-01555(snapshot too old,回滚段不够)。巡检时要练出“过滤噪声、抓住关键错误”的眼力,否则日志刷一屏全是错误,反而分不清轻重。
等待事件也是 Oracle 巡检的核心,但不能等出问题了再去看。我建议在巡检脚本里加上一条快照式的等待事件统计:
-- 查看当前数据库中的前几个主要等待事件 SELECT * FROM ( SELECT event, total_waits, time_waited FROM v$system_event WHERE event NOT LIKE 'rdbms%' AND event NOT LIKE 'SQL*Net%' ORDER BY time_waited DESC ) WHERE ROWNUM <= 10;日常巡检中常见的几个等待事件我会专门盯:db file sequential read代表大量单行随机读——如果出现且耗时大,通常意味着索引没建好或者 SQL 全表扫描;log file sync代表日志写盘速度跟不上提交频率,通常和磁盘 IO 慢有关;enq: TX - row lock contention是行锁竞争,说明有人在并发更新同一行数据。看到这些事件先别慌,只要耗时没有异常增长、total_waits 没有爆发式增加,一般不代表故障,但要记录在案,连续两次巡检如果发现持续增长,就要深入排查了。
4. 把“快检”升级成“快判”:常见指标速查表
4.1 MySQL 侧的关键指标:测什么、看什么、怎么判断
检查了这么多命令、看了这么多状态,如果脑子里没有一张“多少算正常、多少算危险”的判断表,信息就没有价值。下面这张速查表是这几年我巡检 MySQL 时的经验汇总,当然不同业务、不同硬件配置会有差异,但作为基准值足够用了。
| 指标 | 关键命令/源 | 健康区间 | 黄色预警 | 红色危险 |
|---|---|---|---|---|
| 连接数使用率 | Threads_connected / max_connections | 小于 60% | 接近 80% 且持续上升 | 90% 以上 |
| 慢查询数量 | SHOW GLOBAL STATUS LIKE 'Slow_queries' | 长期为 0 或在均值线附近 | 突然翻倍 | 持续增长且伴随响应变慢 |
| 活跃会话 | SHOW PROCESSLIST 非 Sleep 数量 | 在预期范围内 | 缓慢增多 | 超过连接数一半,大量命令卡在锁等待 |
| InnoDB 锁等待 | SHOW ENGINE INNODB STATUS 中 SEMAPHORES 段 | 无长时间等待 | 少量秒级等待 | 等待超过 10 秒且持续堆积 |
| 主从复制延迟 | SHOW SLAVE STATUS 的 Seconds_Behind_Master | 0 或极小 | 持续增长 | 持续超过 60 秒(核心场景) |
| 磁盘容量 | df -h 数据目录所在分区 | 使用率 70% 以下 | 80%~85% | 90% 以上 |
关于复制延迟我要多说两句。很多使用 MySQL 主从架构的团队,日常巡检只关注主库状态,从库的复制延迟被忽略了。等业务读流量切到从库或者做数据恢复的时候才发现,延迟已经高到无法接受。SHOW SLAVE STATUS这条命令在 8.0 里改叫SHOW REPLICA STATUS,用法都一样,但很多人拿旧习惯去执行会报错,这里提醒一下。看到Seconds_Behind_Master出现比较大的数值,别急着骂网络,先检查从库所在机器的 IO 负载和主库有没有大事务在跑——大事务通常是复制延迟的真凶。
4.2 Oracle 侧的关键指标:只看这几个值就能定生死
Oracle 的指标体系比 MySQL 复杂,但我把它简化成一张同样能“速查”的表格,日常巡检足够用了:
| 指标 | 关键命令/源 | 健康区间 | 黄色预警 | 红色危险 |
|---|---|---|---|---|
| 实例状态 | v$instance.status | OPEN | - | MOUNTED 或 STARTED |
| 数据库打开模式 | v$database.open_mode | READ WRITE | READ ONLY | 不能用 |
| 监听器状态 | lsnrctl status | READY | UNKNOWN | NOT FOUND / 起不来 |
| 表空间使用率 | dba_data_files 结合 dba_free_space | 小于 80% | 85% 左右 | 超过 92% |
| 归档日志目录 | dba_archive_dest + df -h | 使用率 70% 以下 | 80% | 满了直接挂库 |
| 最近告警日志 | tail alert 日志 | 无新增 ORA-00600 / ORA-01653 | 有 ORA-1555 | 新出现 ORA-00600 或表空间满 |
表空间检查在 Oracle 里非常重要,因为它和 MySQL 有个本质区别:MySQL 的 InnoDB 表空间文件如果磁盘满了,数据库会直接报错甚至崩溃;Oracle 虽然也有类似问题,但它的表空间可以设置自动扩展(AUTOEXTEND),你没注意的情况下它可能已经蚕食了整个文件系统。所以巡检时不能只看表空间使用率,一定要配合看数据文件所在的磁盘分区是否还有剩余空间。我在实践中见过最典型的翻车现场:表空间使用率才 60%,但某个DATA01.dbf数据文件所在的分区已经满了,数据库无法扩展日志文件,直接 hang 住。
Oracle 还有一个特别容易忽视的地方:临时表空间(TEMP)。很多 SQL 排序、去重会用到临时段,如果 TEMP 表空间满了,会直接报 ORA-01652,这个错误不会写进告警日志的最高优先级里,但业务影响一样严重。巡检时顺带看一下临时表空间的使用情况,多一条命令而已,能少出一次事故。类似地,undo 表空间也要关注,SELECT tablespace_name, ROUND(SUM(bytes)/1024/1024) FROM dba_temp_free_space GROUP BY tablespace_name;这类查询虽然低级,却非常实用。
4.3 判断顺序比单点数值更重要
有了速查表还不够,因为在实际故障中,往往不是一个指标异常,而是几个指标一起异常。这时候判断顺序就很重要了。我强烈建议按“资源 → 会话 → 等待 → 日志”这样的顺序组织巡检流程:先看系统资源(CPU、内存、IO、磁盘),确定是不是物理资源被耗尽;再看会话数量有无异常堆积,是应用问题还是数据库问题;然后看等待事件,定位到底在等什么;最后翻日志,找到报错的具体来源。这个顺序同样适用于故障排查时快速缩小范围。
举个很典型的例子:MySQL 实例 CPU 跑到 99%,你如果一上来就去看慢查询日志,会发现大量 SQL 都很慢——但这只是结果,不是原因。真正的原因可能是某条之前正常的 SQL 因为统计信息过期选了错误的执行计划,也可能是 QPS 高峰期连接数暴涨导致上下文切换太频繁。如果先看会话状态,发现很多会话都卡在某一两张表的行锁上,接下来顺藤摸瓜找到事务源头,问题往往就能定位了。反过来如果会话都正常,再去看慢日志也不迟。
再者说,巡检表和速查表的数值不是死板的“标准答案”。一台 2 核 4G 的小机器和一台 32 核 128G 的大机器,同样的 QPS 数值含义完全不同。我通常是先连续记录一两周的正常基线,把“这个业务、这台机器”的健康区间记录下来,之后再用“跟基线比”的方式做判断——这比任何专家给的“标准阈值”都更贴合你的实际环境。
5. 落地成脚本:一套能直接用起来的巡检模板
5.1 类 Unix 环境巡检脚本模板(MySQL + Oracle)
理论讲完,来点实际能用的东西。很多团队没有商业监控软件,或者监控软件看的是应用层,数据库层需要自己想办法。我的做法是把上面那套检查动作写成一个 Shell 脚本,定时跑(比如每天早上 8 点半),把输出追加到日志文件里。脚本不需要复杂,关键是稳定、输出清晰、一次性覆盖重点指标。下面是我在类 Unix 环境下常用的一个精简模板:
#!/bin/bash # database_quick_check_v1.sh # 用法: ./database_quick_check_v1.sh [mysql|oracle] MYSQL_USER="dba_check" MYSQL_PASS="yourpass" ORACLE_SID="ORCL" LOG_FILE="/var/log/dbcheck/$(date +%Y%m%d)_quick.log" check_mysql() { echo "===== MySQL Quick Check $(date '+%F %T') =====" >> "$LOG_FILE" mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -e " SELECT '-- Version' AS info, VERSION() AS val UNION ALL SELECT '-- Uptime', SEC_TO_TIME(VARIANCE_VALUE) FROM performance_schema.global_status WHERE VARIABLE_NAME='Uptime'; " 2>&1 >> "$LOG_FILE" mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -e " SELECT VARIABLE_NAME, VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME IN ('Threads_connected','Max_used_connections','Slow_queries','Uptime'); " 2>&1 >> "$LOG_FILE" mysql -u"$MYSQL_USER" -p"$MYSQL_PASS" -e "SHOW FULL PROCESSLIST;" 2>&1 >> "$LOG_FILE" } check_oracle() { echo "===== Oracle Quick Check $(date '+%F %T') =====" >> "$LOG_FILE" lsnrctl status >> "$LOG_FILE" 2>&1 sqlplus -S / as sysdba <<EOF >> "$LOG_FILE" SET LINESIZE 200 PAGESIZE 100 SELECT instance_name, status, startup_time FROM v\$instance; SELECT name, open_mode FROM v\$database; SELECT tablespace_name, ROUND(100 - (free/total)*100,1) AS used_pct FROM (SELECT tablespace_name, SUM(bytes) total, 0 free FROM dba_data_files GROUP BY tablespace_name) ORDER BY used_pct DESC; SELECT event, total_waits, time_waited FROM v\$system_event WHERE ROWNUM <= 8 ORDER BY time_waited DESC; EXIT; EOF } case "$1" in mysql) check_mysql ;; oracle) check_oracle ;; *) echo "Usage: $0 [mysql|oracle]"; exit 1 ;; esac这里要解释一下为什么脚本里用 performance_schema 的 global_status 而不是 SHOW GLOBAL STATUS——MySQL 8.0 之后,后者虽然还能用,但官方已经把不少状态变量迁移到 performance_schema 里了,写脚本时统一从 performance_schema 取数,兼容性更好一点。另外脚本里的dba_check用户我在实际环境里会给足够的最小权限,比如 MySQL 只需 PROCESS、SELECT 权限,Oracle 则用 sys 账号或者单独建只读巡检账号。
脚本落到 crontab 里之后,我建议每周抽几分钟扫一眼日志文件里有没有异常,而不是完全放任不管。脚本能帮你把数据记下来,但“看一眼”这个动作是任何工具都没法替代的。
5.2 Windows 与跨平台场景的替代方案
这篇文章不是只给 Linux 环境用的。很多小型团队、开发环境、或者公司历史遗留的 Windows 服务器上也会跑 MySQL 和 Oracle。Windows 下没有ps -ef、没有ss、没有grep,很多同行第一次上手会觉得束手无策。其实核心思路完全一样,只是命令换一下:
# 查看 MySQL 进程和端口(PowerShell) Get-Process mysqld -ErrorAction SilentlyContinue netstat -ano | findstr 3306 # 查看 Oracle 服务和监听服务状态 Get-Service | Where-Object {$_.Name -like '*mysql*' -or $_.Name -like '*Oracle*'} lsnrctl statusWindows 下 MySQL 和 Oracle 都是注册成 Windows 服务运行的,所以Get-Service能看到服务是 Running 还是 Stopped。如果服务状态是 Running,但应用连不上,再执行netstat -ano | findstr 端口号,确认 TCP 层有没有监听。很多时候 Windows 防火墙会静默拦截外部连接,表现为“本地看起来正常、远程连不上”,这时候检查防火墙入站规则往往比查数据库本身更有效。
PowerShell 下面执行 SQL 也没有环境障碍,直接用 MySQL 自带的 mysql.exe,路径通常在安装目录下的 bin 文件夹里。Oracle 则用 sqlplus,Windows 的 PATH 里大概率已经配好了。总体而言,跨平台的核心检查清单和判断逻辑完全一致,只是命令语法换了层皮,不用被 Windows 吓住。
5.3 巡检频率与留痕建议
巡检脚本写好了,多久跑一次?这个问题没有标准答案,但我可以分享自己的配置思路:核心业务库:每天早高峰前一次、中午一次,晚上批量任务前再看一次;非核心库:每天一次就够;开发库:每周一次即可。这里有个容易忽略的点——早高峰前的巡检能发现“昨晚批量任务遗留的会话”、“备份任务留下的磁盘压力”等问题,中午的巡检则是抓“业务请求最多时的连接数和锁等待”,这两个时间点的样本价值比其他时间高很多。
留痕方面,我强烈建议不仅把巡检结果追加进日志文件,最好每周把报告中的关键指标抽出来做一次对比。比如记录本周每天连接数的峰值、慢查询次数、表空间增长速度——这些数据积累一个月后,你再做容量评估、性能优化提案时,就不再拍脑袋了,直接引用历史数据,谁也没法反驳。脚本跑完的数据也可以定期同步到公司的监控系统或者导出成 CSV 表格,方便做趋势图。
还有一个关于安全性的提醒:巡检脚本里硬编码了数据库密码,这在团队协作中是个隐患。比较稳妥的做法是使用 MySQL 的--login-path机制或者 Oracle 的 wallet 特性,把凭证从脚本里剥离。这个改造不复杂,但能避免“DBA 的服务器被人翻出明文密码”这种极其尴尬且危险的事件。如果你自己有权限管理服务器,至少也要确保脚本文件的属主和权限是 600,不能让每个运维人员都能看。这条建议用一句话总结就是:巡检脚本会长期留存,凭证管理好坏直接决定它的安全性,不要嫌麻烦。
6. 故障现场实录:这 6 类问题是巡检中最容易遇到的
6.1 慢、卡、连接失败类问题的排查路径
实践永远是检验套路的最好方式。我分享几个真实的故障排查思路,配合上面那些命令,遇到类似问题时你可以直接照方抓药。
第一类是“数据库突然变慢”。这时候最忌讳的就是直接去翻慢查询日志,因为慢日志记录的是已经发生的事,等你看到了,现场可能已经变化了。我的顺序是:先SHOW PROCESSLIST看有没有卡住的会话,再看vmstat 或 top看系统是否出现 swap、CPU 是否跑满,然后看磁盘 IO,最后才去分析慢 SQL。有一次线上系统全面变慢,我按这个顺序查下来,发现磁盘 IO 使用率已经到了 98%,再一查,原来是隔壁部门的备份脚本把整个数据盘给压满了。这种问题你只盯数据库层面一辈子都查不出来。
第二类是“应用报连接超时”。如果Threads_connected没有打满,processlist里也没有堆积的会话,那问题大概率在连接池或者网络层。这时候我会分别在应用服务器和数据库服务器上执行telnet IP 端口来测连通性,必要时抓一下包。很多连接超时其实是应用服务器和数据库服务器之间的防火墙策略变了,或者数据库所在机器的负载太高导致握手超时,跟数据库本身没什么关系。
第三类是“某个特定 SQL 一直跑不完”。这类问题最好的工具就是执行计划(EXPLAIN),但很多人在巡检阶段容易忽略它。巡检时如果看到 proceslist 里有一条 SQL 执行时间远超预期,我都会让开发把这条 SQL 拿出来,EXPLAIN看有没有走全表扫描、有没有用错索引。MySQL 的优化器失误、Oracle 的统计信息过期都会导致执行计划突然变差,这类问题比硬件故障更隐蔽,也更需要日常巡检时保持敏感。
6.2 一张“问题-命令-原因”速查表
为了方便大家遇到问题时快速定位,我把常见故障现象、对应排查命令、最高概率的原因整理成一张速查表。这不是教科书上的标准答案,但都是我这些年踩坑后得来的实战判断,适配绝大多数场景。
| 故障现象 | 最先执行的命令 | 最常见原因 |
|---|---|---|
| MySQL 服务挂掉或重启了 | journalctl -u mysqld或error log | OOM killer、磁盘满、被误 kill |
| MySQL 报 Too many connections | 上文连接数 SQL | 连接池配置过大、存在长时间挂起的会话 |
| MySQL 主从延迟告警 | SHOW REPLICA STATUS\G | 大事务、从库磁盘慢、主库 DDL |
| Oracle 监听起不来 | lsnrctl start看报错 + 检查 hosts | 主机名解析失败、端口被占 |
| Oracle 实例卡在 STARTED | 告警日志 +v$recovery_status | 崩溃后恢复进程未完成、redo 丢失 |
| Oracle 报表空间不足 | 表空间使用率 +df -h | AUTOEXTEND 上限、磁盘分区满 |
| 应用连接偶发超时 | 连接数 + 网络连通性 + 抓包 | 防火墙策略、连接池耗尽、SYN 队列满 |
| 全表扫描导致 CPU 暴涨 | EXPLAIN+processlist | 索引缺失、统计信息过期、隐式类型转换 |
表格里有两处需要额外提醒。第一,MySQL 主从延迟里“大事务”这个原因,很多新手不好理解,其实比如你执行了一条UPDATE t SET ... WHERE created_at >= '2023-01-01',这如果更新了几亿行,主库自己要跑很久,从库拿到 relay log 后也要回放很久,延迟自然就上去了。遇到这种情况,把大事务拆小才是治本。第二,Oracle 表空间不足的问题里“AUTOEXTEND 上限”太容易被忽略——很多表空间设置了自动扩展,但 maxsize 是 32G,到了上限就不再扩了,从使用率上看是 99%,其实就是配置问题,不需要加磁盘,改上限就行。
6.3 日常巡检中我踩过的最深的三个坑
写到最后,分享三个我自己的翻车经历,每个教训都对应一套预防动作,希望能帮你避开。
第一个坑:只看数据库不配合查看系统资源。早年维护某套 MySQL 集群,有一次业务反馈整体变慢,我在数据库里查了大半天,连接、锁、慢查询都正常,最后无意中看了一眼df -h——日志分区已经 100% 占满,MySQL 写 binlog 和 redo log 的时候一直在阻塞重试。数据库看起来“没什么问题”,但每一个写入都被迫等待磁盘释放空间,整体性能自然就下来了。从那以后我把“系统层的磁盘空间检查”放在数据库巡检的固定项里,而不是等出了问题才去看。
第二个坑:过度依赖监控面板,忽略了巡检的“第一次现场感”。有一段时间我所在的环境全面上了 Grafana 监控,各种指标图表都很齐全,我一度觉得手工巡检没必要了。结果有一次 MySQL 实例出现周期性抖动,监控面板上什么都看不出来,因为指标的采集粒度是 30 秒,而抖动只持续几秒。最后是手工执行SHOW PROCESSLIST连续盯了几分钟后,抓到了那个每 5 秒跑一次、执行 3 秒的定时任务 SQL 才定位问题。监控是“面”,命令行是“点”,再好的监控也替代不了你自己在现场执行一条命令的即时反馈。
第三个坑:巡检记录不归档,等于白巡检。我见过很多团队,巡检脚本跑了、结果看了、没问题就关了,日志留存随意覆盖。等到要做容量规划或者审计追溯时,拿不出任何历史数据。后来我养成了一个习惯:每个季度末把巡检日志做一次归档,按年份和季度分目录存好,至少保留一年。这不仅仅是追溯故障的需要,更是分析趋势、做性能预算的重要依据——三季度磁盘从 60% 涨到 75%,四季度必然破 80%,这时候提加磁盘或者优化归档的立项,比等到系统报警了再紧急扩容要从容得多。
这三个坑看起来都是“常识”,但真正的 DBA 都知道:常识恰恰是最容易在忙碌中被遗忘的东西。写这套方法的初衷也是想把那些“常识性但容易被忽视”的动作标准化,让巡检真正起到预防故障的作用,而不是沦为走过场的形式。