问题描述
在查看了有关隔离级别的Oracle文档之后,我试图确保我正确理解了DML的工作方式。基本用例是,我正在开发一个作业管理服务,该服务触发由其他微服务实现的作业。它通过通过消息代理向它们发送消息来触发这些作业。集中式作业管理服务有多个实例,该实例具有每秒触发一次的计时器。在每1秒运行期间,使用Spring的 @ transaction注释在Java端启动一个事务。然后,Java应用程序调用一个存储过程 (存储的proc是我们公司的安全要求,我们不能直接从我们的Java代码中使用SQL),它将查找处于等待状态且触发时间已准备就绪的所有作业。存储过程将状态从等待状态翻转为运行状态,并返回带有准备运行的作业的resultset。然后,Java端将从resultset创建消息,并将它们推送到代理上。如果推送消息失败,则事务将回滚,这将使受影响的作业回到等待状态。
我已经评估了存储的proc中有几个选项,以获取准备运行的作业并将其更新为运行状态。我的倾向是:
由于将有多个 (大约6个) 作业管理服务的实例,每个调用此存储的proc,我担心竞争条件和两个实例更新相同的行。更新实际上不是一个大问题,因为它们将被更新到相同的状态。更大的问题是归还这些工作并双重触发它们。
这是我认为这是如何工作的。如果我使用READ _ COMMITTED隔离,则每个实例在启动每个语句时都会获得数据的一致视图 (在这种情况下为更新作业的DML)。第一个到达那里的实例将在WHERE子句中找到的行上设置排他锁。如果第二个实例要开始他们的查询,他们将在第一个实例提交数据之前看到数据的视图。它将被锁阻止,并且无法更新该行,但是一旦该行被解锁,它将执行更新并在其批量收集中返回相同的行集。
如果我使用序列化隔离,则每个实例在开始事务时都会获得数据的一致视图。同样的锁定会发生,阻塞也会发生,但是当第二个实例尝试执行更新时,我应该看到一个 “ORA-08177: 无法对该事务序列化访问” 错误。这基本上可以被忽略,因为我知道工作是由不同的实例找到的。
我还考虑过使用SELECT进行更新,然后使用更新而不是更新... 批量收集,但我认为这会给我相同的结果。由于SELECT和UPDATE在两个语句中分开,因此它也可能有更大的冲突机会。
如果可以保证冲突实际上不会引起问题,我宁愿使用更简单的READ _ COMMITTED隔离。否则我好移动到连载隔离。
我也对满足基本要求的其他选项持开放态度-应该只允许一个实例将数据视为等待和准备就绪。
我已经评估了存储的proc中有几个选项,以获取准备运行的作业并将其更新为运行状态。我的倾向是:
TYPE pk_list IS TABLE OF NUMBER INDEX BY PLS_INTEGER; pk pk_list; ... UPDATE jobs SET state = 'RUNNING' WHERE state = 'WAITING' AND SYSTIMESTAMP >= trigger_time RETURNING job_id BULK COLLECT INTO pk; ... -- Use pk to SELECT * FROM jobs returning as SYS_REFCURSOR复制
由于将有多个 (大约6个) 作业管理服务的实例,每个调用此存储的proc,我担心竞争条件和两个实例更新相同的行。更新实际上不是一个大问题,因为它们将被更新到相同的状态。更大的问题是归还这些工作并双重触发它们。
这是我认为这是如何工作的。如果我使用READ _ COMMITTED隔离,则每个实例在启动每个语句时都会获得数据的一致视图 (在这种情况下为更新作业的DML)。第一个到达那里的实例将在WHERE子句中找到的行上设置排他锁。如果第二个实例要开始他们的查询,他们将在第一个实例提交数据之前看到数据的视图。它将被锁阻止,并且无法更新该行,但是一旦该行被解锁,它将执行更新并在其批量收集中返回相同的行集。
如果我使用序列化隔离,则每个实例在开始事务时都会获得数据的一致视图。同样的锁定会发生,阻塞也会发生,但是当第二个实例尝试执行更新时,我应该看到一个 “ORA-08177: 无法对该事务序列化访问” 错误。这基本上可以被忽略,因为我知道工作是由不同的实例找到的。
我还考虑过使用SELECT进行更新,然后使用更新而不是更新... 批量收集,但我认为这会给我相同的结果。由于SELECT和UPDATE在两个语句中分开,因此它也可能有更大的冲突机会。
如果可以保证冲突实际上不会引起问题,我宁愿使用更简单的READ _ COMMITTED隔离。否则我好移动到连载隔离。
我也对满足基本要求的其他选项持开放态度-应该只允许一个实例将数据视为等待和准备就绪。
专家解答
更新总是在当前模式下获取行-这是因为它们现在存在。因此,当一个会话提交时,等待此操作的其他会话中的任何更新都将看到更改。
例如,在一个会话中运行此代码:
如果此时启动第二个会话并运行匿名块,它将等待第一个提交/回滚。当你这样做,你会看到这个输出在第二:
这是因为第二个会话中的更新获取的行,因为它们是在第一次提交之后; 这意味着它没有看到具有等待值的行。
您可以使事务可序列化并忽略ORA-08177错误,但没有必要。
您可以使用SELECT...如果你愿意,也可以更新。为了避免读取一致性问题,批量收集查询输出并使用此数组来驱动更新:
有几个原因,你可能更喜欢这个,而不是简单的更新:
nowait | wait N | 跳过锁定子句
如果由于某种原因,一个更新需要很长时间才能提交,则会话可能会迅速开始堆积。如果你不走运,这可能会使你的应用程序无法使用。这些选项允许事务停止处理,而不是等待 (可能无限期地)。
在更新之前,事务会做 (很多) 其他工作
选择更新也可以在当前模式下工作,在能够锁定行时查看数据。如果查询不返回任何行,则您可能能够立即停止事务,从而避免浪费工作,因为更新不会改变任何内容。
您在事务中有其他DML可能导致死锁
如果事务运行许多更新/删除,则需要注意避免死锁。听起来这里不是这种情况,但是对于一般的事务管理来说,这是值得记住的。
例如,在一个会话中运行此代码:
create table t ( c1 int, c2 varchar2(10) ); insert into t values ( 1, 'WAITING' ); insert into t values ( 2, 'WAITING' ); insert into t values ( 3, 'RUNNING' ); commit; declare recs dbms_sql.number_table; begin update t set c2 = 'RUNNING' where c2 = 'WAITING' returning c1 bulk collect into recs; dbms_output.put_line ( 'Updated ' || recs.count ); for i in 1 .. recs.count loop dbms_output.put_line ( recs(i) ); end loop; end; / Updated 2 1 2复制
如果此时启动第二个会话并运行匿名块,它将等待第一个提交/回滚。当你这样做,你会看到这个输出在第二:
Updated 0复制
这是因为第二个会话中的更新获取的行,因为它们是在第一次提交之后; 这意味着它没有看到具有等待值的行。
您可以使事务可序列化并忽略ORA-08177错误,但没有必要。
您可以使用SELECT...如果你愿意,也可以更新。为了避免读取一致性问题,批量收集查询输出并使用此数组来驱动更新:
declare recs dbms_sql.number_table; begin select c1 bulk collect into recs from t where c2 = 'WAITING' for update; forall i in 1 .. recs.count update t set c2 = 'RUNNING' where c1 = recs (i); dbms_output.put_line ( 'Updated ' || recs.count ); for i in 1 .. recs.count loop dbms_output.put_line ( recs(i) ); end loop; end; /复制
有几个原因,你可能更喜欢这个,而不是简单的更新:
nowait | wait N | 跳过锁定子句
如果由于某种原因,一个更新需要很长时间才能提交,则会话可能会迅速开始堆积。如果你不走运,这可能会使你的应用程序无法使用。这些选项允许事务停止处理,而不是等待 (可能无限期地)。
在更新之前,事务会做 (很多) 其他工作
选择更新也可以在当前模式下工作,在能够锁定行时查看数据。如果查询不返回任何行,则您可能能够立即停止事务,从而避免浪费工作,因为更新不会改变任何内容。
您在事务中有其他DML可能导致死锁
如果事务运行许多更新/删除,则需要注意避免死锁。听起来这里不是这种情况,但是对于一般的事务管理来说,这是值得记住的。
文章转载自ASKTOM,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
评论
相关阅读
【专家有话说第五期】在不同年龄段,DBA应该怎样规划自己的职业发展?
墨天轮编辑部
1464次阅读
2025-03-13 11:40:53
Oracle RAC ASM 磁盘组满了,无法扩容怎么在线处理?
Lucifer三思而后行
894次阅读
2025-03-17 11:33:53
RAC 19C 删除+新增节点
gh
546次阅读
2025-03-14 15:44:18
2月“墨力原创作者计划”获奖名单公布
墨天轮编辑部
498次阅读
2025-03-13 14:38:19
Oracle 如何修改 db_unique_name?强迫症福音!
Lucifer三思而后行
427次阅读
2025-03-12 21:27:56
Oracle DataGuard高可用性解决方案详解
孙莹
372次阅读
2025-03-26 23:27:33
墨天轮个人数说知识点合集
JiekeXu
320次阅读
2025-04-01 15:56:03
XTTS跨版本迁移升级方案(11g to 19c RAC for Linux)
zwtian
297次阅读
2025-04-08 09:12:48
风口浪尖!诚通证券扩容采购Oracle 793万...
Roger的数据库专栏
267次阅读
2025-03-24 09:42:53
切换Oracle归档路径后,不能正常删除原归档路径上的归档文件
dbaking
267次阅读
2025-03-19 14:41:51