执行事务时为更新/删除/插入锁定表
Posted
技术标签:
【中文标题】执行事务时为更新/删除/插入锁定表【英文标题】:Lock table for Update/Delete/Insert when executing a transaction 【发布时间】:2020-03-07 18:53:46 【问题描述】:我正在尝试使用 How to delete large data of table in SQL without log? 将我的 DELETE 语句转换为 TRUNCATE
这是我正在尝试的,
-- Move recent records from Main table to a Temp table
-- TRUNCATE the Main table
-- Return back data from Temp table to Main table
在此期间,我想停止任何 INSERT/UPDATE/DELETE 语句(直到运行 TRUNCATE 语句)在我的主表上运行,因为如果我允许,那么我们可能会在 TRUNCATE 期间丢失一些数据。
【问题讨论】:
TRUNCATE
将在操作期间获取排他表锁,并阻止并发活动。
@DanGuzman 你的意思是我不想担心这个吗?
TRUNCATE 将删除表中的所有数据。您能否详细说明您担心哪些数据丢失?如果表中有您不想丢失的数据,请不要截断或删除。
@DanGuzman 想想这个。在将数据移动到 Temp 表之后,在 TRUNCATE 开始之前,如果有任何数据 INSERTED/UPDATED/DELETED 将丢失。
我明白你现在的意思了。如果您使用 INSERT...SELECT... WITH(TABLOCKX) 移动数据,则 TRUNCATE 并在同一个事务中全部移回。那么就没有损失的风险。
【参考方案1】:
TRUNCATE
语句获取SCH-M
锁意味着它创建了一个模式修改锁
第二种锁是模式修改锁——SCH-M。这把锁 类型由正在更改元数据和实时的会话获取 交易期间。这个锁可以描述为 超级独占锁,它与任何其他锁类型不兼容 包括意图锁
Locking in Microsoft SQL Server (Part 13 – Schema locks)
在此期间,更新、选择和删除语句将等待表截断操作。因此,CRUD 操作将自动停止,直到 TRUNCATE 语句完成。
【讨论】:
看我上面的评论,after moving data to the Temp table and before TRUNCATE starts if there is any data INSERTED/UPDATED/DELETED will be lost
【参考方案2】:
以下是使用SWITCH
和TRUNCATE
减少记录完整恢复模型的示例脚本。 SWITCH
是一个快速的元数据操作。 TRUNCATE
执行的空间释放是由具有更大表 (64MB+) 的异步后台线程完成的,因此与 DELETE
相比,它也很快并且大大减少了日志记录;
事务用于确保全有或全无行为,并在事务期间持有架构修改锁以在此过程中停止数据修改。
以下是示例处理前后使用的事务日志空间,初始为 1M 行,保留 50K:
+--------+---------------+--------------------+
| | Log Size (MB) | Log Space Used (%) |
+--------+---------------+--------------------+
| Before | 1671.992 | 27.50415 |
| After | 1671.992 | 30.65533 |
+--------+---------------+--------------------+
测试设置:
--example main table
CREATE TABLE dbo.Main(
MainID int NOT NULL CONSTRAINT PK_Main PRIMARY KEY
, MainData char(1000) NOT NULL
);
--staging table with same schema and indexes as main table
CREATE TABLE dbo.MainStaging(
MainID int NOT NULL CONSTRAINT PK_MainStaging PRIMARY KEY
, MainData char(1000) NOT NULL
);
--load 1M rows into main table for testing
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t1k AS (SELECT 0 AS n FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c)
,t1g AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t1k AS a CROSS JOIN t1k AS b CROSS JOIN t1k AS c)
INSERT INTO dbo.Main WITH(TABLOCKX) (MainID, MainData)
SELECT num, CAST(num AS char(1000))
FROM t1g
WHERE num <= 1000000;
GO
示例脚本:
SET XACT_ABORT ON; --ensures transaction is rolled back immediately even if script is cancelled
BEGIN TRY
BEGIN TRAN;
--truncate in same transaction so entire script can be safely rerun
TRUNCATE TABLE dbo.MainStaging;
--ALTER TABLE will block other activity until committed due to schema modification lock
--main table will be empty after switch
ALTER TABLE dbo.Main SWITCH TO dbo.MainStaging;
--keep 5% of rows
INSERT INTO dbo.Main WITH(TABLOCKX) (MainID, MainData)
SELECT MainID, MainData
FROM dbo.MainStaging
WHERE MainID > 950000;
COMMIT;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK;
THROW;
END CATCH;
GO
【讨论】:
【参考方案3】:尝试使用事务:
BEGIN TRANSACTION
SELECT TOP 1 *
FROM table_name
WITH (TABLOCK, HOLDLOCK)
-- do your stuff
COMMIT
【讨论】:
TRUNCATE
默认在自动提交事务中运行。除非同一事务中有其他语句,否则显式事务不提供价值。以上是关于执行事务时为更新/删除/插入锁定表的主要内容,如果未能解决你的问题,请参考以下文章