服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > 数据库技术 > Oracle技术 > oracle错误库 > 查看文档

诊断oracle数据库hanging问题

  确定当前数据库是否是真的hanging还是处于活动状态但是运行的非常慢?检查下在alert文件中是否还有日志切换,检查当前的cpu,i/o,内存的利用率。

  将讨论如下的诊断步骤:

  1) 描述清楚出现的现象问题

  2) 寻找具体错误

  3) 收集操作系统级别上的数据

  4) 获取systemstate和hanganalyze的dump

  5) 获取statpack的输出报告

  6) 获取processstate的dump

  注:可能很多时候没有必要关闭数据库来停止hanging,建议如果要关闭数据库之前获取这些诊断信息以便找出错误的原因所在。

  下面就来具体讨论如何诊断数据库hanging问题。

  描述清楚出现的现象问题:

  先弄清楚运行的数据库版本,需要完整的版本号,例如9.2.0.4。

  确定当前数据库是否是真的hanging还是处于活动状态但是运行的非常慢?检查下在alert文件中是否还有日志切换,检查当前的cpu,i/o,内存的利用率。

  查看数据库hanging的开始时间,持续了多长时间?数据库hanging是否是突然发生还是由于增加的活动事务导致性能的逐步降低?当前有多少的连接用户?最近的系统负载是否是在上升?

  是否在初始化参数文件中设置了任何event?数据库当前正在做什么类型的事务?数据库的数据量多大?

  数据库是运行在集群环境吗?如果是集群数据库,那么关闭其他实例就留下一个实例,问题是否还持续存在?这里讨论的某些解决方法适用于集群数据库,但是大部分的方法不适合。例如,一个不大的buffer cache通常对于集群数据库来说意味着较好的性能。关于集群数据库的大部分hanging的问题这里不做讨论,其中包括pcm锁问题,pinging,空间管理问题,节点间并行查询调优,共享磁盘或者虚拟共享磁盘问题,网络问题,dlm问题等。

  数据库是运行在mts环境下吗?如果取消mts,是否问题持续存在?是否使用了oracle的应用或者工具?最近是否升级了数据库,应用,工具或者操作系统,硬件?问题发生的频率?是否能够重现问题?

  是否整个数据库都被hanging?

  所有的实例?所有的连接?所有的操作?所有的节点?

  首先确认是否能够执行查询select * from dual?日志文件多久切换一次?如果在alert日志中有归档相关的错误信息,那么可以着手解决归档错误问题,因为归档问题经常会挂起数据库。例如:归档目的地空间满了,或者数据库处于归档模式下但是arch进程被停止了。一般可以先以sysdba权限连接到数据库中,执行archive log list,查看数据库是否归档模式,是否启用了自动归档,一般如果没有启用自动归档,就很容易挂起数据库了,这个时候通常的做法就是把数据库改成自动归档模式或者是非归档模式。

  一个指定的sql语句操作?

  1) 如果是由于指定的sql语句导致数据库挂起,先执行带有timed_statistics参数的tkprof输出报告以及sql语句的执行计划,然后就需要分sql语句类型来分析了:

  2) 如果是select语句,那么这个sql语句应该是需要被调整,如果是一个非常复杂的sql语句,那么尝试是否可以中断。

  3) 如果是一个并行查询语句,可以参考监控当前并行查询运行状况脚本获得并行查询的执行计划。可能是空间事务竞争,如果在alert日志文件中出现ora-1575错误,那么请将临时表空间的参数pct_increase设置为0以便禁止smon进程接合连续的extents,因此减少查询slaves的竞争。同时将数据文件尽量分散到不同的磁盘上去,减少磁盘i/o的竞争,适当增加sort_area_size的大小可能会‘减少’并行度。

  4) 如果是dml语句,那么可能是由于锁导致的,需要去获取v$lock的输出信息,关于锁的信息可以参考返回锁信息脚本。查看dml语句的对象上是否有限制或者触发器,有可能产生级联锁问题。把索引建立在相关的外键列上,这样会改变在父表上的锁行为。

  5) 如果是ddl语句,可能是一个数据字典的相关问题。如果是create index语句则可能是一个空间事务竞争问题。调整i/o是一个比较好的方法,分布式i/o,分开索引和数据的存放空间,并行执行都是比较有用的方法,还可以设置初始化参数pre_page_sga为true。

  指定的数据库对象?

  在指定对象能是否能做任何操作?做一个select count(*)是否有问题?如果只是update该对象存在问题,那么可能锁了,可以从上面3)、4)中的脚本获取锁的信息。

  是否预先分配好了空间给这个对象?如果是,那么将提高hwm并且导致全表扫描,以至于让数据库看起来像是“挂起”了。全表扫描总是会扫描hwm,即使表只存在很少的数据。解决方案就是尽量避免预分配extents除非马上要执行一个大的并行插入或者常规的装载。千万不要在直接装载的时候预分配extents。

  如果对象是一个表,那么可以尝试

