【问题标题】:How to lock a set of records without fetching them to the client如何锁定一组记录而不将它们提取给客户端
【发布时间】:2021-09-21 13:58:51
【问题描述】:

假设我们有一个类似这样的表:

CREATE TABLE employee (
  id INT PRIMARY KEY,
  department_id INT NOT NULL,
  first_name NVARCHAR(200) NOT NULL
)

可能有多个并发事务在此表的记录上操作。如果它们操作相同的department_id,它们应该是可序列化/同步的(即等待彼此提交或回滚):

-- 1-st statement
BEGIN TRANSACTION
-- 2-nd
SELECT * FROM employee WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE department_id = :department_id
-- 3-nd
... main logic ... 
-- 4-th
COMMIT TRANSACTION

我想要实现的是避免在第二个语句中将所有这些记录获取到客户端(在我的情况下是 Java 应用程序),而是将它们锁定在数据库中以进行此事务,例如通过调用一些内置的存储过程(在我的例子中是动态 SQL):

-- 1-st statement
BEGIN TRANSACTION
-- 2-nd, something like that
CALL sp_lock 'employee' 'ROWLOCK, XLOCK, HOLDLOCK' 'department_id = :department_id'
-- 3-nd
... main logic ... 
-- 4-th
COMMIT TRANSACTION

在 MS SQL Server 中是否可行(在我的情况下也是 Java/JDBC)?

【问题讨论】:

  • 您正在寻找sp_getapplock
  • 听起来像是个主意……
  • 您是否还需要锁定以相同department_id 插入的任何 记录?我很想知道为什么你想要这个,与标准锁定相比,它提供了什么?

标签: java sql sql-server jdbc


【解决方案1】:

您可以在SELECT 语句中使用UPDLOCK 提示,这将导致锁定一直保持到事务结束

BEGIN TRANSACTION;

DECLARE @dummy int = (
  SELECT COUNT(*)
  FROM employee WITH (UPDLOCK)
  WHERE department_id = @department_id);

... main logic ... 

COMMIT TRANSACTION;

HOLDLOCK 提示还将针对使用相同 department_id 插入的任何 记录取消范围锁定,代价是可能锁定超出必要的范围(它锁定到下一个现有密钥)。

如果你有其他索引那么你需要强制一个聚集索引来使它正常工作,你可以添加提示INDEX (0)INDEX (1)

【讨论】:

  • 根据您的示例,聚合函数锁定与其where 子句匹配的所有记录,例如SELECT count(*) FROM employee WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE department_id = 123SELECT * FROM employee WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE department_id = 123 做同样的事情,但只返回 1 条记录和 1 列给客户端。我说的对吗?
  • 是的,但这取决于您是否有其他索引。请注意,我不会向客户端返回任何内容,因为我将它存储在一个变量中,但是 SELECT 中的 XLOCK 用词不当,不会使用 X 锁,无论哪种方式你 需要 UPDLOCK 否则不起作用
猜你喜欢
  • 1970-01-01
  • 2016-04-27
  • 2021-04-27
  • 2012-10-16
  • 2018-04-24
  • 1970-01-01
  • 2018-09-14
  • 2012-01-17
  • 2023-01-31
相关资源
最近更新 更多