今天协助其他部门解决了这个错误。
由于是在其他人的机器上解决的问题,且由于时间比较紧迫,问题需要马上解决,因此没有留下相应的解决步骤,只好根据回忆来描述一下问题,以及解决思路和步骤。
首先看到的现象是通过DBLINK向远端数据库的插入记录时出现了上面的错误,除了这个错误信息外,后面跟着ORA-2063错误。
ORA-02049: timeout: distributed transaction waiting for lockITPUB个人空间0bKK!{0`G7K.avuS
Cause: exceeded INIT.ORA distributed_lock_timeout seconds waiting for lock.ITPUB个人空间B2wT3U3e3Wq
X
Action: treat as a deadlock
看到这个错误,第一个反应是碰到了分布式事务的两阶段提交,本地或远端数据库的事务处于IN DOUBLE状态,需要人工进行干预。
于是询问是否数据库异常关闭过。得到的答复是本地数据库确实在昨天晚上异常关闭,且关闭的时候可能正在执行类似的操作。
看来问题和我预料的基本一致,就是分布式事务在提交和回滚阶段,其中一个数据库发生了异常,导致两个数据库的事务状态不一致所引发的问题。这个时候的做法是人工干预,强制回滚IN DOUBLE状态的事务。
于是查询远端数据库的DBA_2PC_PENDING视图,奇怪的是未发现任何记录。于是查询本地数据库的DBA_2PC_PENDING,也未发现任何的记录。分别查询远端数据库和本地数据库的DBA_2PC_NEIGHBORS视图,仍然没有发现任何的记录。莫非在我检查的这个时间内,Oracle已经自动解决了问题,再次执行远端插入语言,错误依旧。看来可以确定的是,问题并不是分布式事务引起的。
仔细观察错误信息,发现错误信息中虽然包含了分布式事务,但是根据错误信息的描述,错误并不是分布式事务引起的,而是分布式事务无法获取锁资源。看来开始是没有仔细观察错误信息,而仅仅根据分布式事务就盲目作出了错误的判断。
现在的问题其实和普通数据库中的锁问题没有多大的区别,只不过如果目标表被锁,那么本地发起的DML会一直处于等待状态,而不会报错。而远端发起的DML则会在等待时间超过数据库初始化参数distributed_lock_timeout的设置后报错ORA-2049。
确定了问题就好办了,告诉他们只需要找到持有锁的会话,就可以看到是谁锁住了表,如果是被某个人的操作锁住,通知这个人提交就可以了。如果无法使其提交,或者被程序锁住,可以通过ALTER SYSTEM KILL SESSION命令将会话杀掉,这样目标表上的锁就被清除掉了。
不过他们说,他们已经进行了类似的操作,不过那个持有锁的会话没有办法杀掉。没有办法杀掉到并不奇怪,很多时候Oracle的ALTER SYSTEM KILL SESSION命令都无法及时的将会话清除掉,一般情况下,当会话处于繁忙的工作状态,Oracle需要等当前的会话操作告一段落,才能清除会话,而这需要比较长的等待时间。这时一般在操作系统上直接kill对应的进程ID。
查询远端数据库的V$LOCK视图,果然发现有一张表被锁,用V$LOCK的ID1列关联DBA_OBJECTS的OBJECT_ID列,发现被锁住的就是目标表。查询V$SESSION,发现持有锁的会话状态已经是KILLED状态。
现在只有两个方法,一个是等Oracle自己去清理这个会话,当然这可能会等待相当长的一段时间,另一个是利用操作系统命令直接KILL会话对应的进程。由于急需解决这个问题,而采用了后面的方法。不过因为当前操作系统为windows环境,因此没有办法直接杀掉对应的线程。只能采用Oracle提供的orakill命令。
由于很少管理Windows环境下的Oracle,因此orakill命令还是第一次使用,通过帮助看到orakill后面的两个参数分别是sid和thread,就想当然的将会话的SID和进程的SPID作为参数,结果得到了类似下面的错误:
E:\>orakill 135 2415
Could not attach to Oracle instance 135: err = 2
这时才发现orakill里面的SID参数居然指的是Oracle的instance名称,改成实例名称和对应的SPID后,顺利的将会话信息清除。
再次尝试远端插入,插入顺利完成。