确定调度/最低隔离级别:Oracle SQLPlus并发事务场景技术咨询
分析你的并发事务调度与最低隔离级别
咱们一步步拆解你的Oracle并发场景问题哈:首先先把两个事务的核心数据库操作提炼出来(去掉SQL*Plus的工具配置命令,这些不影响事务逻辑):
事务1(T1)核心操作
- 读取
EMPLOYER表中位于新南威尔士州的员工信息:SELECT ename, city FROM EMPLOYER WHERE state = 'New South Wales'; - 更新
POSITION表中薪资超过10万的员工奖金为薪资的1/10:UPDATE POSITION SET bonus = salary / 10 WHERE salary > 100000; - 提交事务:
COMMIT;
事务2(T2)核心操作
- 接收用户输入的额外奖金值,给
POSITION表中编号小于7的员工追加该奖金:UPDATE POSITION SET bonus = NVL(bonus, 0) + &AdditionBonus WHERE pnumber < 7; - (还有一个未完成的
UPDATE POSITION操作,假设最终会执行提交/回滚)
一、可能的调度类型
调度分为串行调度和并发调度两种:
1. 串行调度(无并发冲突)
这是最安全的调度方式,两个事务完全按顺序执行,不会出现并发异常:
- 调度A:T1执行完成后再执行T2
顺序:SELECT→UPDATE(T1)→COMMIT(T1)→UPDATE(T2)→ 后续操作 →COMMIT(T2) - 调度B:T2执行完成后再执行T1
顺序:UPDATE(T2)→ 后续操作 →COMMIT(T2)→SELECT→UPDATE(T1)→COMMIT(T1)
串行调度的优点是完全避免并发问题,但缺点是效率低,无法利用Oracle的并发能力。
2. 并发调度(交叉执行)
实际生产中更常见的是交叉执行的调度,比如以下几种典型场景:
- 场景1:T1先读EMPLOYER,然后T2执行UPDATE,接着T1执行UPDATE,最后分别提交
顺序:SELECT(T1)→UPDATE(T2)→UPDATE(T1)→COMMIT(T1)→ 后续操作 →COMMIT(T2) - 场景2:T2先执行UPDATE,然后T1读EMPLOYER,接着T1执行UPDATE,T2完成后续操作后提交,最后T1提交
顺序:UPDATE(T2)→SELECT(T1)→UPDATE(T1)→ 后续操作 →COMMIT(T2)→COMMIT(T1)
这类调度能提升系统吞吐量,但需要配合合适的隔离级别来避免并发异常。
二、最低隔离级别确定
Oracle提供三种隔离级别:READ COMMITTED(默认)、REPEATABLE READ、SERIALIZABLE。我们结合你的事务逻辑来分析最低需要哪个级别:
1. 先明确你的事务潜在的并发风险
你的两个事务的核心冲突点在于对POSITION表的写操作:如果某行员工同时满足salary>100000和pnumber<7,那么T1和T2的UPDATE会修改同一行数据,可能出现丢失更新(比如T2先追加了奖金,T1后续直接把奖金设为薪资的1/10,覆盖了T2的修改)。
另外,T1的SELECT操作读取的是EMPLOYER表,和T2的操作无交集,所以不存在读一致性问题。
2. 不同隔离级别的适配性
(1)READ COMMITTED(默认级别)
- 特点:只能读取已提交的数据,避免脏读;每次查询获取最新的已提交快照。
- 适配性:
- 对于你的场景,如果业务上允许T1和T2的UPDATE互相覆盖(比如T1的奖金设置是优先级更高的规则),那么
READ COMMITTED完全足够。 - 当两个UPDATE操作修改同一行时,Oracle会自动加行级锁,后执行的UPDATE会等待前一个事务提交/回滚后再执行,不会出现脏写。但如果是后执行的事务直接赋值(比如T1的
bonus=salary/10),会覆盖前一个事务的修改,这属于业务逻辑问题,而非隔离级别导致的异常。
- 对于你的场景,如果业务上允许T1和T2的UPDATE互相覆盖(比如T1的奖金设置是优先级更高的规则),那么
(2)REPEATABLE READ
- 特点:事务中第一次查询后,后续查询读取的是事务开始时的快照,直到事务结束;但修改操作仍会获取最新的数据并加锁。
- 适配性:这个级别并不能解决你的丢失更新问题,因为当T1执行UPDATE时,还是会基于最新的已提交数据(包括T2已提交的修改)进行赋值,仍然可能覆盖。所以这个级别对你的场景没有额外价值。
(3)SERIALIZABLE
- 特点:强制事务按串行方式执行,避免所有并发异常(包括丢失更新、不可重复读、幻读)。
- 适配性:如果业务上不允许任何UPDATE覆盖,要求两个事务的修改都必须保留,那么
SERIALIZABLE是需要的——它会让两个事务串行执行,保证先执行的UPDATE生效后,后执行的UPDATE再基于该结果修改(如果要彻底避免覆盖,建议同时修改SQL逻辑,比如把T1的UPDATE改成基于当前奖金追加的形式,或者用乐观锁校验)。
总结
- 如果业务允许UPDATE互相覆盖:最低隔离级别为READ COMMITTED(Oracle默认级别),可以选择任意并发调度或串行调度。
- 如果业务不允许UPDATE覆盖且无法修改SQL逻辑:最低隔离级别为SERIALIZABLE,此时调度会自动串行化,避免交叉执行导致的覆盖。
内容的提问来源于stack exchange,提问作者James H
相关产品推荐
相关产品推荐

