注册
数据库CPU飙升排查与处理
培训园地/ 文章详情 /

数据库CPU飙升排查与处理

Foresee. 2026/06/30 510 0 0

一、现象描述

在日常运维工作中,数据库服务器CPU使用率突然飙升至100%是较为常见的故障现象。该问题通常在业务高峰期、压测期间或定时任务执行时段突现,直观表现为应用响应变慢、SQL执行超时、数据库连接堆积等,严重时可导致业务中断。

二、常见原因分类

业务量突增与并发风暴:短时间内高并发等场景带来瞬时QPS飙升,如很多人同时点击同一个功能,大量会话击穿应用打到数据库层,或应用端故障导致重试风暴,超出数据库承载水位。
锁等待与阻塞堆积:长事务未提交或加锁顺序不一致,引发大面积锁等待,会话堆积后CPU资源被连接管理和锁调度耗尽。
慢SQL与执行计划劣化:表中未添加合理索引或SQL未走索引或统计信息过期,导致优化器选择低效执行计划,大量消耗CPU资源进行全表扫描、大量回表操作产生逻辑读、排序或哈希连接。

三、排查思路与流程

1)整体查看CPU及系统其他资源占用情况

1.1)查看CPU占用率

执行top -c ,输入P 可按照CPU占用率进行排序。使用top命令查看系统整体CPU使用率,重点关注us(用户态)和sy(内核态)的占比,如果us(用户态)占比高且则查dmserver进程占用高,则基本锁定是数据库问题,否则非数据库问题。
image.png

1.2)查看内存占用是否正常

执行free -g查看是否使用到swap,内存不足也可能导致CPU使用率抖动。
image.png

1.3)查看是否存在I/O瓶颈

执行iostat -x 1 ,查看是否存在持续的%util>90%或w_await >20ms的情况。 CPU使用率升高可能由于I/O瓶颈拖累CPU。
image.png

2)查看数据库层面

2.1)查看活动回话数

查看活动回话数是否远高于平常,是否有大量会话堆积,或者已知的业务激增情况,也可以根据同一时间段内CPU占用异常和正常时生成的sql日志情况进行推断,例如CPU占用异常时sql日志生成更频繁。如相差不是特别明显,则继续向慢SQL方向排查。

select * from v$sessions where state='ACTIVE';

2.2)获取执行异常的SQL

2.2.1)检查数据库是否有运行中的异常 SQL(执行速度慢)

SELECT * FROM ( SELECT sess_id, sql_text, datediff (ss, last_recv_time, SYSDATE) Y_EXETIME , SF_GET_SESSION_SQL (SESS_ID) fullsql , clnt_ip FROM V$SESSIONS WHERE STATE = 'ACTIVE' ) WHERE Y_EXETIME >= 2; --执行时间超 2s,可以自定义该时间

2.2.2)查看语句的资源开销

通过V$SQL_STATV$SQL_STAT_HISTORY系统视图查看语句的资源开销(ENABLE_MONITOR=1 才 开 始 监 控),其中V$SQL_STAT记录当前正在执行的SQL语句的资源开销,V$SQL_STAT_HISTORY记录历史SQL语句的资源开销,单机最大行数为 10000。视图官方文档。(打开网页直接搜 SQL_STAT)

select SESSID, LOGIC_READ_CNT, SQL_TXT from V$SQL_STAT order by LOGIC_READ_CNT DESC; select SESSID, LOGIC_READ_CNT, SQL_TXT from V$SQL_STAT_HISTORY order by LOGIC_READ_CNT DESC;

通过以上查询,可以找到逻辑读次数最多的历史SQL和当前SQL。根据查询出来的SQL,可以查看其执行计划信息

  • 关注执行计划中是否出现BLKUP2操作符
    BLKUP2表示通过二级索引回表获取数据,若该操作符对应的ROWS估算值较大(如超过10万行),说明存在大量回表操作。每次回表都是一次独立的逻辑读,大量回表会直接推高CPU的us消耗。

  • 若无BLKUP2回表操作,则需关注是否因缺少正确索引而进行了CSCN2全表扫描
    全表扫描本身并不一定会导致性能问题,但其对缓存池的副作用值得特别关注:数据库执行全表扫描时会先从缓存中寻找数据页,对于未缓存的数据页会触发中断处理,从硬盘拷贝数据到缓存中。这个过程不仅消耗sy(内核态CPU),还会将其他SQL频繁访问的热点数据挤出缓存池。当这些被挤出的热点SQL再次执行时,又需要重新从硬盘读取数据,进一步增加I/O消耗和sy%占用率,形成恶性循环。

2.2.3)通过top命令查看dmserver的各个线程cpu使用率来定位sql

可通过 top -p -H 命令查看dmserver进程的各个线程的CPU使用率,dm_sql_thd线程是数据库执行的sql线程,此类线程占用CPU高,则可以通过此时的线程号在数据库中的v$sessions视图中查询对应的线程会话,验证对应的sql是否执行异常。
image.png
通过此时的线程号在数据库中的v$sessions视图中查询对应的线程会话,验证对应的sql是否执行异常

