适用范围Oracle任何平台上的企业版数据库
适用对象所有数据库管理员和数据库支持人员
本文目的这篇文章主要描述用于诊断数据库hanging和性能问题的方法和 工具这些问题可能是由于调整问题设计问题或者Oracle的bug引起的
将讨论如下的诊断步骤
) 描述清楚出现的现象问题
) 寻找具体错误
) 收集操作系统级别上的数据
) 获取systemstate和hanganalyze的dump
) 获取STATPACK的输出报告
) 获取PROCESSSTATE的dump
注可能很多时候没有必要关闭数据库来停止hanging建议如果要关闭数据库之前获取这些诊断信息以便找出错误的原因所在
下面就来具体讨论如何诊断数据库Hanging问题
描述清楚出现的现象问题
先弄清楚运行的数据库版本需要完整的版本号例如
确定当前数据库是否是真的hanging还是处于活动状态但是运行的非常慢?检查下在Alert文件中是否还有日志切换检查当前的CPUI/O内存的利用率
查看数据库hanging的开始时间持续了多长时间?数据库hanging是否是突然发生还是由于增加的活动事务导致性能的逐步降低?当前有多少的连接用户?最近的系统负载是否是在上升?
是否在初始化参数文件中设置了任何event?数据库当前正在做什么类型的事务?数据库的数据量多大?
数据库是运行在集群环境吗?如果是集群数据库那么关闭其他实例就留下一个实例问题是否还持续存在?这里讨论的某些解决方法适用于集群数据库但是大部分的方法不适合例如一个不大的buffer cache通常对于集群数据库来说意味着较好的性能关于集群数据库的大部分hanging的问题这里不做讨论其中包括PCM锁问题pinging空间管理问题节点间并行查询调优共享磁盘或者虚拟共享磁盘问题网络问题DLM问题等
数据库是运行在MTS环境下吗?如果取消MTS是否问题持续存在?是否使用了Oracle的应用或者工具?最近是否升级了数据库应用工具或者操作系统硬件?问题发生的频率?是否能够重现问题?
是否整个数据库都被hanging?
所有的实例?所有的连接?所有的操作?所有的节点?
首先确认是否能够执行查询select * from dual?日志文件多久切换一次?如果在Alert日志中有归档相关的错误信息那么可以着手解决归档错误问题因为归档问题经常会挂起数据库例如归档目的地空间满了或者数据库处于归档模式下但是ARCH进程被停止了一般可以先以sysdba权限连接到数据库中执行ARCHIVE LOG LIST查看数据库是否归档模式是否启用了自动归档一般如果没有启用自动归档就很容易挂起数据库了这个时候通常的做法就是把数据库改成自动归档模式或者是非归档模式
一个指定的SQL语句操作?
) 如果是由于指定的SQL语句导致数据库挂起先执行带有timed_statistics参数的TKPROF输出报告以及SQL语句的执行计划然后就需要分SQL语句类型来分析了
) 如果是select语句那么这个SQL语句应该是需要被调整如果是一个非常复杂的SQL语句那么尝试是否可以中断
) 如果是一个并行查询语句可以参考监控当前并行查询运行状况脚本获得并行查询的执行计划可能是空间事务竞争如果在Alert日志文件中出现ORA错误那么请将临时表空间的参数pct_increase设置为以便禁止SMON进程接合连续的extents因此减少查询slaves的竞争同时将数据文件尽量分散到不同的磁盘上去减少磁盘I/O的竞争适当增加sort_area_size的大小可能会减少并行度
) 如果是DML语句那么可能是由于锁导致的需要去获取v$lock的输出信息关于锁的信息可以参考返回锁信息脚本查看DML语句的对象上是否有限制或者触发器有可能产生级联锁问题把索引建立在相关的外键列上这样会改变在父表上的锁行为
) 如果是DDL语句可能是一个数据字典的相关问题如果是create index语句则可能是一个空间事务竞争问题调整I/O是一个比较好的方法分布式I/O分开索引和数据的存放空间并行执行都是比较有用的方法还可以设置初始化参数pre_page_sga为true
指定的数据库对象?
在指定对象能是否能做任何操作?做一个select count(*)是否有问题?如果只是update该对象存在问题那么可能锁了可以从上面))中的脚本获取锁的信息
是否预先分配好了空间给这个对象?如果是那么将提高HWM并且导致全表扫描以至于让数据库看起来像是挂起了全表扫描总是会扫描HWM即使表只存在很少的数据解决方案就是尽量避免预分配extents除非马上要执行一个大的并行插入或者常规的装载千万不要在直接装载的时候预分配extents
如果对象是一个表那么可以尝试
ANALYZE TABLE <tablename> VALIDATE STRUCTURE CASCADE
是否有报错如果有报错意味着表或者表上的索引存在坏块了如果没有报错那么继续尝试下面的SQL语句得到相应的的信息
块级上的空间信息一个高的chain out也可能是问题的一部分
SELECT *
FROM sysdba dba_tables
WHERE table_name = <TABLENAME>
如果你有很多的更新和删除操作那么一个不适合的索引也会造成问题下面的SQL语句能帮你得到相关的索引信息
SELECT i*
FROM sysindex_stats i sysdba_indexes d
WHERE iname = dindex_name
AND dtable_name = <TABLENAME>SELECT i*
FROM sysindex_stats i sysdba_indexes d
WHERE iname = dindex_name
AND dtable_name = <TABLENAME>
如果是一个视图那么需要查看视图建立在的表的信息
SELECT text
FROM sysdba_views
WHERE view_name = <VIEWNAME>
大规模的更新操作(例如使用SQLLDRIMPORT或者批处理操作)?
这些操作上的表上存在有哪些索引?是否这些更新操作是在数据库高峰时期运行的?是否在Alert文件中存在有checkpoint not complete的错误信息?如果有表明重做日志文件太小了需要调整它们是否表空间被置于在热备模式下?(v$backup)如果表空间处于热备模式那么产生日志records而不是vectors在一个大的更新操作中就可能导致相当多的竞争和性能下降
如果是一个SQLLDR操作是否使用了传统路径方式?是否使用了REPLACE选项?(推荐使用TRUNCATE选项)在SQLLDR的控制文件中是否有sql functions?是否采用了readbuffersbindsizerowsparallele方式?
如果是一个IMPORT操作是否使用了commit=yindexes=yconstraints=y这些参数?是否增大了buffer?
如果在update期间有很多的用户在操作那么容易造成资源竞争导致系统变慢回滚段redo latches i/o和数据缓沖区都可能成为竞争的区域我们可以从V$session_wait以及statpack中获取更多关于具体竞争的相关信息
指定的包存储过程或者PRO*C应用?
首先需要查看这些包存储过程或者PRO*C的具体内容其中的哪个语句一直在执行?去掉这个语句后相应的程序是否能运行正常?如果是存储过程那么可以利用DBMS_ALERT查看那里开始挂起了如果是PRO*C程序那么可以使用tkprof来识别parsing是否是瓶颈?如果是那么可以使用预编译参数
hold_cursor和release_cursor来调整如果是一个包那么尝试是否能单独执行每个存储过程?查看是否包和存储过程被刷新出了共享池如果是可以尝试把这些包和存储过程pin在共享池中
SELECT *
FROM v$db_object_cache
WHERE name = <NAME>
仅仅是远程访问?
是否可以执行select * from dual@db_link?是否能够连接到远程的机器上执行本地的操作?是否是在做一个分布式的更新操作?初始化参数distributed_lock_timeout设置了多少?是否正在刷新快照?是否使用了对称复制?尝试做一个tkprof输出得到相应的执行计划执行计划中如果标明是REMOTE的那么就是远程执行的操作如果在一个远程的机器上join两张表那么请尝试在本地节点上生成join视图之后查询这个视图在sql操作中设置ARRAYSIZE多使用pl/sql而不是单独的sql语句使用显性游标这些都可以减少网络的负载
使用第三方应用软件的操作
是否能在sqlplus中重现问题?如果不可以重现那么就需要联系第三方应用软件供应商寻求帮助
数据关闭/启动过程中出现挂起
关闭使用的什么参数?数据库是否crash了?如果是数据库启动挂起并且非正常关闭但是在Alert日志文件中没有任何的错误那么可能只是一个正常的实例恢复如果在Alert文件中出现内部错误系统错误那么请尝试正常的关闭数据库然后启动
下面是一个正常实例恢复的时候在Alert日志文件中列出的相关信息
Starting ORACLE instance (normal)
…………………
Starting up ORACLE RDBMS Version
System parameters with nondefault values
……………………
Beginning crash recovery of threads
Started redo scan
Completed redo scan
redo blocks read data blocks need recovery
Recovery of Online Redo Log Thread Group Seq Reading mem
Completed redo application
Completed crash recovery at
Thread logseq block scn
data blocks read data blocks written redo blocks read
SMON enabling cache recovery
SMON enabling tx recovery
Completed ALTER DATABASE OPEN
如果正常的关闭或者immediate关闭挂起那么意味着Oracle正在等待激活的会话退出
在Unix系统上还可以寻找正在挂起的启动或者关闭操作然后trace pid
寻找错误
) 检查AlertSIDlog告警日志文件看看是否存在错误信息此告警日志文件的具体路径位置可以由初始化参数中的background_dump_dest中获得或者在sqlplus中执行show parameter dest获得
) 检查上述目录中的在数据库挂起时间生成的跟蹤文件查看里面的错误信息不用搜索整个跟蹤文件相关的错误信息一般都是在文件的最开始出现
) 如果是远程访问的问题那么还需要检查sql*net跟蹤目录下的跟蹤文件
) 检查系统信息的错误日志在大多数的Unix下都是在/var/adm目录下
输出查看相关的V$视图
当数据库挂起的时候执行下面的查询
SPOOL v_viewslog
SELECT *
FROM v$parameter
SELECT class value name
FROM v$sysstat
SELECT sid id id type lmode request
FROM v$lock
SELECT llatch# nname hpid lgets lmisses
limmediate_gets limmediate_misses lsleeps
FROM v$latchname n v$latchholder h v$latch l
WHERE llatch# = nlatch#
AND laddr = hladdr(+)
SELECT *
FROM v$session_wait
ORDER BY sid
/* 重复最后一个查询最少三遍以确定哪个在重复等待*/
SPOOL OFF
如果是指定的查询被挂起了可以使用下面的查询找出相应的查询SQL语句
通过操作系统上的PID找出相应的SQL语句的SID
SELECT ssid pspid
FROM v$session s v$process p
WHERE spaddr = paddr
AND …… < pspid = <os pid> or perhaps
ssid = <sid from v$session> >
然后通过SID找出相应的SQL语句的具体内容
SELECT ssid sstatus qsql_text
FROM v$session s v$sqltext q
WHERE ssql_hash_value = qhash_value
AND ssql_address = qaddress
AND ssid = <sid>
order by qpiece
查询V$SESSION_WAIT视图看看当前的等待事件
column sid format
column seq# format
column wait_time heading WTime format
column event format a
column p format
column p format
column p format
select sideventseq#pppwait_time from V$session_wait
where sid=<SID>
order by sid
查询当前挂起数据库的SQL语句中的lockwait设置的是多少如果非空那么看看什么锁住了当前对象是什么类型的锁
SELECT lockwait
FROM v$session
WHERE sid = <sid>
col Username format A
col Sid format heading SID
col Type format A
col Lmode format heading HELD
col Request format heading REQ
col Id format
col Id format
select SNUsername MSid MType
DECODE(MLmode None Null Row Share Row
Excl Share S/Row Excl Exclusive
LTRIM(TO_CHAR(Lmode))) Lmode
DECODE(MRequest None Null Row Share Row
Excl Share S/Row Excl Exclusive
LTRIM(TO_CHAR(MRequest ))) Request
MId MId from V$SESSION SN V$LOCK M
WHERE (SNSid = MSid and MRequest ! = )
or (SNSid = MSid and MRequest = and Lmode != and (id id)
in (select SId SId from V$LOCK S where Request != and SId
= MId and SId = MId) ) order by Id Id MRequest
查询v$process视图中的LATCHWAIT设置是多少?如果这个值非空那么继续查是谁保存了这个latch
SELECT latchwait
FROM v$process
WHERE spid = <pid>SELECT latchwait
FROM v$process
WHERE spid = <pid>
column name format a heading LATCH NAME
column pid heading HOLDER PID
select cnameaaddragetsamissesasleeps
aimmediate_getsaimmediate_missesbpid
from v$latch a v$latchholder b v$latchname c
where aaddr = bladdr(+) and alatch# = clatch#
and cname like &latch_name% order by alatch#
上述这些保存了锁和latch的会话是否关闭了终端但是没有退出这可能会导致一个影子进程继续保存那些资源这样就需要杀掉相应的进程可以使用如下语句
alter system kill session <sid serial# from v$session>
如果会话没有被挂起而只是运行缓慢那么需要查看会话的具体信息
SELECT ssid svalue tname
FROM v$sesstat s v$statname t
WHERE sstatistic# = tstatistic#
AND ssid = <sid>
如果会话极度的缓慢或者是被挂起了那么需要查看会话的等待信息
SELECT *
FROM v$session_wait
where sid = <sid>
如果是个分布式事务那么需要在各个节点上都运行如下SQL语句
SELECT * FROM dba_pc_pending
SELECT * FROM pending_sessions$
SELECT * FROM pending_sub_sessions$
SELECT * FROM dba_pc_neighbors
如果是MTS服务器那么可以查看一下当前的dispatcher的繁忙程度
select namenetworkstatus
(busy /(busy + idle)) * % of time busy
from v$dispatchers
还可以查看V$SHARED_SERVERS视图获取相应的信息
select namestatusrequests (busy /(busy + idle)) * % of time busy
from v$shared_servers
收集操作系统的相关信息
) 简短的描述你的架构包括CPU的数量磁盘的数量是否使用了裸设备使用了NFS文件系统共享磁盘……是否镜像了这些?
) 测量不同操作系统级别的活动过量的CPU或者I/O页面交换区等有许多的工具可以监测这些例如TOP
Unix上的工具SARVMSTATNETSTATTOPTRUSS等
Vms上的工具MONITORANALYZEPROCESS等
Windows上的工具Performance Monitor Event Monitor Dr Watsonqslice等
) 检查系统的日志文件在大多数Unix平台上日志文件都存在于/var/adm目录下
获取SYSTEMSTATE和HANGANALYZE的dump
这两个命令将在user_dump_dest目录下创建一个非常大的跟蹤文件初始化参数文件中的MAX_DUMP_FILE_SIZE参数确定了能够容纳的最大跟蹤文件的大小使用Oradebug命令设置unlimit将能允许执行一个完全的dump请确认整个数据库已经挂起或者即将挂起并且在Alert告警日志文件中没有任何归档的错误的时候才可以做此操作
注意当数据库是集群数据库的时候如果需要诊断挂起的问题则需要在每个节点上都执行systemstate dump操作建议做次左右以便能够确定数据库或者进程是否是真的挂起还是激活状态
对于Oracle x to x的版本
$ svrmgrl
svrmgr> connect internal
svrmgr>ALTER SESSION SET EVENTS IMMEDIATE TRACE NAME HANGANALYZE LEVEL
wait seconds
svrmgr>ALTER SESSION SET EVENTS IMMEDIATE TRACE NAME HANGANALYZE LEVEL
EXIT …… then reconnect
svrmgr>ALTER SESSION SET MAX_DUMP_FILE_SIZE=UNLIMITED
svrmgr>ALTER SESSION SET EVENTS IMMEDIATE TRACE NAME SYSTEMSTATE LEVEL
wait seconds
svrmgr>ALTER SESSION SET EVENTS IMMEDIATE TRACE NAME SYSTEMSTATE LEVEL
wait seconds
svrmgr>ALTER SESSION SET EVENTS IMMEDIATE TRACE NAME SYSTEMSTATE LEVEL
对于Oracle 或者更高的版本
$ sqlplus /nolog
connect / as sysdba
oradebug setmypid
oradebug unlimit
oradebug hanganalyze
wait seconds
oradebug hanganalyze
oradebug dump systemstate
wait seconds
oradebug dump systemstate
wait seconds
oradebug dump systemstate
获取STATPACK的输出报告
对于如何得到和分析statpack的输出报告可以参考eygle的个人网站上的文章
获取PROCESSSTATE的dump
获取processstate dump可以使用如下命令建议执行三遍将可以在user_dump_dest目录下找到生成的跟蹤文件
$ sqlplus /as sysdba
oradebug setospid <process ID>
oradebug unlimit
如果要获取errorstacks dump可以使用如下命令建议执行三遍同样可以在user_dump_dest目录下找到生成的跟蹤文件
$ sqlplus /as sysdba
oradebug setospid <process ID>
oradebug unlimit
oradebug dump errorstack