如何锁定和解锁 SQL SERVER 表?

Posted

技术标签:

【中文标题】如何锁定和解锁 SQL SERVER 表?【英文标题】:How to lock and unlock a SQL SERVER table? 【发布时间】:2019-07-29 13:28:43 【问题描述】:

冒着过度解释我的问题的风险,我会因信息过多而犯错。

我正在创建一个将数据插入两个表的批量上传过程。两张表大致如下。 TableA 是一个自引用表,允许 N 级引用。

Parts (self-referencing table)
--------
PartId (PK Int Non-Auto-Incrementing)
DescriptionId (Fk)
ParentPartId
HierarchyNode (HierarchyId)
SourcePartId (VARCHAR(500) a unique Part Id from the source)
(other columns)

Description
--------
DescriptionId (PK Int Non-Auto-Incrementing)
Language (PK either 'EN' or 'JA')
DescriptionText (varchar(max))

(我还应该注意,还有其他表将引用我们的 PartID,我暂时不讨论这些表。)

Description 中,DescriptionLanguage 的组合将是唯一的,但实际的 `DescriptionID 将始终至少有两个实例。

现在,对于批量上传过程,我创建了两个暂存表,它们看起来很像 PartsDescription,但没有任何 PK、索引等。它们是 Parts_StagingDescription_Staging

Parts_Staging 中有一个额外的列,其中包含一个层次结构节点字符串,即这种格式的层次结构节点:/1/2/3/ 等。然后当数据从 _Staging 表复制到实际表时,我使用CAST(Source.Column AS hierarchyid)

由于两个表共享 ID 的复杂性,Parts 中的自引用 ID 和层次结构 ID,以及要插入的行数(可能在 100,000 行),我决定 100%首先编译 C# 模型中的所有数据,包括 PK ID。所以这个过程在 C# 中是这样的:

    在两个表中查询 MAX ID 使用这些 Max ID,编译两个表的所有数据的完整模型(包括 hierarchyid /1/2/3/) 对两个 _Staging 表进行批量插入 触发将非重复数据从两个 _Staging 表复制到实际表中的 SP。 (这是CAST(Source.Column AS hierarchyid) 发生的地方)。

我们正在导入大量零件书籍,单个零件可能会在多本书中复制。我们需要删除重复项。在第 4 步中,通过检查Parts 表中的SourcePartIdDescription 表中DescriptionText 中的Description 来清除重复项。

整个过程运行良好!最重要的是,它真的很快。但是,如果您仔细阅读本文(如果您仔细阅读,我会感谢您),那么您已经注意到一个明显的问题。

如果同时发生多个进程(这绝对会发生!),则存在非常现实的风险,即 ID 混淆并且数据真正损坏。 Process1 可以执行GET MAX ID 查询,在它设法完成之前,Process2 还可以执行GET MAX ID 查询,因为 Process1 还没有真正写入表,它会得到相同的 ID。

我最初的想法是使用 SEQUENCE 对象。起初,这个计划似乎很出色。但它在测试中崩溃了,因为完全有可能相同的数据将被处理多次,并在从 _Staging 表复制到最终表时最终被忽略。在这种情况下,序列号将已被声明和使用,从而导致 ID 中的巨大差距。并不是说这是一个致命的缺陷,而是我们宁愿避免的问题。

所以...问这个实际问题需要大量背景信息。我想做的是这样的:

    锁定有问题的两个表 如上所述的步骤 1-4 解锁两个表。

锁需要是 READ 锁(我认为是排他锁?),这样如果另一个进程尝试执行 GET MAX ID 查询,它就必须等待。

我的问题是:1)这是最好的方法吗? 2)如何在表上放置独占锁?

谢谢!

【问题讨论】:

该过程根本无法正常工作。将数据处理移动到客户端增加延迟,它不会减少延迟。使用 MAX(ID) 计算 ID 会引入重复 ID 的风险,不仅在有多个已处理的情况下,而且在某些行被删除的情况下也是如此。在这种情况下,不会有任何错误,但一些新值将接收 已删除 条目使用的 ID,并与错误记录相关 任何时候你认为你需要锁定表,你应该考虑整个过程是否也被破坏了。锁适用于短期操作。它们不是签入/签出机制。 好的,谢谢 Panagiotis。我听到你了。请问您将如何完成整个过程? 在这种特殊情况下,您可以将主键添加到临时表中,这些表通过 DEFAULT 约束从 SEQUENCE 对象中获取它们的值。这样就不会有任何重复的风险,也不需要锁定。批量插入将分配唯一的递增值,就像它们是通过 IDENTITY 函数添加的一样。不同之处在于 IDENTITY 一次只能处理一个表,而 SEQUENCE 可以被多个表使用,例如,如果您插入多种类型的相似记录,这些记录一旦到达最终目的地就需要一个公共键 简短版:这是一个在同一个事务中插入多个父母和多个孩子的经典案例。长版:阅读my answer to a similar case,画出您的相似之处,更改建议的解决方案以适合您的确切细节,然后为自己泡一杯来之不易的咖啡。 【参考方案1】:

我不确定最好的方法是什么,但就在表上放置“排他”锁而言,只需在查询中使用 with (TABLOCKX) 即可在表上放置一个。

如果你想了解它;

https://msdn.microsoft.com/en-GB/library/ms187373.aspx

【讨论】:

以上是关于如何锁定和解锁 SQL SERVER 表?的主要内容,如果未能解决你的问题,请参考以下文章

(Discord.py) 如何锁定和解锁语音通道

10分钟后锁定和解锁屏幕时发生的崩溃如何解决?

如何从服务中可靠地捕获 Windows 登录、注销、锁定和解锁事件?

sql语句对数据库表进行加锁和解锁

如何在 Windows 中为可以锁定(只读)和解锁驱动器的 diskpart cmd 创建批处理文件

MySQL锁表和解锁操作