如何以时间戳原子更新行?

时间:2013-04-16 19:05:38

标签: transactions oracle11g sql-update rowlocking

我有一个分布式应用程序,它使用数据库来同步客户端。客户端将尝试更新记录,但只有在过去1小时内没有其他客户端更新记录时才会这样做。


这是缩小的代码和困境:

假设一个客户端尝试将字段更新为“红色”(检查过去一小时内没有其他人更新过该字段):

UPDATE mytesttable
SET Status = 'Red',
    TimeOfLastUpdate = sysdate 
WHERE TimeOfLastUpdate < sysdate-1/24

同时,另一个客户端尝试将其更新为“绿色”(检查过去一小时内没有其他人更新过它):

UPDATE mytesttable
SET Status = 'Green',
    TimeOfLastUpdate = sysdate 
WHERE TimeOfLastUpdate < sysdate-1/24

我可以假设只有一个客户端才能成功更新行吗?


这就是我认为答案是“不”的原因:

由于Oracle必须在之前解析sysdate ,它才会获取行级更新锁(因为它必须首先使用它来查找行),所以看起来有一场比赛条件:

  1. 客户端“红色”计算sysdate
  2. 客户端“绿色”计算sysdate
  3. 1小时通过
  4. 客户端“红色”使用旧TimeOfLastUpdate
  5. 更新sysdate
  6. 客户端“绿色”使用旧TimeOfLastUpdate更新sysdate(因此会更新两次)

  7. 我认为这是一种竞争条件吗?如果没有,我错过了什么?

    如果是这样,是否有一个有效,更可靠的解决方案来解决这个问题?

2 个答案:

答案 0 :(得分:0)

根据我的理解,这些场景应该在代码级别处理,而不是留在后端。在编程期间可以轻松处理同步。尝试以这种方式实现。

答案 1 :(得分:0)

对我有用的解决方案:加倍update。例如:

UPDATE mytesttable
SET TimeOfLastUpdate = TimeOfLastUpdate 
WHERE TimeOfLastUpdate < sysdate-1/24

UPDATE mytesttable
SET Status = 'Red',
    TimeOfLastUpdate = sysdate 
WHERE TimeOfLastUpdate < sysdate-1/24

COMMIT;

('Green'的类似代码)

第一个update不会改变任何内容,但会抓住该行的锁定,直到调用commit才会释放。

第二次更新使用sysdate更新行,该行保证大于或等于获取锁定的时间,从而防止竞争条件。 (除非sysdate及时倒退。)