当前位置:首页 > 问答 > 正文

Oracle数据库访问突然卡住了,教你几招快速排查和解决办法

Oracle数据库用着用着突然就卡住了,点什么都没反应,或者网页转圈圈,这绝对是让运维和开发人员最头疼的问题之一,别慌,遇到这种情况,不要急着重启数据库(重启大法虽然好,但可能会丢失问题现场),按照下面这几招,一步步来排查,很多时候能快速找到症结所在。

第一招:先看看数据库整体状态——是“死”了还是“病”了?

当应用反馈卡顿时,你首先需要登录到数据库服务器上(如果连不上,问题可能更底层,比如网络或服务器挂了),用命令行工具(比如SQL*Plus)尝试连接一个具有DBA权限的用户。

  • 情景A:连不上,报错或超时。 这说明数据库实例可能已经崩溃(crashed)或者监听程序(Listener)出了问题,这时候可以去检查数据库的告警日志(alert log),这个日志文件就像数据库的“黑匣子”,里面会记录任何严重的错误信息,告警日志的位置一般在$ORACLE_BASE/diag/rdbms/<数据库名>/<实例名>/trace/alert_<实例名>.log,快速用tail -f命令查看日志尾部,看有没有ORA-错误代码,比如进程异常终止之类的记录。
  • 情景B:能连上,但非常慢,执行任何命令都卡住。 这说明数据库实例还在运行,但内部可能出现了严重的资源争用或阻塞,这是最常见的情况,我们接下来的几招主要针对这个情景。

第二招:揪出“罪魁祸首”——谁在占用大量资源?

数据库卡顿,十有八九是某些SQL语句或者会话(Session)把系统资源(主要是CPU和I/O)给“吃”光了,我们需要快速找出这些“捣蛋鬼”。

  1. 看系统资源: 在操作系统层面,先用top(Linux)或Task Manager(Windows)命令看看,是不是Oracle的进程(ora_开头或者oracle.exe)占用了极高的CPU或者内存,如果某个进程持续占用CPU 90%以上,那它就很可疑。
  2. 看数据库内部等待事件: 这是Oracle数据库性能调优的核心概念,你可以执行一条简单的SQL来查看当前所有会话都在“等”什么:
    SELECT sid, serial#, username, event, seconds_in_wait, state
    FROM v$session
    WHERE wait_class <> 'Idle' -- 过滤掉空闲等待事件
    ORDER BY seconds_in_wait DESC;

    (来源:Oracle官方文档中关于动态性能视图V$SESSION的用法) 如果发现大量会话都在等待同一个事件,比如db file sequential read(通常指索引读等待,可能磁盘慢)或者enq: TX - row lock contention(行锁等待),那么问题的方向就明确了。

  3. 找出消耗资源最多的SQL: 执行下面的SQL,可以查看当前正在执行或者刚刚执行完的、消耗资源最厉害的语句。
    SELECT sql_id, sql_text, elapsed_time, cpu_time, disk_reads, buffer_gets
    FROM v$sql
    ORDER BY elapsed_time DESC;

    (思路参考自CSDN上多位Oracle DBA的经验分享文章) 找到那个elapsed_time(执行总时间)或cpu_time(CPU时间)高得离谱的sql_id,它就是嫌疑犯。

第三招:解决常见的“卡顿元凶”

根据第二招找到的线索,我们可以采取具体行动:

  • 如果是某个SQL语句慢:

    • 紧急处理: 找到执行这个SQL的会话的SIDSERIAL#,然后用ALTER SYSTEM KILL SESSION 'sid,serial#';命令杀掉这个会话,先恢复系统正常,但这只是治标。
    • 根本解决: 拿着那个sql_id,去分析SQL的执行计划(Explain Plan),看看是不是没有走索引、产生了全表扫描,或者表统计信息过时了,然后通过创建索引、优化SQL写法等手段来根治。
  • 如果是锁等待(Blocking Lock): 经常有用户A更新了一条数据没提交,用户B也要更新同一条数据,B就会被卡住,如果A一直不提交,可能引发一串会话都被卡住。

    • 排查锁链: 执行查询锁的SQL,找到谁阻塞了谁(参考Oracle官方文档中关于DBA_BLOCKERSDBA_WAITERS等视图的用法)。
    • 解决方法: 联系阻塞者(比如某个开发人员)让他提交或回滚事务,如果找不到人,同样可以用KILL SESSION命令结束阻塞源会话。
  • 如果是系统资源瓶颈:

    • CPU高: 除了杀掉耗CPU的会话,还要考虑是不是需要增加CPU,或者优化那些计算密集型的SQL。
    • I/O慢(磁盘读等待严重): 检查存储性能,看看是不是磁盘本身慢了,或者热点数据太多导致缓存(Buffer Cache)不够用,需要频繁从磁盘读,可以考虑增加数据库缓存大小,或者优化SQL减少物理读。

第四招:别忘了检查周边环境

问题不一定出在数据库本身。

  • 网络问题: 应用服务器到数据库服务器的网络是否出现延迟或丢包?可以用pingtraceroute命令简单判断。
  • 应用服务器问题: 是不是应用服务器本身负载太高(CPU、内存耗尽),导致它处理不过来,看起来像是数据库卡了?
  • 监听器问题: 数据库监听器(Listener)负载过高或出现异常,也会导致新的连接无法建立或非常缓慢,可以尝试重启监听器:lsnrctl stop followed by lsnrctl start

总结一下排查流程:

  1. 判断状态:能连上吗?
  2. 定位源头:连不上查告警日志;连得上慢就查资源消耗和等待事件。
  3. 采取行动:根据找到的原因,杀会话、优化SQL、解决锁冲突、扩容资源。
  4. 扩大范围:数据库没问题,就查网络和应用服务器。

平时养成定期监控数据库性能的习惯,比如使用AWR/ASH报告(Oracle自带的性能诊断工具,来源:Oracle官方文档)做定期健康检查,就能防患于未然,减少这种突然卡顿的发生。

Oracle数据库访问突然卡住了,教你几招快速排查和解决办法