ORA-00060是Oracle数据库中常见的死锁错误,当两个或多个事务互相持有对方需要的锁资源,且都在等待对方释放锁时,就会触发这个错误,Oracle会自动回滚其中一个事务来打破死锁。下面我们先通过一张示意图了解死锁的基本逻辑,再展开具体的内容。

ORA-00060错误基础说明
死锁发生时,Oracle会在告警日志中记录详细的死锁信息,包含涉及的事务、等待的锁类型、相关的SQL语句等。ORA-00060的错误提示通常包含死锁的跟踪文件位置,我们可以通过查看跟踪文件获取更详细的死锁上下文。
常见的锁类型中,行级排他锁(TX锁)是引发ORA-00060最常见的原因,当事务修改某行数据时,会持有该行的TX锁直到事务提交或回滚,此时其他事务想要修改同一行就会被阻塞。
模拟ORA-00060死锁的示例
我们可以通过两个会话同时操作同一张表的不同行,交叉持有并等待锁来模拟死锁,首先需要创建测试表并插入初始数据:
-- 创建测试表
CREATE TABLE deadlock_test (
id NUMBER PRIMARY KEY,
name VARCHAR2(50),
score NUMBER
);
-- 插入测试数据
INSERT INTO deadlock_test VALUES (1, '张三', 80);
INSERT INTO deadlock_test VALUES (2, '李四', 90);
COMMIT;接下来打开两个Oracle会话,按顺序执行以下SQL:
会话1执行步骤
-- 会话1:修改id=1的记录,持有该行的TX锁 UPDATE deadlock_test SET score = 85 WHERE id = 1; -- 此时先不提交,切换到会话2执行操作
会话2执行步骤
-- 会话2:修改id=2的记录,持有该行的TX锁 UPDATE deadlock_test SET score = 95 WHERE id = 2; -- 此时先不提交,切换回会话1执行下一步
会话1再次执行
-- 会话1:尝试修改id=2的记录,此时会话2持有id=2的TX锁,会话1会被阻塞等待 UPDATE deadlock_test SET score = 88 WHERE id = 2;
会话2再次执行
-- 会话2:尝试修改id=1的记录,此时会话1持有id=1的TX锁,会话2会被阻塞等待 UPDATE deadlock_test SET score = 92 WHERE id = 1; -- 此时两个会话互相等待,Oracle检测到死锁后会抛出ORA-00060错误,回滚其中一个事务
执行完成后,其中一个会话会收到ORA-00060的错误提示,另一个会话的UPDATE语句会执行成功。
常见的ORA-00060触发场景
- 交叉更新同一批数据:多个事务按照不同的顺序更新相同的多行数据,比如事务A先更id=1再更id=2,事务B先更id=2再更id=1,就容易出现死锁。
- 外键未加索引:如果子表的外键列没有索引,当父表删除或更新被引用的记录时,会锁定整个子表的相关行,此时多个事务操作父子表就容易触发死锁。
- 批量操作顺序不一致:批量更新数据时,不同事务对数据的主键排序规则不同,导致锁的获取顺序冲突,进而引发死锁。
- 位图索引并发修改:位图索引的锁粒度较大,多个事务同时修改位图索引对应的列时,很容易出现锁冲突导致死锁。
ORA-00060的解决与预防方案
遇到ORA-00060错误时,首先可以查看Oracle生成的死锁跟踪文件,定位具体是哪些SQL和操作引发了死锁,再针对性优化:
- 统一数据操作顺序:所有事务更新多行数据时,按照相同的顺序(比如按主键升序)获取锁,避免交叉等待。
- 给外键添加索引:检查所有外键约束对应的子表列,确保都创建了合适的索引,减少父表操作时的锁范围。
- 缩短事务持有锁的时间:尽量把非必要的逻辑放在事务之外,减少事务的执行时长,降低锁冲突的概率。
- 批量操作先排序:批量更新或删除数据时,先对操作的数据按主键排序,保证所有事务的锁获取顺序一致。
如果线上频繁出现ORA-00060错误,还可以通过监控数据库的锁等待情况,提前发现潜在的锁冲突风险,优化相关业务逻辑。