此 UPDATE 语句中是不是存在可能的竞争条件?

Posted

技术标签:

【中文标题】此 UPDATE 语句中是不是存在可能的竞争条件?【英文标题】:Is there a possible race condition in this UPDATE statement?此 UPDATE 语句中是否存在可能的竞争条件? 【发布时间】:2011-04-29 02:39:12 【问题描述】:

我正在编写一个同步器软件,它将获取一个数据库中的所有更改并将它们同步到另一个数据库。为此,我在表格T 中添加了两列:

alter table T add LastUpdate rowversion, LastSync binary(8) not null default 0

现在我可以轻松地选择自上次同步以来发生变化的所有行:

select * from T where LastUpdate > LastSync

但是在执行同步之后,我应该使两个字段相等。但是更新行也会更新时间戳,所以我必须这样做:

update T set LastSync=@@DBTS+1 where ID=@syncedId

但我想知道 - 这会一直有效吗?如果我读取了@@DBTS 的值,然后另一个用户在我的行被提交之前设法在某处插入/更新了一行怎么办?这是有风险的代码吗?如果是的话 - 怎样才能做得更好?

【问题讨论】:

顺便问一下,您使用的是哪个版本的 SQL Server? Change Data Capture 是一个选项吗? @Martin Smith - 2008 年,我想。不确定客户有什么。 @Martin Smith - 我检查了变更数据捕获,但那将是矫枉过正。一个简单的时间戳就足够了。我只需要知道哪些记录还需要同步。我不需要完整的历史记录。 【参考方案1】:

将“LastSync”存储在与真实数据相同的表中可能根本不是一个好主意。 尝试将其存储在另一个没有行版本的表中。这样您就可以避免“更新行也会更新时间戳”的问题。

您的同步器软件可以这样工作:

从附加表中获取@LastSync 值 “选择@ThisSync = max(LastUpdate) from T where LastUpdate > @LastSync” "Select * from T where LastUpdate > @LastSync 和 LastUpdate 将@ThisSync 存储为附加表中的新“LastSync”。

在同步运行时修改的条目将具有比 max() 查询更高的 rowversion 值。下次调用同步器时,它们将被同步。

【讨论】:

这是一个想法。我会记住的,以防没有更好的结果。【参考方案2】:

如果您在 Serializable 事务中运行此操作,则其他读取/写入操作将无法影响这些表。

RepeateableRead 也可以完成这项工作...

【讨论】:

表格 - 是的。但是@@DBTS 的值呢?这不存储在任何表中! @Vilx 如果您在同步期间使用排他表锁,那么@@DBTS 是否被另一个表中的事件递增可能并不重要。 @Martin Smith - 但@@DBTS 对于整个数据库来说是全局的。为什么没关系?如果我为 LastSync 字段获得一个(旧)值,而为 LastUpdate 字段获得一个新值,我的同步将被破坏。 @Vilx 好吧,如果您更新表并且 @@DBTS 的值为 10,那么另一个表会更新几行,使 @@DBTS 的值等于 12,您最终会存储 12在LastSync 字段中,为什么这是个问题?下次您进行同步时,您仍在寻找 LastUpdate > LastSync 所在的行。 @Martin Smith - 问题是反过来。 LastUpdate 获得 之后 LastSync 的值。否则,我首先不需要在查询中使用 +1

以上是关于此 UPDATE 语句中是不是存在可能的竞争条件?的主要内容,如果未能解决你的问题,请参考以下文章

寻找竞争条件的方法

sql语句 根据条件update

oracle Update语句卡死,详细情况见下文

在MySQL中阻止UPDATE语句没有添加WHERE条件的发生

原子对象是不是受到保护免受竞争条件的影响?

T-SQL:在 UPDATE 语句中使用 CASE 根据条件更新某些列