Oracle表被锁八成是Spring Boot事务未提交,需查v$locked_object定位长事务会话,重点排查@Transactional失效(如调用private方法)、checked异常未回滚、应用崩溃残留连接等问题。

Oracle 表被锁,八成是 Spring Boot 里某个事务没提交、卡住了——不是数据库坏了,是应用层漏了收尾。
查 v$locked_object 确认是不是真有长事务在锁表
别急着 kill session,先确认锁源是否来自你的应用。用 DBA 权限执行:
SELECT l.session_id, s.serial#, s.username, s.machine, s.osuser, s.logon_time,
o.object_name, s.status, s.sql_id
FROM v$locked_object l
JOIN dba_objects o ON l.object_id = o.object_id
JOIN v$session s ON l.session_id = s.sid
WHERE s.username IS NOT NULL;
重点关注 logon_time 明显早于当前时间(比如几小时甚至一天前)、status 是 ACTIVE 或 INACTIVE 但 sql_id 非空的会话。这些大概率就是 Spring Boot 启动后没正常结束的事务。
- 如果
username是你应用配置的数据库用户(如APP_USER),基本可锁定为应用侧问题 -
machine字段常含服务器主机名或容器 ID,能帮你快速定位是哪台服务实例 - 若
sql_id为空但status是INACTIVE,说明事务已挂起但连接未断,更危险——它不跑 SQL,却一直占着锁
@Transactional 方法里调用了非 public 方法,事务实际没生效
这是最隐蔽的“伪事务”陷阱:方法标了 @Transactional,但内部调用了 private 方法做 DML,结果整个事务范围失效,连接不会自动提交/回滚。
- Spring 的代理机制只拦截 public 方法调用;private、protected 或包级方法调用会绕过事务切面
- 这类代码运行时完全不报错,但事务行为等同于无事务——连接保持 open,锁一直挂着
- 检查所有被
@Transactional标注的方法体,确保所有数据修改逻辑都在 public 方法内,或拆到另一个 service bean 中调用
事务方法里吞了异常,setRollbackOnly() 没触发
Spring 默认只对 RuntimeException 和 Error 回滚。如果你在事务方法里 catch 了 IOException 或自定义 checked 异常,又没手动标记回滚,事务就会静默提交——哪怕业务逻辑失败了。
- 现象:日志显示“处理失败”,但数据库里数据却更新了,且锁没释放(因为事务“成功”结束了)
- 正确做法:要么抛出
RuntimeException,要么在 catch 块里加TransactionAspectSupport.currentTransactionStatus().setRollbackOnly() - 特别注意工具类封装的数据库操作(如通用 DAO 层),容易在底层把异常吃掉
Spring Boot 应用重启后连接没清理干净,残留会话锁着表
Oracle 不会主动回收异常中断的应用连接。Spring Boot 进程 crash 或强制 kill -9 后,连接可能还挂在数据库里,锁资源持续占用。
- 观察
v$session中status为INACTIVE、last_call_et超过 3600 秒的会话,大概率是僵尸连接 - 应用侧应配置 HikariCP 的
connection-timeout和idle-timeout,并启用leak-detection-threshold抓连接泄漏 - 生产环境建议加一条定时巡检脚本,每 5 分钟查一次
v$session+v$transaction,自动 kill 掉超时 30 分钟的闲置事务会话
真正难处理的不是锁本身,而是那些 status=INACTIVE 却死握着锁不放的会话——它们不跑 SQL,不占 CPU,监控里毫无波澜,却能让下游所有请求排队等待。排查时别只盯着“正在执行”的语句,得翻 v$transaction 和 v$session 的组合视图,看事务开始时间与会话登录时间是否严重脱节。


