analyze table validate structure cascade;

  是否有报错,如果有报错,意味着表或者表上的索引存在坏块了。如果没有报错,那么继续尝试下面的sql语句得到相应的的信息:

  块级上的空间信息,一个高的chain out,也可能是问题的一部分。

select *
from sys.dba dba_tables
where table_name = '<tablename>';

  如果你有很多的更新和删除操作,那么一个不适合的索引也会造成问题,下面的sql语句能帮你得到相关的索引信息:

select i.*
from sys.index_stats i, sys.dba_indexes d
where i.name = d.index_name
and d.table_name = '<tablename>';select i.*
from sys.index_stats i, sys.dba_indexes d
where i.name = d.index_name
and d.table_name = '<tablename>';

  如果是一个视图,那么需要查看视图建立在的表的信息:

select text
from sys.dba_views
where view_name = '<viewname>';

  大规模的更新操作(例如使用sqlldr,import或者批处理操作)?

  这些操作上的表上存在有哪些索引?是否这些更新操作是在数据库高峰时期运行的?是否在alert文件中存在有"checkpoint not complete"的错误信息?如果有表明重做日志文件太小了,需要调整它们。是否表空间被置于在热备模式下?(v$backup)如果表空间处于热备模式,那么产生日志”records”而不是“vectors”,在一个大的更新操作中,就可能导致相当多的竞争和性能下降。

  如果是一个sqlldr操作,是否使用了传统路径方式?是否使用了replace选项?(推荐使用truncate选项)在sqlldr的控制文件中是否有sql functions?是否采用了readbuffers,bindsize,rows,parallele方式?

  如果是一个import操作,是否使用了commit=y,indexes=y,constraints=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: 10.2.0.1.0.
system parameters with non-default values:
……………………
beginning crash recovery of 1 threads
started redo scan
completed redo scan
120 redo blocks read, 46 data blocks need recovery
recovery of online redo log: thread 1 group 2 seq 143 reading mem 0
completed redo application
completed crash recovery at
thread 1: logseq 143, block 4358, scn 512699
46 data blocks read, 46 data blocks written, 120 redo blocks read
smon: enabling cache recovery
smon: enabling tx recovery
completed: alter database open

  如果正常的关闭或者immediate关闭挂起,那么意味着oracle正在等待激活的会话退出。

  在unix系统上,还可以寻找正在挂起的启动或者关闭操作,然后trace pid。

  寻找错误:

  1) 检查alertsid.log告警日志文件看看是否存在错误信息,此告警日志文件的具体路径位置可以由初始化参数中的background_dump_dest中获得或者在sqlplus中执行show parameter dest获得。

  2) 检查上述目录中的在数据库挂起时间生成的跟踪文件。查看里面的错误信息,不用搜索整个跟踪文件,相关的错误信息一般都是在文件的最开始出现。

  3) 如果是远程访问的问题,那么还需要检查sql*net跟踪目录下的跟踪文件。

  4) 检查系统信息的错误日志,在大多数的unix下都是在/var/adm目录下。

  输出查看相关的v$视图:

  当数据库挂起的时候,执行下面的查询:

spool v_views.log;
select *
from v$parameter;
select class, value, name
from v$sysstat;
select sid, id1, id2, type, lmode, request
from v$lock;
select l.latch#, n.name, h.pid, l.gets, l.misses,
l.immediate_gets, l.immediate_misses, l.sleeps
from v$latchname n, v$latchholder h, v$latch l
where l.latch# = n.latch#
and l.addr = h.laddr(+);
select *
from v$session_wait
order by sid;

/* 重复最后一个查询最少三遍,以确定哪个在重复等待*/
spool off;

  如果是指定的查询被挂起了,可以使用下面的查询找出相应的查询sql语句:

  通过操作系统上的pid找出相应的sql语句的sid:

select s.sid, p.spid
from v$session s, v$process p
where s.paddr = p.addr
and ... < p.spid = <os pid> or perhaps
s.sid = <sid from v$session> >

  然后通过sid找出相应的sql语句的具体内容:

select s.sid, s.status, q.sql_text
from v$session s, v$sqltext q
where s.sql_hash_value = q.hash_value
and s.sql_address = q.address
and s.sid = <sid>
order by q.piece;

  查询v$session_wait视图看看当前的等待事件

column sid format 990
column seq# format 99990
column wait_time heading 'wtime' format 99990
column event format a30
column p1 format 9999999990
column p2 format 9999999990
column p3 format 9990
select sid,event,seq#,p1,p2,p3,wait_time from v$session_wait
where sid=<sid>
order by sid;

  查询当前挂起数据库的sql语句中的lockwait设置的是多少,如果非空,那么看看什么锁住了当前对象,是什么类型的锁。

