数据库出了问题,第一反应不应该是直接重启。重启能暂时掩盖问题,但会破坏现场,导致无法定位根因。正确的做法是先止血,再诊断。所谓止血,就是通过命令行快速查看当前数据库的运行状态,确认是慢查询、锁等待还是连接数打满,然后针对性地处理。下面直接进入各类数据库最常用、最能救命的命令实操和排查思路。
连接数与会话管理数据库连接数被打满是生产环境最常见的故障之一。应用层报错“too many connections”,意味着数据库已经拒绝新的连接请求。在MySQL中,先查看当前连接数和限制:
SHOW VARIABLES LIKE 'max_connections'; SHOW STATUS LIKE 'Threads_connected';
如果当前连接数接近最大值,需要马上找到这些连接来自哪里,在干什么。执行下面的命令查看所有活跃连接:
SHOW FULL PROCESSLIST;
这个命令会列出所有连接的ID、用户、来源IP、数据库、执行时间和当前状态。重点关注Command列为Query且Time值很大的行,这些就是长时间运行的查询。如果发现大量连接处于“Locked”或“Sending data”状态,说明存在锁等待或慢查询。对于确认需要终止的连接,使用KILL命令:
KILL [CONNECTION] 连接ID;
如果连接数已经打满,连执行SHOW PROCESSLIST都进不去,可以尝试使用管理端口登录。MySQL可以预先配置admin_address和admin_port,这个端口专门留给管理员做紧急处置。PostgreSQL中,查看连接和活动的命令是:
SELECT * FROM pg_stat_activity;
重点关注state列为active的会话,以及wait_event_type和wait_event字段,这两个字段直接告诉你会话在等什么,是等锁还是等IO。终止会话使用:
SELECT pg_terminate_backend(pid);
排查连接数问题的核心思路是:先看总量,再看明细,找到异常来源后,是应用端连接池配置错误,还是某个功能突然高并发,还是有人忘了关查询工具,一目了然。
慢查询定位与执行计划分析CPU突然飙高,最常见的原因就是慢查询。慢查询不是靠猜,而是靠日志和实时抓取。在MySQL中,先确认慢查询日志是否开启:
SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time';
如果没开,在紧急情况下可以临时开启,但更直接的方法是实时抓取当前正在执行的慢查询。MySQL 5.7及以上版本提供了sys库,里面有一张statement_analysis表,能直接看到哪些SQL执行次数多、总耗时高、平均耗时高:
SELECT * FROM sys.statement_analysis ORDER BY avg_latency DESC LIMIT 10;
对于已经抓到的具体SQL,必须用EXPLAIN来分析执行计划。关键看几个字段:type列,从好到差依次是const、eq_ref、ref、range、index、ALL。如果出现ALL,就是全表扫描,数据量一大必然出问题。rows列是预估扫描行数,extra列出现Using filesort或Using temporary,说明排序或分组用到了临时表和文件排序,性能会很差。
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'pending';
如果EXPLAIN的结果和预期不符,可能是统计信息不准。MySQL中执行ANALYZE TABLE来更新统计信息。PostgreSQL中对应的是EXPLAIN ANALYZE,这个命令会实际执行SQL并返回真实耗时和实际扫描行数,比单纯的EXPLAIN更有价值:
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;
PostgreSQL的查询分析更细致,能看到每个步骤的实际启动时间和总时间,以及计划行数和实际行数的偏差。如果偏差巨大,说明统计信息过时,需要执行ANALYZE命令收集统计信息。慢查询排查的完整路径是:定位慢SQL、分析执行计划、确认索引使用情况、检查统计信息、优化SQL或添加索引。
锁问题排查与处理锁等待会导致业务大面积阻塞,表象是某个功能突然卡住,然后整个系统连接数耗尽。MySQL的InnoDB引擎,查看当前锁等待情况最直接的方法:
SELECT * FROM information_schema.INNODB_TRX; SELECT * FROM information_schema.INNODB_LOCKS; SELECT * FROM information_schema.INNODB_LOCK_WAITS;
这三张表分别记录当前事务、持有的锁和锁等待关系。但更推荐MySQL 8.0中的performance_schema,数据更清晰:
SELECT * FROM performance_schema.data_locks; SELECT * FROM performance_schema.data_lock_waits;
通过data_lock_waits可以找到阻塞者和被阻塞者。找到源头事务后,果断KILL掉。如果不想直接杀,可以先看这个事务执行了多久,有没有提交记录。PostgreSQL中查看锁等待:
SELECT blocked_locks.pid AS blocked_pid,
blocking_locks.pid AS blocking_pid,
blocked_activity.query AS blocked_query,
blocking_activity.query AS blocking_query
FROM pg_locks blocked_locks
JOIN pg_locks blocking_locks ON blocked_locks.locktype = blocking_locks.locktype
AND blocked_locks.relation = blocking_locks.relation
AND blocked_locks.pid != blocking_locks.pid
JOIN pg_stat_activity blocked_activity ON blocked_locks.pid = blocked_activity.pid
JOIN pg_stat_activity blocking_activity ON blocking_locks.pid = blocking_activity.pid
WHERE NOT blocked_locks.granted;
这条SQL直接列出谁在等锁,谁持锁。锁问题的根因通常是事务未及时提交、长事务、或者DDL操作在等MDL锁。排查时要养成习惯,先看有没有未提交的事务,再看具体的锁类型,是行锁还是表锁,是间隙锁还是元数据锁。
磁盘空间与日志文件应急处理磁盘空间满会导致数据库直接停服,这是最紧急的故障之一。常见原因包括:binlog堆积、慢查询日志过大、数据文件增长超出预期、临时表空间膨胀。MySQL中快速查看磁盘占用:
SELECT table_schema, table_name,
ROUND(SUM(data_length + index_length) / 1024 / 1024 / 1024, 2) AS size_gb
FROM information_schema.tables
GROUP BY table_schema, table_name
ORDER BY size_gb DESC LIMIT 20;
这条命令找出占用空间最大的表。对于binlog,先查看保留策略:
SHOW VARIABLES LIKE 'expire_logs_days'; SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';
如果磁盘已经满了,紧急清理binlog可以执行:
PURGE BINARY LOGS BEFORE NOW();
但执行前务必确认主从同步状态,避免删掉从库还没拉取的日志。PostgreSQL中,检查WAL日志大小和归档状态:
SELECT pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0'); SELECT * FROM pg_ls_waldir() ORDER BY modification DESC;
如果WAL堆积,检查归档命令是否正常,是否有replication slot残留。一个常见的坑是某个不再使用的replication slot阻止了WAL清理,导致磁盘爆满。通过以下命令检查并删除无用的slot:
SELECT * FROM pg_replication_slots;
SELECT pg_drop_replication_slot('slot_name');
内存与缓存命中率分析
数据库性能下降,很多时候是因为内存配置不合理,导致缓存命中率低,大量物理IO。MySQL中查看缓冲池命中率:
SHOW STATUS LIKE 'Innodb_buffer_pool_read%';
计算命中率公式:(Innodb_buffer_pool_read_requests - Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests。这个值应该无限接近1,如果低于0.99,说明缓冲池太小,数据频繁从磁盘读取。还要关注缓冲池的利用率:
SHOW STATUS LIKE 'Innodb_buffer_pool_pages%';
通过Innodb_buffer_pool_pages_total和Innodb_buffer_pool_pages_free判断缓冲池是否用满。PostgreSQL中,缓存命中率通过以下查询获得:
SELECT
sum(heap_blks_read) as heap_read,
sum(heap_blks_hit) as heap_hit,
sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) as ratio
FROM pg_statio_user_tables;
同样,这个比值应该接近1。如果命中率低,需要增大shared_buffers,或者检查是否有大量顺序扫描操作绕过了缓存。
主从复制延迟与状态监控读写分离架构下,从库延迟会导致读到的数据不是最新的,引发业务逻辑错误。MySQL中查看从库状态:
SHOW SLAVE STATUS\G
重点关注Seconds_Behind_Master字段,这个值表示从库落后主库的秒数。如果持续增大,说明从库追不上主库。还要看Slave_IO_Running和Slave_SQL_Running是否都是Yes,任何一个为No都说明复制中断。常见中断原因包括:主键冲突、缺少对应表、SQL线程执行出错。跳过错误可以临时使用:
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; START SLAVE;
但这是治标不治本,必须查看错误日志找到根本原因。PostgreSQL中查看复制延迟:
SELECT
client_addr,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes
FROM pg_stat_replication;
lag_bytes表示主库已发送但备库尚未应用的WAL字节数,这个值持续增长就要警惕。复制延迟的根因排查,要看主库是否有大事务、网络带宽是否打满、备库硬件性能是否不足、备库是否有长查询阻塞WAL应用。
日常巡检与自动化监控很多故障如果能在早期发现,就不会演变成事故。建立一套简洁有效的日常巡检命令清单很有必要。MySQL每日必查项:连接数使用率、缓冲池命中率、慢查询数量、锁等待数量、从库延迟、磁盘空间使用率。这些都可以写成脚本定时执行并输出到监控系统。PostgreSQL同理,重点检查:连接数、缓存命中率、锁等待、复制延迟、WAL生成速率、autovacuum是否正常运行。vacuum相关检查尤其重要:
SELECT relname, last_autovacuum, last_autoanalyze, n_dead_tup FROM pg_stat_user_tables WHERE n_dead_tup > 10000 ORDER BY n_dead_tup DESC;
如果发现大量死元组没有被清理,说明autovacuum配置不合理或者被阻塞,这会导致表膨胀和查询性能下降。排查autovacuum阻塞,要看是否有长事务或者未结束的prepared transaction。
SELECT * FROM pg_prepared_xacts;
如果有记录且存在时间很长,需要及时提交或回滚。这些实操命令和排查路径,是数据库运维中最基础也最核心的技能。遇到问题时,先不要慌,按照连接、查询、锁、资源、复制的顺序逐一排查,绝大多数问题都能在几分钟内定位到根因。
