【问题标题】:Problems deleting then inserting into SQL Server with READ_COMMITTED_SNAPSHOT ON在 READ_COMMITTED_SNAPSHOT ON 的情况下删除然后插入 SQL Server 时出现问题
【发布时间】:2019-06-12 15:37:23
【问题描述】:

我们使用的是 SQL Server 2017。我们有 READ_COMMITTED_SNAPSHOT ON。我们有一个执行删除然后插入的查询,如下所示:

begin transaction
SELECT DISTINCT STAGING.MyTable.CategoryId
INTO #categories_to_delete
FROM STAGING.MyTable;
commit;

begin transaction;
SELECT DISTINCT STAGING.MyTable.ItemId
INTO #items_to_delete
FROM STAGING.MyTable;
commit;

begin transaction;
DELETE FROM PROD.MyTableProd
FROM PROD.MyTableProd
INNER JOIN #categories_to_delete
ON #categories_to_delete.CategoryId = PROD.MyTableProd.CategoryId;
commit;

begin transaction;
DELETE FROM PROD.MyTableProd
FROM PROD.MyTableProd
INNER JOIN #items_to_delete
ON #items_to_delete.ItemId= PROD.MyTableProd.ItemId;
commit;

begin transaction;
INSERT INTO PROD.MyTableProd
SELECT
    CategoryId
    , ItemId
    , <other_columns>
FROM
STAGING.MyTable;
commit;

PROD.MyTableProdSTAGING.MyTable 的主键都是ItemId。 prod 表有大约 1200 万行,而 staging 表在上一步中插入数据时会被截断。

运行时,我们会经常在insert 上遇到主键违规。我的怀疑是因为READ_COMMITTED_SNAPSHOT ONinsertdelete 锁没有相互阻塞,所以insert 发生在delete 还没有完成时?但是,我认为将所有内容包装在事务中可以防止出现此类问题?有什么建议可以防止这些主键问题而无需关闭READ_COMMITTED_SNAPSHOT?有没有办法只为这张桌子关闭它?

【问题讨论】:

  • 您使用 DISTINCT 生成要删除的项目列表这一事实表明您的表具有重复的 ItemID 值。也许只是很少。要验证这一点,请在脚本开头检查临时表中的重复 ItemId 值并引发错误。
  • 它不考虑两个表具有相同的主键列。

标签: sql sql-server database tsql


【解决方案1】:

这是一个数据库设置,你不能按表来做,但是....

如果您仅将 RCSI 设置为 ON,它只会将已提交的读取操作更改为快照读取。更新、插入和删除使用正常的事务隔离来处理。

设置 READ_COMMITTED_SNAPSHOT ON 选项允许访问 默认 READ COMMITTED 隔离级别下的版本化行。

Snapshot Isolation in SQL Server

在任何情况下,您都不会因为快照隔离而看到 PK 违规,但如果在同一行上有两个并发更新/插入/删除,您会看到更新冲突。

【讨论】:

  • 在进行更多测试时,删除发生后,当我进行选择时,我看到表中仍然存在应该删除的行。在所有行都消失之前,我必须运行几次删除。会不会是删除被并行化了,所以会发生一些奇怪的并发事情?
  • 如果删除尚未提交,您仍然可以在未提交的情况下看到它们。 OTOH,如果他们在另一个会话中并且您在单独的会话中使用快照隔离阅读,这就是 RCSI 应该如何工作的。如果属实,您的建议将是数据库引擎中的一个极其严重的错误。
【解决方案2】:

当它运行时,我们会经常在插入时遇到主键违规。我的怀疑 是不是因为READ_COMMITTED_SNAPSHOT ON,插入和删除锁没有>相互阻塞,所以插入发生在删除还没有完成的时候?

您为每个命令使用不同的事务,如果您希望在插入后将它们放入同一事务中进行删除,例如

开始交易 SELECT DISTINCT STAGING.MyTable.CategoryId INTO #categories_to_delete FROM STAGING.MyTable;

从 PROD.MyTableProd 中删除 来自 PROD.MyTableProd 内连接#categories_to_delete ON #categories_to_delete.CategoryId = PROD.MyTableProd.CategoryId; 提交;

开始交易; SELECT DISTINCT STAGING.MyTable.ItemId INTO #items_to_delete FROM STAGING.MyTable;

从 PROD.MyTableProd 中删除 来自 PROD.MyTableProd 内连接#items_to_delete ON #items_to_delete.ItemId= PROD.MyTableProd.ItemId; 提交;

使用 READ_COMMITTED_SNAPSHOT 时,事务不会相互阻塞,因为所有事务在初始事务开始时都会读取表的快照。

【讨论】:

    猜你喜欢
    • 2019-04-30
    • 1970-01-01
    • 1970-01-01
    • 2014-09-11
    • 1970-01-01
    • 2014-05-30
    • 1970-01-01
    • 2011-07-19
    • 2016-02-20
    相关资源
    最近更新 更多