
最近好几个朋友问我要一份能直接照抄的数据库巡检清单而且点名就要Oracle和MySQL的。说实话这需求我太懂了——生产环境里Oracle和MySQL混着用的公司不少平时开发写SQL挺顺手真到了数据库卡顿、连接数飙高、磁盘报警的时候手边要是没有一套顺手的心得很容易东敲一条命令西看一眼状态折腾半天也说不清数据库到底是“病了”还是“只是累了”。这篇文章我就把自己日常用的那套快速检查流程整理出来。不是什么高深调优大全就是一套以“最快速度定位问题、判断风险”为目标的实用口诀和命令组合覆盖实例可用性、关键性能指标、容量隐患和常见故障四个维度。不管你是刚接手数据库的新人还是被临时拉去救火的开发照着这套思路走一遍基本能在十分钟内对数据库的运行状态心里有数。1. 快速检查的整体思路与准备1.1 检查应该“快”到什么程度先说一个很多人容易搞混的点快速检查和性能调优完全是两码事。快速检查追求的是“在最短时间内确认数据库是否健康、风险点在哪里”而不是“把每条SQL都分析一遍”。所以思路一定要分层从外到内、从可用性到性能一层层筛。我习惯把检查分成四层第一层能不能连上。实例是否启动、监听是否正常、基本登录是否成功。第二层有没有明显异常。连接数是否快满、是否有大量阻塞会话、日志里有没有报错。第三层容量够不够。表空间、磁盘、内存相关的核心指标是否逼近阈值。第四层性能有没有隐患。慢查询是否变多、关键等待事件是否异常、缓存命中率是否下降。每层在Oracle和MySQL上都有对应的核心命令下面我会分别展开。这套分层逻辑的好处是不会被海量监控数据带偏每层都有明确的“通过/不通过”标准适合值班巡检和应急排查两个场景通用。1.2 环境准备与连接信息核对开始之前先把环境准备好不然边查边发现连不上数据库很耽误事。确认你手上有数据库服务器的系统账号或者至少能通过堡垒机登录。确认数据库连接账号有足够的查询权限。Oracle一般需要能查v$视图MySQL需要有process和replication client权限。最小权限原则下巡检账号别用root或sys。准备好客户端工具。Oracle的sqlplus最常见MySQL的mysql命令行就行。Windows环境下注意把客户端bin目录加进PATH避免“不是内部或外部命令”的尴尬。记住数据库的监听端口、服务名或SID。Oracle默认1521MySQL默认3306但生产环境改端口的太多了不确认清楚就连接白折腾。提示巡检账号最好单独创建并固定下来不要每次都用业务账号连。一来安全二来避免因为业务账号密码过期导致巡检中断。2. Oracle数据库运行状态快速检查2.1 第一层实例、监听和告警日志Oracle的快速检查我习惯先从“三件套”开始实例状态、监听状态、告警日志。实例状态用这条select instance_name, status, database_status, log_mode from v$instance;status应该是OPENdatabase_status应该是ACTIVE。如果看到MOUNTED或STARTED说明数据库还没完全打开业务是不可能正常的。log_mode显示ARCHIVELOG或NOARCHIVELOG这个要记住生产库不开归档的隐患很大后边再细说。监听检查在操作系统层面做lsnrctl status重点看几个信息监听是否启动、监听的服务名是否包含目标库、最后刷新时间是否一直在动。我遇到过不少次“数据库没问题但应用连不上”的情况最后都是监听服务挂了或者服务注册出了问题。如果lsnrctl status卡住不动基本可以判断监听服务处于假死状态需要查看监听日志并考虑重启监听这个在第四章的排查实录里再展开。告警日志是Oracle最直接的“病历本”。用adrci可以快速定位adrci show alert -tail 20重点看最后几十行有没有ORA-600内部错误、ORA-01653表空间不够、ORA-01555快照太旧这类常见致命错误。有个容易被忽略的细节告警日志里出现“Thread 1 cannot allocate new log”之类的信息说明日志切换太频繁通常跟归档空间不足有关这往往是数据库突然卡顿的隐藏原因。2.2 第二层连接数与会话状态实例确认没问题后立刻看连接数使用率。Oracle的连接数上限由processes参数控制如果达到上限新的连接会直接报ORA-12519或ORA-00020。快速检查命令select count(*) from v$process; select value from v$parameter where nameprocesses;两个数一除就是进程使用率。我给的参考值是平时稳定在70%以下算安全超过80%就要警惕超过90%基本早晚要出问题。注意不是到100%才出问题很多应用连接池的报错在85%左右就开始出现了因为每个应用服务器可能有多个连接池同时抢连接。会话方面重点看两类阻塞会话和异常状态会话。select sid, serial#, username, status, machine, program, sql_id, event, blocking_session from v$session where status ACTIVE order by blocking_session nulls last;这个SQL能一次性看活跃会话、等待事件和阻塞源。如果blocking_session有值说明存在锁等待链需要往上追。status为INACTIVE的会话一般来说正常但如果INACTIVE会话数量上千同时CPU很高就要怀疑是不是连接泄漏通常能从program字段看到是哪台应用服务器哪个应用连的顺藤摸瓜去排查。另外可以快速看一类典型等待事件select event, count(*) from v$session where wait_class Idle group by event order by count(*) desc;如果排在最前的是db file sequential read、db file scattered read这类IO相关事件数据库在等磁盘如果是enq: TX - row lock contention就是有行锁阻塞如果是library cache lock多半有同学在线上跑DDL导致共享池被锁住这类问题处理不当很容易“拖垮”整个实例。2.3 第三层表空间与临时表空间的容量隐患Oracle容量问题里最常见的就是表空间满了导致业务写入失败。快速检查表空间剩余量我常用这条SQLselect d.tablespace_name, round(d.sum_mb, 2) as total_mb, round(nvl(f.free_mb, 0), 2) as free_mb, round((d.sum_mb - nvl(f.free_mb, 0)) / d.sum_mb * 100, 2) as used_pct from (select tablespace_name, sum(bytes) / 1024 / 1024 as sum_mb from dba_data_files group by tablespace_name) d left join (select tablespace_name, sum(bytes) / 1024 / 1024 as free_mb from dba_free_space group by tablespace_name) f on d.tablespace_name f.tablespace_name order by used_pct desc;看到使用率超过90%的就要注意了。这里有个经验不是所有表空间满了都会立刻报错取决于表空间的扩展策略。如果开启了autoextend使用率到100%时会尝试自动扩展但如果磁盘本身满了或者maxsize设了上限一样会报ORA-01653。所以查看剩余空间时还得顺手确认磁盘可用空间df -hOracle还有个特别容易忽略的点临时表空间。排序、去重、复杂join都会用到临时表空间一旦临时表空间不够用直接报ORA-01652而且业务日志里看到的错误往往五花八门不容易直接关联到容量问题上。检查临时表空间select tablespace_name, current_size, max_size, tablespace_size, allocated_size, free_space from dba_temp_free_space;此外undo表空间也别漏掉。快速检查undo使用情况select tablespace_name, sum(bytes) / 1024 / 1024 as used_mb from dba_undo_extents where status ACTIVE group by tablespace_name;ACTIVE状态的undo量如果持续增长不释放大概率有大事务跑太久或者是应用开启事务后忘了提交/回滚。这种情况在使用率上不一定能看出异常但会引发ORA-01555快照太旧非常坑。2.4 快速查看性能拐点如果前面的检查都正常但业务就是反馈慢那就需要往性能方向多看两步。第一步看整体负载变化。Oracle的负载可以从v$sysstat里抓关键指标比如每秒事务数、每秒逻辑读、物理读变化。手工对比历史值不方便但可以借助AWR报告。有人觉得AWR太慢、太正式其实换个思路用AWR的DB Time和每秒事务数做个参考就行。快速生成最近一小时AWRselect * from table(dbms_workload_repository.awr_report_text( :dbid, :inst_num, (select min(snap_id) from dba_hist_snapshot where begin_interval_time sysdate - interval 1 hour), (select max(snap_id) from dba_hist_snapshot) ));如果AWR报告里的DB Time显著大于Elapsed Time说明数据库CPU和IO确实在忙不是业务自身在等待应用服务器响应。第二步看SQL层面有没有明显异常。定位到Top SQL我习惯直接用实时视图select sql_id, executions, elapsed_time/1000000 as elapsed_sec, cpu_time/1000000 as cpu_sec, disk_reads, buffer_gets from v$sql where parsing_schema_name not in (SYS,SYSTEM,DBSNMP) order by elapsed_time desc fetch first 10 rows only;注意elapsed_time是累计值不是单次执行时间。更好的判断方式是用v$sqlarea配合last_active_time字段筛选最近几分钟还在执行的SQL。如果有某条SQL的disk_reads很大但buffer_gets很小说明它在大量走磁盘读往往跟缺少索引或者统计信息陈旧有关系。这时候可以把SQL语句取出来用执行计划看一眼是不是在走全表扫描但这一步属于深入排查快速检查阶段做到“发现嫌疑对象”就够了。3. MySQL数据库运行状态快速检查3.1 进程、端口和复制状态的“首诊”MySQL的检查入口比Oracle更“轻”但也有它自己的坑。第一步先看进程和端口mysqladmin -h127.0.0.1 -uroot -p ping systemctl status mysqld ss -lntp | grep 3306ping返回“mysqld is alive”说明实例进程活着。但这里要提醒一句实例活着不代表能正常服务。有一种常见情况是连接数耗尽实例本身没宕但新连接进不来应用报错“Too many connections”所以端口和进程只是最最基础的第一关。对于使用主从复制架构的环境复制状态的检查必须排在很靠前的位置。MySQL 8.0开始推荐用这条命令show replica status\GMySQL 5.7及以前版本用的是show slave status\G不管哪条命令重点看三个字段Replica_IO_Running / Slave_IO_Running应为Yes。Replica_SQL_Running / Slave_SQL_Running应为Yes。Seconds_Behind_Source / Seconds_Behind_Master越接近0越好。有个经验供参考Seconds_Behind_Source为0不代表主从绝对健康还要看主库的binlog有没有正常同步到relay log。如果从库的IO线程频繁断开重连而SQL线程一直运行Seconds_Behind_Source可能显示为0但实际上已经断档了。判断方法很简单对比主库和从库的binlog文件位置或者直接看从库的错误日志有没有dump线程异常信息。所以复制检查千万不能只看一个字段两个运行状态必须一起看。3.2 连接数第一道风险线MySQL连接数问题是生产环境出现频率最高的故障之一。快速检查核心指标show global status like Threads_connected; show global status like Max_used_connections; show variables like max_connections;Threads_connected是当前连接数Max_used_connections是历史最大连接数max_connections是上限。我判断的标准是当前连接数超过max_connections的80%或者Max_used_connections已经逼近上限都需要马上处理。一个容易踩的坑很多人只看Threads_connected忽略了Max_used_connections。实际上业务高峰期一过当前连接数会降下来但Max_used_connections记录的是历史峰值。如果你能发现历史峰值离上限很近说明高峰期随时可能爆连接这是很重要的预警信号。如果真出现Too many connections处理思路是先不要盲目重启MySQL。重启成本高而且很快又会打满。从performance_schema或processlist里找连接来源定位具体是哪个应用在大量建连。如果实在紧急可以在配置文件里临时提高max_connections比如调到2000然后重启实例。但这属于救急不是治病治根还得从应用连接池配置和慢SQL入手。查看当前所有连接select id, user, host, db, command, time, state from information_schema.processlist order by time desc;这个结果里可以直观看到哪些连接长时间处于Sleep状态。Sleep连接多不一定是坏事连接池里的空闲连接就是Sleep。但如果Sleep连接数量异常大且持续增长就要怀疑连接泄漏了。3.3 慢查询与缓冲池命中率性能状态的体温计MySQL性能快速检查我一般看三块慢查询数量、InnoDB缓冲池命中率、临时文件使用情况。慢查询开关和阈值show variables like slow_query_log; show variables like long_query_time;如果slow_query_log是OFF我建议先开起来生产环境必须开。long_query_time一般设置成1秒或2秒超过这个阈值的SQL都会被记录。检查当前慢查询数量show global status like Slow_queries;这个数值是累计值单看没意义要和启动时间、之前记录值对比才知道增长趋势。最实用的是直接分析慢查询日志mysqldumpslow -s at -t 20 /var/log/mysql/mysql-slow.logmysqldumpslow是MySQL自带的工具-s at表示按平均执行时间排序-t 20表示取前20条。它会自动把数字参数抽象成N把字符串参数抽象成S方便聚合统计。InnoDB缓冲池命中率我用这条SQL算show global status like Innodb_buffer_pool_read_requests; show global status like Innodb_buffer_pool_reads;命中率 (read_requests - reads) / read_requests * 100%。正常情况下应该超过99%如果低于95%说明大量查询走了磁盘读缓冲池配置可能偏小或者SQL没有走索引导致扫描量太大。这里多说一句Innodb_buffer_pool_reads是直接从磁盘读的次数而不是物理读page的数量但作为趋势参考完全够用。临时文件这块容易被忽略show global status like Created_tmp_disk_tables; show global status like Created_tmp_tables;如果Created_tmp_disk_tables占总临时表比例很高说明很多SQL排序或分组操作没有充分利用内存而是落到了磁盘临时表。这种SQL在业务高峰期会加剧IO压力。搭配查看tmpdir所在磁盘空间如果满了MySQL会直接报错。3.4 磁盘容量、binlog与分区表的隐患MySQL的数据目录空间检查最好连binlog一起看df -h /var/lib/mysql du -sh /var/lib/mysql du -sh /var/lib/mysql/binlog.*binlog增长速度快的环境经常上演“磁盘突然告警”的戏码。这里分享一个我踩过的坑曾经有一个系统每天binlog增长几十GB当时的维护同学认为磁盘还够就没有清理结果某天早上磁盘100%满MySQL直接进入只读保护状态整个业务写入全部失败。所以binlog的清理周期和保留策略一定要提前定好不要等磁盘满了才动手。我们常用的清理方法是配置expire_logs_days5.7及以下或者binlog_expire_logs_seconds8.0及以上show variables like binlog_expire_logs_seconds;注意8.0版本里expire_logs_days已经被废弃要用秒来配置。如果磁盘已经满了可以手动清理mysql -e purge binary logs to mysql-bin.000123;purge to会把指定binlog之前的所有日志清掉。这里千万注意不要直接删binlog文件一定要用purge语句否则会导致主从复制元数据不一致从库再也追不上主库只能重建。磁盘空间检查还有一个容易忽略的点MySQL自身的临时表和undo表空间。一般undo会自动管理但如果你用的是独立表空间或者磁盘上有大文件占空间还是要留意。另外分区表环境下如果一个分区被大量写入但历史分区不归档清理数据文件会无限膨胀这种问题不是数据库层面的报错但会让备份、恢复、日常巡检都变得异常慢。4. 常见问题与排查技巧实录这一节把我在实际工作中遇到频率最高的几个问题按“现象、定位、处理”格式整理成一个速查表方便值班时照着做。问题现象快速定位命令常见原因处理建议Oracle连接报ORA-12519lsnrctl status查看服务注册processes参数接近上限排查连接泄漏考虑增大processes参数并重启Oracle监听卡死lsnrctl status长时间无响应监听日志过大或网络异常备份并清空监听日志重启监听Oracle表空间无法扩展检查dba_data_files和df -h表空间autoextend关闭或maxsize限制改为自动扩展或手工增加数据文件MySQL报Too many connectionsshow processlist、Max_used_connections应用连接池配置过大或慢SQL阻塞优化连接池、kill长时间Sleep会话、临时调大上限MySQL主从IO线程断连show replica status看Last_IO_Errno网络抖动、主库binlog被清追查错误码重设主从关系或重建从库MySQL磁盘空间满进入只读df -h、binlog目录大小binlog未清理或数据增长过快purge binlog、清理临时表、扩容磁盘磁盘IO利用率飙升Linux的iostat、show global status慢查询全表扫描、临时表落盘优化SQL、增加索引、调整缓冲池再讲一个排查思路的实例。有一次系统反馈“登录很慢”我连上数据库看Oracle的CPU不算高会话数正常表空间也充足单看这些基础指标一切都好。后来多查了一步等待事件发现大量会话卡在enq: TX - row lock contention上。顺着blocking_session追上去发现某个定时任务的事务一直没有提交把一张核心业务表的某一行锁住了所有登录操作都因为更新同一条用户记录而排队。这个问题的根源不在数据库配置而在于应用代码里的事务处理逻辑。这类问题在快速检查中如果不做“等待事件”这一层基本很难肉眼发现。还有一个很值得说的经验快速检查要养成“对比”的习惯。单看某一条命令的当前数值可能什么也说明不了但如果和昨天、上周的同时段数值对比往往能马上发现异常。比如Threads_connected平时500今天突然900即使离上限还有距离也已经说明有问题了。所以我在脚本里会额外保留一份历史输出用最简单的文本文件保存每次巡检自动对比并高亮差值明显的指标。5. 把这套巡检固化下来脚本与自动化建议5.1 输出格式与快照基线如果你只是偶尔排查问题手工敲命令没问题。但数据库管理这件事靠人肉盯是盯不住的建议把前面的检查步骤固化成一个脚本让它在每天固定时间自动跑一遍输出一份快照。快照不需要太复杂关键是“有历史对比”。最简单的做法#!/bin/bash # 简易巡检脚本骨架 log_dir/tmp/dbcheck mkdir -p $log_dir # Oracle部分 sqlplus -s check_user/passwordhost:1521/orcl EOF $log_dir/oracle_$(date %F_%H%M).log select instance_name, status from v\$instance; select count(*) as process_cnt from v\$process; select tablespace_name, round(sum_bytes/1024/1024,2) from ...; EOF # MySQL部分 mysql -h127.0.0.1 -ucheck_user -p密码 \ -e show global status like Threads_connected; show global status like Slow_queries; show slave status\G; \ $log_dir/mysql_$(date %F_%H%M).log # 查看最近两次差异 diff $log_dir/mysql_$(date %F_%H%M --date-1 day).log \ $log_dir/mysql_$(date %F_%H%M).log脚本里要注意一个细节Oracle的SQL里引用v$视图时$符号在shell脚本里会被当作变量解析必须用反斜杠转义成v\$instance。有了快照基线之后阈值判断可以做得更稳。比如连续三天Slow_queries增量都在上涨虽然单日数量不大但趋势已经指向某个新上线SQL有问题这种提前发现的价值远高于某天突然爆掉再去救火。5.2 巡检脚本的关键细节与安全事项最后说几个写巡检脚本时容易忽略的细节都是实践经验。第一巡检SQL一定要加超时控制。Oracle的sqlplus可以用set timeout或提前在系统层面用timeout命令控制MySQL则可以在连接参数里设置connect_timeout和wait_timeout。别小看这一点我有一次就是因为一条误写的巡检SQL在dba_hist视图上全量扫直接把数据库搞卡了几分钟教训很深。第二巡检账号权限要克制。Oracle只给create session、select any dictionary、select on v_$视图等必要权限。MySQL只给process、select、replication client等权限。永远不要用dba或root账号直接跑巡检脚本一旦脚本被篡改或者误操作后果完全可控。第三避免在业务高峰跑大查询。巡检里查看数据字典和动态视图本身一般不会太重但如果有的巡检脚本加了“查Top SQL历史执行计划”这种重量级操作尽量放到业务低峰期执行。另外有些巡检SQL如果写法不当会在解析阶段消耗大量shared pool资源这在Oracle里是需要特别注意的。第四日志保留周期要有策略。快照日志建议保留30到90天太短的话对比不出趋势太长又浪费磁盘。另外日志目录本身要纳入磁盘监控防止日志把磁盘写满。这种事听起来很蠢但我确实见过巡检脚本正常运行几个月后因为日志文件太大导致磁盘告警的案例。我现在的习惯是每天早上八点自动跑一遍快照巡检结果直接输出到固定目录每周看一次趋势对比每月做一次由脚本生成的结果汇总。遇到告警先看本周的趋势再决定是否需要深入排查。这套流程看起来很简单但坚持下来以后我对每套数据库的运行习性会有很清晰的把握——哪套库平时连接数就是偏高哪套库的慢查询增长有周期性哪套库的主从延迟经常在深夜抬头这些信息都比“某天数据库突然出故障”要值钱得多。