有没有办法以原子方式执行SQL合并?

时间:2017-02-05 11:08:45

标签: sql database oracle concurrency mvcc

我正在寻找一种以非锁定方式将值元组与随机UUID连接的方法,并且由于并发约束而不会使事务失败。

我需要编辑的表包含应由UUID描述的几个值。在此示例中,该表名为foo,并声明了两个字符串列barqux,它们指向单个字段uuid(bar, qux)在整个表格中必须是唯一的。 UUID本质上是独一无二的。

我想知道SQL(使用Oracle 12c)是否能够以原子方式执行以下操作:

MERGE INTO foo a
  USING (SELECT bar, qux FROM foo b
  ON b.bar = 'a' and b.qux = 'b'
WHEN NOT MATCHED THEN INSERT (a.bar, a.qux, a.uuid)
  VALUES ('a', 'b', 'some-uuid');

SELECT uuid FROM foo WHERE bar = 'a' and qux = 'b'; 

由于我的数据库查询,我希望元组(bar, qux)与随机UUID连接。对于任何并发事务,此UUID必须相同,并且我不希望竞争请求因并发插入另一个(随机)UUID而失败。

作为背景:这些插入是一个相当长时间运行的事务的一部分,这些事务在很大程度上是彼此独立的,但是具有这个共享标识符表,其中值必须不同意。许多编程语言提供CAS,这就是我在这种情况下的目的,但我不知道SQL中的一个微笑功能。

作为一个想法,我想知道是否允许脏读(未提交读取隔离级别)将是一个解决方案但我不知道合并语句是否是原子的并且对其他事务是可见的在这种情况下。(这在Oracle中是不可能的。)数据库是通过JDBC访问的,但是可能来自多个VM节点。

2 个答案:

答案 0 :(得分:2)

您无法通过长时间运行的事务来执行此操作,因为只有在提交事务后插入才会可见。

您需要做的是从应用程序层打开一个新事务,然后再打开MERGE或UPSERT。这样,MERGE / UPSERT原子性由您发出的辅助事务保证。这样,一旦提交了辅助事务,如果您在READ_COMMITTED中运行,则长时间运行的事务将看到更改,但不是SERIALIZABLE。

类似的方法采用基于TABLE的Hibernate标识符。您可以在this article中了解有关它的更多信息。

答案 1 :(得分:1)

您可以将MERGE和SELECT语句封装在使用AUTONOMOUS_TRANSACTION编译指示定义的PL / SQL函数中。

如果由于另一个会话刚刚插入了相同的UUID而导致唯一约束违规,则可以捕获该函数中的异常,然后选择并返回UUID。

这样MERGE语句只会在很短的时间内锁定记录(只要该函数执行)并且您不会推断应用程序当前事务,因为该函数在单独的事务中运行并执行在唯一约束违规情况下的错误处理。