select lockwait
from v$session
where sid = <sid>;
col username format a15
col sid format 9990 heading sid
col type format a4
col lmode format 990 heading 'held'
col request format 990 heading 'req'
col id1 format 9999990
col id2 format 9999990
select sn.username, m.sid, m.type,
decode(m.lmode, 0, 'none', 1, 'null', 2, 'row share', 3, 'row
excl.', 4, 'share', 5, 's/row excl.', 6, 'exclusive',
ltrim(to_char(lmode,'990'))) lmode,
decode(m.request, 0, 'none', 1, 'null', 2, 'row share', 3, 'row
excl.', 4, 'share', 5, 's/row excl.', 6, 'exclusive',
ltrim(to_char(m.request, '990'))) request,
m.id1, m.id2 from v$session sn, v$lock m
where (sn.sid = m.sid and m.request ! = 0)
or (sn.sid = m.sid and m.request = 0 and lmode != 4 and (id1, id2)
in (select s.id1, s.id2 from v$lock s where request != 0 and s.id1
= m.id1 and s.id2 = m.id2) ) order by id1, id2, m.request

  查询v$process视图中的latchwait设置是多少?如果这个值非空,那么继续查是谁保存了这个latch。

select latchwait
from v$process
where spid = <pid>;select latchwait
from v$process
where spid = <pid>;
column name format a32 heading 'latch name'
column pid heading 'holder pid'
select c.name,a.addr,a.gets,a.misses,a.sleeps,
a.immediate_gets,a.immediate_misses,b.pid
from v$latch a, v$latchholder b, v$latchname c
where a.addr = b.laddr(+) and a.latch# = c.latch#
and c.name like '&latch_name%' order by a.latch#;

  上述这些保存了锁和latch的会话是否关闭了终端但是没有退出,这可能会导致一个影子进程继续保存那些资源,这样就需要杀掉相应的进程,可以使用如下语句:

alter system kill session ''

  如果会话没有被挂起而只是运行缓慢,那么需要查看会话的具体信息:

select s.sid, s.value, t.name
from v$sesstat s, v$statname t
where s.statistic# = t.statistic#
and s.sid = <sid>;

  如果会话极度的缓慢或者是被挂起了,那么需要查看会话的等待信息:

select *
from v$session_wait
where sid = <sid>;

  如果是个分布式事务,那么需要在各个节点上都运行如下sql语句:

select * from dba_2pc_pending;
select * from pending_sessions$;
select * from pending_sub_sessions$;
select * from dba_2pc_neighbors;

  如果是mts服务器,那么可以查看一下当前的dispatcher的繁忙程度:

select name,network,status,
(busy /(busy + idle)) * 100 "% of time busy"
from v$dispatchers;

  还可以查看v$shared_servers视图获取相应的信息:

select name,status,requests, (busy /(busy + idle)) * 100 "% of time busy"
from v$shared_servers

  收集操作系统的相关信息:

  1) 简短的描述你的架构,包括cpu的数量,磁盘的数量。是否使用了裸设备,使用了nfs文件系统,共享磁盘。。。。是否镜像了这些?

  2) 测量不同操作系统级别的活动:过量的cpu或者i/o,页面,交换区等。有许多的工具可以监测这些,例如top。

  unix上的工具:sar,vmstat,netstat,top,truss等

  vms上的工具:monitor,analyze,process等

  windows上的工具:performance monitor, event monitor, dr. watson,qslice等

  3) 检查系统的日志文件,在大多数unix平台上日志文件都存在于/var/adm目录下。

  获取systemstate和hanganalyze的dump

  这两个命令将在user_dump_dest目录下创建一个非常大的跟踪文件,初始化参数文件中的max_dump_file_size参数确定了能够容纳的最大跟踪文件的大小。使用oradebug命令设置unlimit将能允许执行一个完全的dump。请确认整个数据库已经挂起或者即将挂起,并且在alert告警日志文件中没有任何归档的错误的时候才可以做此操作。

  注意:当数据库是集群数据库的时候,如果需要诊断挂起的问题,则需要在每个节点上都执行systemstate dump操作,建议做3次左右,以便能够确定数据库或者进程是否是真的挂起还是激活状态。

  对于oracle 8.0.5.x to 8.1.7.x的版本:

$ svrmgrl
svrmgr> connect internal
svrmgr>alter session set events 'immediate trace name hanganalyze level 3';
wait 90 seconds
svrmgr>alter session set events 'immediate trace name hanganalyze level 3';
exit ... then reconnect
svrmgr>alter session set max_dump_file_size=unlimited;
svrmgr>alter session set events 'immediate trace name systemstate level 10';
wait 90 seconds
svrmgr>alter session set events 'immediate trace name systemstate level 10';
wait 90 seconds
svrmgr>alter session set events 'immediate trace name systemstate level 10';

  对于oracle 9.2.0.1或者更高的版本:

$ sqlplus /nolog
connect / as sysdba
oradebug setmypid
oradebug unlimit
oradebug hanganalyze 3
wait 90 seconds
oradebug hanganalyze 3
oradebug dump systemstate 10
wait 90 seconds
oradebug dump systemstate 10
wait 90 seconds
oradebug dump systemstate 10

  获取statpack的输出报告

  对于如何得到和分析statpack的输出报告,可以参考eygle的个人网站上的文章。

  http://www.eygle.com/archives/2004/11/statspack_list.html

  获取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 3


  阅读关于 oracle 数据库 hanging问题 的全部文章

扫描关注微信公众号