Java高并发下Oracle数据库如何防止死锁_通过SELECT FOR NOWAIT处理排他锁

千晨小哥_5345

千晨小哥_5345

2026-05-08

872人浏览

原创

select …… for update nowait 不能防止死锁,仅能快速暴露锁冲突并立即报错(如ora-00054),避免线程挂起;真正防死锁需统一加锁顺序、缩小锁范围及应用层指数退避重试。

直接结论:用 select ... for update nowait 能避免线程挂起等待锁,但不能防止死锁本身;它只是把“等死锁”变成“立刻报错”,从而让应用层有机会重试或降级,属于防御性手段,不是根治方案。

为什么 SELECT FOR UPDATE NOWAIT 不等于防死锁

死锁是多个事务循环等待对方持有的锁(比如 T1 持有 A 行锁、等 B 行锁,T2 持有 B 行锁、等 A 行锁),Oracle 自动检测并抛 ORA-00060。而 NOWAIT 的作用仅限于:当本事务执行 SELECT ... FOR UPDATE NOWAIT 时,若目标行已被其他事务加了排他锁,不排队、不挂起,而是立刻抛 ORA-00054: resource busy and acquire with NOWAIT specified。

它没改变事务加锁顺序,也没干预锁的持有逻辑——所以如果两个事务仍以不同顺序访问同一组行,依然可能在后续的 UPDATE 阶段触发死锁。

  • NOWAIT 只作用于当前这条 SELECT FOR UPDATE 语句,不影响后续 DML
  • 它不释放已有锁,也不跳过已锁行(那是 SKIP LOCKED 干的事)
  • Java 中捕获 SQLException 且 getSQLState().equals("61000") 或检查 getMessage() 含 ORA-00054 才算正确识别

Java 中怎么写才真正起效

关键不是加不加 NOWAIT,而是加在哪儿、配合什么逻辑。单纯加了但没重试或没统一加锁顺序,等于白搭。

JavaStoryDevelop
JavaStoryDevelop

你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在

下载
  • 必须在事务开启后、实际修改前尽早执行 SELECT ... FOR UPDATE NOWAIT,否则可能在 UPDATE 时才撞上锁,那时已晚
  • 不要只对单行查锁,比如转账场景中,应一次性锁定所有涉及账户:SELECT * FROM account WHERE holder IN (?, ?) FOR UPDATE NOWAIT
  • Java 里需显式捕获异常并控制重试次数,例如用 @Retryable(Spring Retry)或手动 while 循环 + Thread.sleep,避免瞬间重试压垮 DB
  • 连接池要配置合理超时(如 HikariCP 的 connection-timeout),防止连接卡在 NOWAIT 失败后长期闲置

NOWAIT 和 SKIP LOCKED 到底该选谁

适用场景完全不同,混用反而危险。

  • 用 NOWAIT:你希望“强一致性”,某条数据此刻不可用就立刻拒绝,比如库存扣减不允许跳过热门商品(秒杀中不许绕开已锁 SKU)
  • 用 SKIP LOCKED:你接受“最终一致性”,只要能处理任意可用行就行,比如任务队列分发,跳过别人正在处理的 job 行,拿下一个空闲的
  • 二者不能共存:SELECT ... FOR UPDATE NOWAIT SKIP LOCKED 是非法语法(Oracle 报 ORA-02014)
  • MySQL 8.0+ 支持两者共存,但 Oracle 不行——这点容易踩坑,尤其团队有跨数据库经验时

真正防死锁,还得靠设计层面

NOWAIT 是兜底手段,根子上的问题得从 Java 层解决。

  • 所有涉及多行更新的操作,必须约定全局加锁顺序,比如按主键升序排列后再锁:SELECT ... WHERE id IN (101, 99) ORDER BY id FOR UPDATE NOWAIT → 实际锁顺序变成 99、101,避免颠倒
  • 避免在事务中调用外部服务(HTTP / RPC),否则锁持有时间不可控,极易引发长事务和锁竞争
  • 慎用 SELECT FOR UPDATE 在高并发下做“预占位”,它本质是悲观锁,吞吐量天然受限;高频场景优先考虑应用层分布式锁 + 短事务 + 版本号校验
  • 监控必须跟上:定期查 v$locked_object 和 v$session,发现 LOCKED_MODE = 3(X 锁)且 STATUS = 'INACTIVE' 的长时会话,往往是隐患

最常被忽略的一点:NOWAIT 报错后,很多人直接返回失败,但没清理本地缓存或下游状态,导致业务语义错乱。错误处理必须和业务补偿对齐,而不是只盯着 SQL 是否成功。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

oracle java oracle数据库

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

10737

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

7862

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6832

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1164

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

948

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1396

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2829

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20131

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1255

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程