【问题标题】:HOLDLOCK XLOCK ROWLOCK not working some timesHOLDLOCK XLOCK ROWLOCK 有时不工作
【发布时间】:2015-04-05 07:17:37
【问题描述】:

我有以下场景:银行环境中,我不希望特定客户完成从他的账户中提款,除非之前的提款没有提交。

为了实现这一点,我创建了下表

CREATE TABLE [dbo].[Locks](
        [CustomerID] [int] NOT NULL,
    CONSTRAINT [PK_Locks] PRIMARY KEY CLUSTERED 
    (
        [CustomerID] ASC
    )

现在,每当提款开始时,我都会使用以下代码将客户插入此帮助表(如果他不存在),然后在交易期间锁定该行,以便不会发生其他提款在提交第一个事务之前

BEGIN TRAN
IF NOT EXISTS (SELECT * FROM Locks WHERE CustomerID=@customerId) --if customerid does not exist, insert the row
BEGIN
    INSERT INTO Locks (CustomerID) 
    VALUES (@customerId)
END
SELECT CustomerID FROM Locks WITH (HOLDLOCK XLOCK ROWLOCK) WHERE CustomerID=@customerId --lock on row
--(check if customer has enough balance, then perform withdraw from customer account)
COMMIT

上面的代码似乎可以正常工作,每天有数千次提款,但我确实每周一次左右,我确实遇到了一个锁不起作用并且发现客户余额为负的情况,因为两次提款操作发生在同一时间。

任何想法在什么情况下 HOLDLOCK XLOCK ROWLOCK 可能无法锁定事务?

【问题讨论】:

  • 请指定 dbms - 这不是 ANSI SQL。
  • @jarlh 语法显然是SQL Server
  • 你不能在初始 SELECT 上只 XLOCK 吗?实际上,我会 UPDLOCK 以允许在非 SI 隔离下并发读取。
  • @jarlh 抱歉,SQL Server

标签: sql sql-server transactions


【解决方案1】:
IF NOT EXISTS (SELECT * FROM Locks WHERE CustomerID=@customerId) --if customerid does not exist, insert the row
BEGIN
    INSERT INTO Locks (CustomerID) 
    VALUES (@customerId)
END
SELECT CustomerID FROM Locks WITH (HOLDLOCK XLOCK ROWLOCK) WHERE CustomerID=@customerId --lock on row
--(check if customer has enough balance, then perform withdraw from customer account)

这是一个竞态条件农场

  • 两个事务都可以运行 SELECT,得出没有锁的结论,然后都继续插入锁。有了 PK 约束,就会失败。这是最幸福的情况。
  • 一个事务可以来,运行 SELECT,断定有一行并继续第二个 SELECT。同时该行可以被删除。 SELECT WITH(锁定提示)将找不到任何行,但仍会继续得出它锁定某些东西的结论(它没有,没有要锁定的行)。这是一个更糟糕的情况,它可能导致(请滚!)负余额

这些是在 10 秒的代码检查中出现的。我敢肯定还有更多(我什至没有考虑回滚......)。使用行作为锁是一种反模式。 Use applocks instead.

【讨论】:

  • Remus,有什么理由让rowlocks明显不如applocks?这是众所周知的反模式吗?我已经使用了几次行锁,对结果很满意。
  • @usr 用 rows 作为 locks 的替代品,无论查询处理决定是 right 锁定,您都会受到影响战略。考虑锁升级。您在 希望 中暗示它要死(HOLDLOCK, ROWLOCK) 的事实清楚地表明您正试图强迫引擎的手。应用锁更简洁,因为它们是 lock,是您真正想要的。
  • @RemusRusanu 这个 SP 听起来正是我想要的。但是性能呢?它会在每秒 100 个事务的事务环境中快速执行吗?我有点害怕锁在那里
  • @ThemosPiperakis 应用锁的性能与数据锁完全相同,它们使用完全相同的内部结构。因此,考虑到您不再需要进行任何实际数据访问 (SELECT),总体上它应该会快得多。如果需要,应用锁也有每个会话语义,但是那些你必须小心释放它们,否则它们会在应用中泄漏。每事务应用锁的行为就像数据锁一样,它们在提交/回滚时释放。
  • 但是没有行锁怎么能可靠地使用应用锁呢?如果我有一个队列,多个工作人员要求处理行,我如何在不首先阻塞该行的情况下请求对行 ID 的应用锁?我事先不知道 id,我选择 next available 行。如果我在没有锁定提示的情况下这样做并尝试对其进行应用锁定,则会有多个客户端对同一行执行此操作,因此它们都必须包含一个(可怕的)循环来选择下一行,尝试申请应用程序锁定并在失败时重复。 with(rowlock, updlock, readpast) 似乎更干净更快?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-07
  • 2021-04-23
  • 2011-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多