【问题标题】:Block inserts using for update用于更新的块插入
【发布时间】:2015-06-15 06:51:49
【问题描述】:

我正在尝试在 oracle 中使用 select for update 来防止发生插入。例如,假设在一个会话中(自动提交关闭,隔离级别 = 可序列化)地址表不包含任何行,而我在 SESSION1 中这样做:

SESSION1: select * from Address where addressID = 1 for update

现在在 SESSION2:

SESSION2:insert into Address (addressID, street, city,zip) values (1, 'main','ny','12345'); commit;

我还以为被屏蔽了。但是,我发现插入发生了。我能够做到。然后再次进入 SESSION1。

  SESSION1: insert into Address (addressID, street, city,zip) values (1, 'main','ny','12345')

即使在提交之前,这也会产生完整性约束错误。 (不像我预期的那样可序列化的异常)。

为什么会这样?我正在使用 oracle 12c。有几个意想不到的结果。首先,为什么在提交之前我会在 Session1 中收到约束错误? Oracle 不应该看到来自其他会话的插入。其次,Session1中的插入不应该因为“for update”选择而被阻止吗?最后,有没有办法阻止特定键的插入?

【问题讨论】:

  • 如果您的SELECT FOR UPDATE 没有锁定行,它不会对包括会话1 在内的任何会话产生任何影响。您要解决的业务问题是什么?您的插入不会使用序列来生成AddressID,从而消除两个会话尝试插入相同AddressID 值的可能性吗?为什么要在会话 1 中“保留”一个键值?我想您可以插入一个大部分为空的行,然后再填写数据。但这可能会对空间利用产生一些不幸的影响。
  • 请附上会话 1 的 ORA 错误
  • @BarakKedem SQL 错误:ORA-00001:违反了唯一约束 (TEST.SYS_C0010129) 00001。00000 -“违反了唯一约束 (%s.%s)” *原因:尝试了 UPDATE 或 INSERT 语句插入重复键。对于在 DBMS MAC 模式下配置的 Trusted Oracle,如果在不同级别存在重复条目,您可能会看到此消息。 *操作:要么删除唯一限制,要么不插入密钥。
  • @JustinCave 我不一定在这里使用序列。这是我实现 upsert 的方式。之前已经解决过,但我试图按照这里的描述解决它。我的问题是为什么这不起作用。例如,该方法适用于 Mysql 和 Oracle。同样奇怪的是,当事务甚至不应该看到来自会话 2 的插入时(至少在提交之前)会生成重复键异常
  • 用 MERGE 语句实现不满意。

标签: sql oracle locking


【解决方案1】:

请参阅http://www.oracle.com/technetwork/issue-archive/2010/10-jan/o65asktom-082389.html 了解 SERIALIZABLE 隔离级别。

这种程度的孤立是有代价的,而这个代价就是 以下可能的错误:

ERROR at line 1: ORA-08177: can't serialize access for this transaction

每当您尝试更新具有 自您的交易开始后发生了变化。 (请注意,Oracle 试图做 这纯粹是在行级别,但您可能会收到 ORA-08177 错误 即使您有兴趣修改的行尚未修改。 ORA-08177 可能是由于某些其他行被修改 包含您的行的块。)

在您的情况下,它并不完全是更新,但想法是相同的。如果您查询表,它不会“看到”它,因为它是从回滚段读取的。 但是 insert 不同,因为它需要访问真实数据以确保一致性。

至于如何解决它 - 使用命名锁。尽管不应该,但它们仍然可以工作:)

Alter session set isolation_level=serializable; 

declare
  v_lockhandle varchar2(128);
  v_result number;
begin
  dbms_lock.allocate_unique('table_name'||'id'
                           ,v_lockhandle);

  v_result := dbms_lock.request(v_lockhandle
                               ,release_on_commit => true
                               ,lockmode => 6 -- exclusive
                               ,timeout => 0);

  dbms_output.put_line('result: '||v_result);
end;
/

【讨论】:

  • 获得 ORA-08177 对我来说是有意义的,我可以处理它。但是,我收到完整性约束错误。那篇文章中没有任何内容解释为什么会发生这种情况。 SERIALIZABLE 应该防止幻读,这意味着它不应该能够看到来自其他会话的插入。你的想法?
  • SERIALIZABLE 意味着您无法通过查询看到来自其他会话的插入。 Oracle 在内部所做的可能会有所不同。
  • 但是 Session1 中的完整性约束违规意味着它甚至在提交之前就看到了数据。
  • 命名锁的问题是它只有在所有插入都使用命名锁时才有效。因此,任何编写代码或直接对数据库进行更改的人都必须意识到这一点。这可能是一个维护问题。
  • 也许您可以将它们与行前级触发器结合起来?
猜你喜欢
  • 1970-01-01
  • 2012-09-13
  • 2010-11-22
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
  • 2016-01-03
相关资源
最近更新 更多