SELECT * FROM V$SESSIONS WHERE THRD_ID = <pid>;

2.2.4)分析sql日志文件

可通过分析sql日志文件对整体慢sql情况进行分析。
检查数据库的sqllog功能是否开启,PARA_VALUE =1为已开启

select PARA_NAME,PARA_VALUE from v$dm_ini where para_name='SVR_LOG';

如未开启,可以先配置实例目录下的sqllog.ini文件配置文件生成路径和文件大小、数量的信息,再开启sqllog功能,如不修改配置文件则默认生成在软件安装目录的log文件夹下,保留数量为5个,每个128M,文件命名格式为dmsql_DMSERVER_20260624_004004.log

SP_SET_PARA_VALUE(1,'SVR_LOG',1);

收集cpu占用高时段的sqllog文件,使用sqllog分析工具进行分析。

四、应急处理手段

1)获取到大量的update,delete语句时

如获取到大量的update,delete语句时,且执行时间与平时时间段差距非常大。需考虑数据库是否存在阻塞,若数据库发生阻塞,则会产生大量的会话堆积,占用SESSION,导致应用系统无响应、报错数据库达到最大会话数限制等。查询数据库是否存在阻塞,与获取到的异常SQL进行对比。

--使用该sql查询是否存在阻塞,如有结果集则存在阻塞 SELECT DS.SESS_ID "被阻塞的会话ID", DS.SQL_TEXT "被阻塞的SQL", DS.TRX_ID "被阻塞的事务ID", ( CASE L.LTYPE WHEN 'OBJECT' THEN '对象锁' WHEN 'TID' THEN '事务锁' END CASE ) "被阻塞的锁类型", DS.CREATE_TIME "开始阻塞时间", SS.SESS_ID "占用锁的会话ID", SS.SQL_TEXT "占用锁的SQL", SS.CLNT_IP "占用锁的IP", L.TID "占用锁的事务ID" FROM V$LOCK L LEFT JOIN V$SESSIONS DS ON DS.TRX_ID = L.TRX_ID LEFT JOIN V$SESSIONS SS ON SS.TRX_ID = L.TID WHERE L.BLOCKED = 1; --如存在阻塞,根据查询到的"占用锁会话ID"进行关闭会话 SP_CANCEL_SESSION_OPERATION(SESS_ID); SP_CLOSE_SESSION(SESS_ID);

2)如获取到的某一类简单的 SQL 语句在并发场景下执行速度显著下降

如获取到的某一类简单的 SQL 语句在并发场景下执行速度显著下降,此时需要分析此条 SQL 的执行计划,一般是由于此 SQL 回表太多引起的,当高并发上来时,回表会消耗主机大量资源(sys),引起 SQL 执行缓慢,造成系统瓶颈,情况复杂时会导致系统处于瘫痪状态,占用别的 SQL 系统资源,导致其他SQL执行缓慢。
出现此问题则需要消除 SQL 中的回表操作符,常用的方法有:创建适当的索引(包括组合索引),select 查询选出的结果集中不要出现无关的字段,尽量使用索引中的字段,减少执行计划中的回表操作符。或在对应的表上创建适当的聚集索引或者组合索引,减少执行计划中的回表操作符。

3)在紧急情况下,确认是否可以限流、强制终止

必须先与用户或相关负责人确认是否可以kill会话,并告知风险

3.1)终止异常会话

定位到异常会话的sess_id后在数据库里面执行 SQL 语句

sp_close_session(sess_id);

3.2)批量终止异常会话

这种一般都是大量的几乎相同的sql占满会话资源,使用下面的方法先查询一下再执行关闭会话的操作

BEGIN FOR V_SESSID IN (SELECT SESS_ID FROM V$SESSIONS where CLINT_IP !=:1 and 条件) LOOP SP_CLOSE_SESSION(V_SESSID.SESS_ID); END LOOP; END;

五、持续观察数据库服务中是否还存在SQL的执行耗时异常情况

建议使用数据的定时作业的方式进行监控

1)定时记录记录执行时间超2s的sql

SELECT * FROM ( SELECT sess_id, sql_text, datediff (ss, last_recv_time, SYSDATE) Y_EXETIME, SF_GET_SESSION_SQL (SESS_ID) fullsql, clnt_ip FROM V$SESSIONS WHERE STATE = 'ACTIVE' ) WHERE Y_EXETIME >= 2;

2)定时查询v$sessions视图信息并获取完整sql

SELECT sess_id "会话号", SF_GET_SESSION_SQL (SESS_ID) "会话执行完整SQL", datediff (ss, last_recv_time, SYSDATE) EXEC_TIME, --执行时间 user_name, TRX_ID, CREATE_TIME, LAST_RECV_TIME, LAST_SEND_TIME, clnt_ip "会话发起IP" FROM V$SESSIONS WHERE STATE = 'ACTIVE';
评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服