Oracle RAC如何有效解决跨节点并发事务冲突导致的死锁问题?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1038个文字,预计阅读时间需要5分钟。
探讨相关主题
oracle rac 中死锁无法靠“重试”或“加索引”解决,核心矛盾是全局锁检测延迟 + 跨实例资源协商机制暴露了事务设计缺陷。必须从 lmd 进程行为、xid 追踪和业务执行顺序三处下手。
死锁报错后为什么看不到详细 TRACE?
RAC 下死锁不生成 _ORA_*.TRC,而是由 LMD 进程 dump 出 _LMD_*.TRC,内容精简且不含完整 SQL 绑定值或行级上下文。这不是日志配置问题,是架构决定的——LMD 只管协调,不参与事务执行逻辑。
- 别在 alert.log 里翻 “ORA-00060” 后直接查 SQL,它只告诉你“发生了”,不告诉你“哪两行、哪两个事务、谁先锁的”
- 真正线索藏在
v$transaction和gv$session的XID字段里:发生死锁时,两个会话的XID(事务 ID)必然同时出现在阻塞链中 - 用
LOGMINER对应时间窗口的归档日志解析这两个XID,才能还原出每个事务实际执行的全部 DML 语句及顺序
为什么死锁检测要等 60 秒?
单机死锁秒级响应,RAC 默认 60 秒,根源在 _lm_dd_interval 隐含参数控制的 LMD 全局死锁扫描周期。这不是 bug,是权衡:频繁跨节点广播检测消息会压垮私网。
- 该参数不能设为 0(10.2.0.3+ 版本启动即失败),也不建议调到低于 30 秒——实测会导致 LMD CPU 持续超 80%
- 不同实例可设不同值(如 inst1=60, inst2=45),但不解决根本问题,只改变“谁被 kill”的随机性
- 真正要盯的是 AWR 中
enq: TX - row lock contention的平均等待时间,若持续 > 500ms,说明已不是检测慢,而是 GC 协调本身卡在中间环节
如何定位跨节点死锁的真实执行路径?
别只看 gv$session.blocking_session,它对瞬时 GC 等待不可靠。用 v$ges_blocking_enqueue 直接抓阻塞源头,再关联 v$lock 查资源类型和对象 ID。
- 执行
SELECT * FROM v$ges_blocking_enqueue WHERE INST_ID = <target_inst>,重点关注RESOURCE_NAME是TX-xxxxxx或HW-xxxxxx的行 - 拿到
GRANTING_INST_ID和REQUESTING_INST_ID后,去对应实例查gv$session中的SID和SQL_ID - 特别注意
RESOURCE_NAME以US-开头的行:这是 undo segment 争用,意味着事务未及时提交或回滚,导致其他实例读一致性请求失败
业务层能做的最有效规避动作
数据库层调参治标,业务层统一访问顺序治本。RAC 死锁 70% 以上源于两个事务以相反顺序更新同一组主键。
- 强制所有服务模块按主键升序更新多行:比如转账场景,总是先更新
account_id小的账户,再更新大的 - 避免在事务内做远程调用(如 HTTP 请求、MQ 发送)后再更新数据库——网络延迟会拉长锁持有时间,放大交叉概率
- 禁用
SELECT FOR UPDATE NOWAIT做乐观锁兜底:NOWAIT 不跳过全局锁协商,反而因快速失败重试制造更多 GC 消息
复杂点在于:HW 锁和 TX 锁常混在一起爆发,而应用日志通常只记录最终失败,不会存下事务开始前的行锁申请序列。所以一旦出现死锁,必须立刻抓取 v$ges_enqueue 快照 + 归档日志位置,否则 5 分钟后线索就不可逆丢失。
本文共计1038个文字,预计阅读时间需要5分钟。
探讨相关主题
oracle rac 中死锁无法靠“重试”或“加索引”解决,核心矛盾是全局锁检测延迟 + 跨实例资源协商机制暴露了事务设计缺陷。必须从 lmd 进程行为、xid 追踪和业务执行顺序三处下手。
死锁报错后为什么看不到详细 TRACE?
RAC 下死锁不生成 _ORA_*.TRC,而是由 LMD 进程 dump 出 _LMD_*.TRC,内容精简且不含完整 SQL 绑定值或行级上下文。这不是日志配置问题,是架构决定的——LMD 只管协调,不参与事务执行逻辑。
- 别在 alert.log 里翻 “ORA-00060” 后直接查 SQL,它只告诉你“发生了”,不告诉你“哪两行、哪两个事务、谁先锁的”
- 真正线索藏在
v$transaction和gv$session的XID字段里:发生死锁时,两个会话的XID(事务 ID)必然同时出现在阻塞链中 - 用
LOGMINER对应时间窗口的归档日志解析这两个XID,才能还原出每个事务实际执行的全部 DML 语句及顺序
为什么死锁检测要等 60 秒?
单机死锁秒级响应,RAC 默认 60 秒,根源在 _lm_dd_interval 隐含参数控制的 LMD 全局死锁扫描周期。这不是 bug,是权衡:频繁跨节点广播检测消息会压垮私网。
- 该参数不能设为 0(10.2.0.3+ 版本启动即失败),也不建议调到低于 30 秒——实测会导致 LMD CPU 持续超 80%
- 不同实例可设不同值(如 inst1=60, inst2=45),但不解决根本问题,只改变“谁被 kill”的随机性
- 真正要盯的是 AWR 中
enq: TX - row lock contention的平均等待时间,若持续 > 500ms,说明已不是检测慢,而是 GC 协调本身卡在中间环节
如何定位跨节点死锁的真实执行路径?
别只看 gv$session.blocking_session,它对瞬时 GC 等待不可靠。用 v$ges_blocking_enqueue 直接抓阻塞源头,再关联 v$lock 查资源类型和对象 ID。
- 执行
SELECT * FROM v$ges_blocking_enqueue WHERE INST_ID = <target_inst>,重点关注RESOURCE_NAME是TX-xxxxxx或HW-xxxxxx的行 - 拿到
GRANTING_INST_ID和REQUESTING_INST_ID后,去对应实例查gv$session中的SID和SQL_ID - 特别注意
RESOURCE_NAME以US-开头的行:这是 undo segment 争用,意味着事务未及时提交或回滚,导致其他实例读一致性请求失败
业务层能做的最有效规避动作
数据库层调参治标,业务层统一访问顺序治本。RAC 死锁 70% 以上源于两个事务以相反顺序更新同一组主键。
- 强制所有服务模块按主键升序更新多行:比如转账场景,总是先更新
account_id小的账户,再更新大的 - 避免在事务内做远程调用(如 HTTP 请求、MQ 发送)后再更新数据库——网络延迟会拉长锁持有时间,放大交叉概率
- 禁用
SELECT FOR UPDATE NOWAIT做乐观锁兜底:NOWAIT 不跳过全局锁协商,反而因快速失败重试制造更多 GC 消息
复杂点在于:HW 锁和 TX 锁常混在一起爆发,而应用日志通常只记录最终失败,不会存下事务开始前的行锁申请序列。所以一旦出现死锁,必须立刻抓取 v$ges_enqueue 快照 + 归档日志位置,否则 5 分钟后线索就不可逆丢失。

