savepoint仅支持局部回滚,事务必须显式commit或rollback收尾,否则持续挂起并锁资源;命名需带上下文前缀防冲突;自治事务中主事务的savepoint无效;commit后所有savepoint自动失效。

Oracle存储过程里用SAVEPOINT能局部回滚,但必须手动COMMIT或ROLLBACK收尾,否则事务一直挂着——这是最常被忽略的一环。
为什么ROLLBACK TO SAVEPOINT后数据没生效?
因为ROLLBACK TO SAVEPOINT只是撤销该点之后的操作,它不结束事务。事务仍处于“未提交”状态,所有未COMMIT的变更(包括保存点之前的部分)在会话断开前都锁着资源,其他会话查不到、也改不了。
- 常见现象:
UPDATE+SAVEPOINT+INSERT失败 +ROLLBACK TO→ 表里查不到UPDATE那条记录 - 根本原因:没执行
COMMIT或ROLLBACK,事务卡在中间态 - 验证方法:在PL/SQL Developer或SQL*Plus里执行
SELECT * FROM V$TRANSACTION;,能看到未结束事务
SAVEPOINT名字怎么起才安全?
Oracle对保存点名不区分大小写,但名字不能是Oracle保留字,也不能含空格或特殊符号;更重要的是,嵌套调用时容易重名冲突。
- 别用
sp、save这种通用缩写,优先带上下文前缀,比如sp_user_update、sp_batch_import_01 - 避免在循环体里重复声明同名点,MySQL会静默覆盖,Oracle则可能报
ORA-01086: savepoint 'xxx' never established - 如果过程调用了自治事务(
PRAGMA AUTONOMOUS_TRANSACTION),主过程设的SAVEPOINT对它完全无效——两者事务上下文隔离
异常块里ROLLBACK TO之后必须跟COMMIT或ROLLBACK
很多示例只写ROLLBACK TO sp_xxx;就结束,这会导致事务残留。实际生产代码必须显式收尾。
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
ROLLBACK TO sp_insert_start;
p_result := -1;
COMMIT; -- 关键:这里必须COMMIT,否则事务悬空
WHEN OTHERS THEN
ROLLBACK TO sp_insert_start;
p_result := -2;
ROLLBACK; -- 或者彻底放弃整个事务
END;
-
COMMIT适用于“局部失败但前面操作仍要保留”的场景(如扣款成功、充值失败) -
ROLLBACK适用于“只要出错就全退”的强一致性要求 - 千万别漏掉这两句中的任意一个,否则下次调用该过程时可能遇到锁等待或
ORA-00060死锁
COMMIT之后SAVEPOINT自动失效,但不会报错
这是个隐蔽坑:COMMIT一执行,所有该事务内的SAVEPOINT立即消失,但后续若再写ROLLBACK TO sp_xxx,Oracle不提示“点不存在”,而是直接报ORA-01086: savepoint 'sp_xxx' never established,容易误判为语法错误。
- 典型误用:在
COMMIT后还试图ROLLBACK TO,尤其在多分支逻辑里没注意控制流 - 调试建议:在关键
COMMIT后加注释,比如-- COMMIT clears all savepoints - 如果真需要跨
COMMIT的恢复能力,得用闪回查询(AS OF TIMESTAMP)或日志挖掘,不是SAVEPOINT能解决的











