选择和更新之间的竞争条件

时间:2011-12-14 00:27:50

标签: oracle oracle11g

我需要更新Oracle数据库中的一行,我不会在基于Web的应用程序中默默地破坏来自其他客户端的更改。

在我目前的系统中,我执行以下操作:

SELECT * FROM table WHERE id=:ID AND lastmodified=:LASTMOD

如果在我们开始时该行仍然存在且具有相同的最后修改日期,我们知道没有人更改它,因此我们可以使用上次修改时间进行更新。

然而,当使用两个会话手动执行此操作时,我注意到两个客户端在大致相同的时间选择可能会错过选择和更新步骤之间的行已更改,因为两者都发生在同一秒或毫秒内

最终结果是我破坏了其他用户的更改,并且没有发出警告。

我在考虑使用SELECT FROM UPDATE,但显然是bad idea(特别是对于网络应用程序),文章建议重新阅读(这就是我上面所做的),但我仍然认为我是面临竞争风险。

编辑:明确表示我很关注时间的引用方式。

4 个答案:

答案 0 :(得分:5)

我假设您的UPDATE声明本身正在验证您在lastmodified声明中所读取的SELECT值,正如ninesided所暗示的那样。

如果lastmodifiedDATE,那么如果同一行中同一行有多次更新,则存在潜在的竞争条件,因为DATE仅具有第二个的粒度。另一方面,如果lastmodifiedTIMESTAMP,则可以发生竞争条件的窗口更加有限,因为TIMESTAMP将具有3到9位数的亚秒精度(在大多数Windows机器上为3,在大多数Unix机器上为6)。虽然并非不可能,但是在同一毫秒或甚至相同的微秒内有两次更新并非不可能。但这并非万无一失。

您可以使用序列生成的值而不是上次修改的日期。这可以保证您不会丢失更新,因为NOCYCLE序列不会返回相同的值两次。但是如果沿着这条路走下去,你要么失去了在每一行上拥有最后更新日期的信息好处,要么在表的每一行中存储了一些额外的数据字节。根据您的应用程序,这些权衡取舍可能是值得的,或者可能会产生比解决方案更多的问题。

答案 1 :(得分:1)

您可以采取的一种方法是,当用户更新表格时,他们会添加

AND lastmodified = :LASTMOD

到update语句的WHERE子句,其中:LASTMOD是用户原始select返回的值。如果更新现在影响了行(SQL%ROWCOUNT=0),那么您知道第二个用户已经更新了该行,因为第一个用户最初运行了他们的选择。

答案 2 :(得分:0)

我不确定SQL内部解决方案,但我假设你正在使用一些脚本语言(php?perl?)来执行web app / backend的东西,所以你可以简单地使用它们来使用文件锁或信号量/互斥锁来锁定写入和等待(如果已锁定)或只是告诉用户稍等片刻再试一次。

作为替代方案,您可以查看MediaWiki源代码。我知道他们有一个保护措施,以避免两个用户在DB中覆盖彼此的内容。说实话,我真的认为维基百科将是一个非常大的平台示例,这种并发编辑可能会在一段时间内不止一次发生。

答案 3 :(得分:0)

另一种选择是在表上启用ROWDEPENDENCIES(这需要重建表!)并使用ORA_ROWSCN伪列。在行与块级别跟踪SCN每行需要6个字节;但是,它小于DATETIMESTAMP列,并且不需要创建其他对象 - 如序列或触发器以确保填充序列。

有关更多信息,请参阅Tom Kyte提出的问题here