SQL Server - 防止丢失更新和死锁

本文关键字:更新 死锁 Server SQL | 更新日期: 2023-09-27 18:32:39

我正在做一个项目,其中包含 2 个用 C# (.NET Framework 4( 开发的应用程序 -

  1. WCF 服务(向客户公开(
  2. ASP.NET 网络表单应用程序(总部使用(。

这两个应用程序都可以从 SQL Server 2005 数据库中的公共"帐户"表中选择和更新行。账户表中的每一行都包含客户余额。

这两个应用程序中请求的业务逻辑都涉及从"accounts"表中选择一行,根据余额执行一些处理,然后更新数据库中的余额。选择和更新余额之间的过程不能参与交易。

我意识到在选择行和更新

它之间是可能的,该行可以通过来自相同或不同应用程序的另一个请求来选择和更新。

我在下面的文章中发现了这个问题。我指的是"丢失更新"的第二种情况。http://www.codeproject.com/Articles/342248/Locks-and-Duration-of-Transactions-in-MS-SQL-Serve

第二种情况是当一个事务(事务 A(读取 将值记录并检索到局部变量中,并且相同 记录将由另一笔交易(交易 B(更新。和 稍后事务 A 将使用 局部变量。在这种情况下,事务 B 完成的更新可以 被视为"丢失的更新"。

我正在寻找一种方法来防止上述情况,并防止在同一行收到多个并发请求时余额变为负数。一次只能由一个请求(来自任一应用程序(选择和更新一行,以确保平衡一致。

我正在考虑在一个请求选择一行后立即阻止对行的访问。根据我的研究,以下是我的观察结果。

  1. 隔离级别

使用"可重复读取"隔离级别,2 个事务可以选择公共行。

我测试了打开 2 个 SSMS 窗口。在这两个窗口中,我启动了一个具有可重复读取隔离级别的事务,然后在公共行上进行选择。我能够在每个事务中选择该行。

接下来,我尝试从每个事务更新同一行。语句持续运行了几秒钟。然后,来自第一个事务的更新成功,而来自第二个事务的更新失败,并显示以下消息。

错误 1205:事务(进程 ID(在锁定资源上死锁 与另一个进程,并被选为死锁受害者。重新运行 事务。

因此,如果我使用具有可重复读取的事务,则 2 个并发事务应该不可能更新同一行。SQL 服务器自动选择回滚 1 个事务。这是对的吗?

但我也想通过仅允许单个事务选择特定行来避免死锁错误。

  1. 行锁

我在 Stackoverflow 上找到了以下答案,其中提到了使用ROWLOCK提示来防止死锁。(请参阅接受答案的评论(。

避免"丢失更新"的最低事务隔离级别

我开始了一个事务,并使用了一个带有 ROWLOCKUPDLOCK 的选择语句。然后在新的 SSMS 窗口中,我启动了另一个事务,并尝试使用相同的选择查询(具有相同的锁(。这次我无法选择该行。该语句在新的 SSMS 窗口中继续运行。

因此,在事务中使用 Rowlock 似乎阻止了使用相同锁定提示的选择语句的行。

如果有人能回答以下问题,我将不胜感激。

  1. 我关于隔离级别和行锁定的观察是否正确?

  2. 对于我描述的方案,我是否应该使用ROWLOCKUPDLOCK提示来阻止对行的访问?如果不是,正确的方法是什么?

我计划将我的选择和更新代码放在事务中。事务中的第一个选择查询将使用ROWLOCKUPDLOCK提示。这将防止记录被另一个使用具有相同锁的选择来检索同一行的事务选择。

SQL Server - 防止丢失更新和死锁

我建议快照的SQL隔离级别。 与预言机锁管理非常相似。

见 http://www.databasejournal.com/features/mssql/snapshot-isolation-level-in-sql-server-what-why-and-how-part-1.html

如果你的代码不是太复杂,你可能可以在不做任何更改的情况下实现它。请记住,某些可见性可能会受到影响(即脏读取可能不会提供脏数据(

我发现这个毯子系统比到处使用查询提示更容易、更精确。

使用以下方法配置数据库:

SET ALLOW_SNAPSHOT_ISOLATION ON

然后使用它作为交易语句的前缀:

SET TRANSACTION ISOLATION LEVEL SNAPSHOT