2个并发事务中的行数

时间:2018-10-17 07:55:36

标签: oracle transactions rowcount

我有以下代码来更新客户,如果不存在,它将插入/创建客户。

UPDATE CUSTOMER SET ...
IF SQL%rowcount = 0 THEN
  INSERT INTO customer ..

问题是此代码在事务中。在生产中,我会随机收到以下错误:

ORA-00001: unique constraint (..) violated at the line with the INSERT INTO customer .. (see above)

我的问题是Oracle事务如何工作? 我同时有2笔交易(稀有,但可能会发生)。假设事务1已通过rowcount = 0的IF传递,但尚未提交。假设事务2已通过rowcount = 0的IF(因为事务1尚未插入任何东西)。然后事务1提交。然后事务2提交,它将“保持”行计数= 0,或者再次检查IF SQL%rowcount = 0然后再考虑事务1提交了什么?

我描述的并发性问题是我能想到的生产中发生的上述随机错误的唯一原因。

设置特定的交易隔离级别可能会有所帮助?

1 个答案:

答案 0 :(得分:0)

是的,这就是事实。时机必须正确,这就是为什么它是断断续续的。如果打开两个sqlplus会话,则可以验证这一点。

  1. 会话A:更新,未更改任何行,因此start = pygame.time.get_ticks() while 1: # ... code omitted. now = pygame.time.get_ticks() # When 20000 ms have passed. if now - start > 20000: start = now enemy = Enemy(60, 200, player) enemy_list.add(enemy) = 0。
  2. 会话B:更新,未更改任何行,因此SQL%ROWCOUNT = 0。
  3. 会话A:通过SQL%ROWCOUNT条件,插入也通过。
  4. 会话B:通过IF条件,插入也通过。
  5. 会话A:提交。
  6. 会话B:提交。糟糕,存在约束冲突,因为现在我可以看到会话A的更改。

更改隔离级别将无济于事。 Oracle没有任何隔离级别可让您查看另一个会话中的未提交更改(这是一件好事)。

要做的第一件事是将IFUPDATE更改为INSERT语句。这样,您只有一条成功或失败的语句。就我而言,以条件分隔插入和更新是一种反模式。像这样:

MERGE

MERGE INTO customer USING ( SELECT customer_name FROM wherever ) source ON ( source.customer_name = customer.customer_name ) WHEN NOT MATCHED THEN INSERT VALUES ( source.customer_name ) WHEN MATCHED THEN UPDATE SET ( customer_name = source.customer_name ); 的缺点是它没有MERGE子句,因此,如果需要它,则必须使用select / insert。

第二,要停止同时插入两个会话,您需要执行以下操作:

RETURNING INTO

我通常不喜欢创建自定义锁,但是我不知道有其他解决方法。这将阻止其他会话修改表,直到第一个会话提交为止,尽管他们可以查询它。这意味着对该表的访问已序列化,因此,如果您有很多会话试图更新该表,则可能不可接受。