执行交易时为 Update/Delete/Insert 锁定 table

Lock table for Update/Delete/Insert when executing a transaction

我正在尝试使用 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

在此期间,我想在我的 Main table 上停止任何 INSERT/UPDATE/DELETE 语句(直到 TRUNCATE 语句 运行)到 运行 因为如果我允许那么我们可能在 TRUNCATE 期间丢失了一些数据。

尝试使用事务:

BEGIN TRANSACTION

SELECT TOP 1 *
FROM table_name
WITH (TABLOCK, HOLDLOCK)

-- do your stuff

COMMIT 

TRUNCATE statement acquires SCH-M lock 这意味着它创建了一个 Schema Modification lock

Second type of the lock is schema modification lock – SCH-M. This lock type is acquired by sessions that are altering the metadata and live for duration of transaction. This lock can be described as super-exclusive lock and it’s incompatible with any other lock types including intent locks

Locking in Microsoft SQL Server (Part 13 – Schema locks)

在此期间,更新、select 和删除语句将等待 table 截断操作。因此,CRUD 操作将自动停止,直到 TRUNCATE 语句完成。

下面是一个示例脚本,它使用 SWITCHTRUNCATE 减少了完整恢复模型的日志记录。 SWITCH 是一个快速的元数据操作。 TRUNCATE 执行的 space 释放是由具有更大表 (64MB+) 的异步后台线程完成的,因此与 DELETE;

相比,它也很快并且大大减少了日志记录

事务用于确保全或-none 行为,并在事务期间持有架构修改锁以停止进程中的数据修改。

下面是示例处理前后使用的事务日志space,最初有 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