AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 341667
Accepted
ToC
ToC
Asked: 2024-08-15 05:44:04 +0800 CST2024-08-15 05:44:04 +0800 CST 2024-08-15 05:44:04 +0800 CST

两个 MERGE 查询插入同一张表时发生阻塞

  • 772

设想

我有一个按列分区的大表INT。当我MERGE在该表的两个不同分区上运行两个不同的语句时,它们似乎互相阻塞。

重现该场景的示例代码:

1. 准备工作。创建表和一些虚拟数据

SET NOCOUNT ON
GO
--
--  Create parition function and partition scheme
--
    DROP FUNCTION IF EXISTS PF_Site_ID 
    GO
    CREATE PARTITION FUNCTION PF_Site_ID (INT)  
        AS RANGE RIGHT FOR VALUES   (
                                        0,
                                        1, 2, 3, 4, 5, 6, 7, 8, 9, 10
                                    )
    GO  
    DROP PARTITION SCHEME PS_Site_ID
    GO
    CREATE PARTITION SCHEME PS_Site_ID
        AS PARTITION PF_Site_ID
        ALL TO ('PRIMARY')
    GO

--
-- Large table partitioned on Site_ID. Two STG tables. And some dummy data
--
    DROP TABLE IF EXISTS dbo.PartitionedLargeTable
    GO
    CREATE TABLE dbo.PartitionedLargeTable
    (
          ID       INT          NOT NULL IDENTITY(1,1)
        , Site_ID  INT          NOT NULL
        , Name     VARCHAR(50)
    ) ON PS_Site_ID (Site_ID)
    GO
    ALTER TABLE dbo.PartitionedLargeTable SET (LOCK_ESCALATION = AUTO)
    GO

--
--  STG tables
--
    DROP TABLE IF EXISTS dbo.STG_Test1
    GO
    CREATE TABLE dbo.STG_Test1
    (
          ID       INT          NOT NULL IDENTITY(1,1)
        , Site_ID  INT          NOT NULL
        , Name     VARCHAR(50)
    ) ON [PRIMARY]
    GO
    DROP TABLE IF EXISTS dbo.STG_Test2
    GO
    CREATE TABLE dbo.STG_Test2
    (
          ID       INT          NOT NULL IDENTITY(1,1)
        , Site_ID  INT          NOT NULL
        , Name     VARCHAR(50)
    ) ON [PRIMARY]
    GO

--
--  Dummy data
--
    INSERT INTO dbo.PartitionedLargeTable (Site_ID, Name) SELECT 1, NEWID()
    INSERT INTO dbo.PartitionedLargeTable (Site_ID, Name) SELECT 2, NEWID()
    GO 10000

    INSERT INTO dbo.PartitionedLargeTable (Site_ID, Name)
    SELECT Site_ID, Name FROM dbo.PartitionedLargeTable
    GO 5

    INSERT INTO dbo.STG_Test1(Site_ID, Name) SELECT 1, NEWID()
    GO 10000
    INSERT INTO dbo.STG_Test2(Site_ID, Name) SELECT 2, NEWID()
    GO 10000

    INSERT INTO dbo.STG_Test1 (Site_ID, Name)
    SELECT Site_ID, Name FROM dbo.STG_Test1
    GO 7

    INSERT INTO dbo.STG_Test2 (Site_ID, Name)
    SELECT Site_ID, Name FROM dbo.STG_Test2
    GO 7

2. 合并 1

在一个 SSMS 窗口中,运行此MERGE语句:

MERGE dbo.PartitionedLargeTable AS TGT

USING (SELECT ID, Site_ID, Name FROM dbo.STG_Test1) AS SRC
    ON  SRC.Site_ID = TGT.Site_ID
    AND SRC.ID      = TGT.ID

WHEN MATCHED THEN
    UPDATE 
        SET TGT.Name = SRC.Name

WHEN NOT MATCHED THEN
    INSERT (Site_ID, Name)
    VALUES (SRC.Site_ID, SRC.Name);

3. 合并 2

在第二个 SSMS 窗口中,运行此MERGE语句:

MERGE dbo.PartitionedLargeTable AS TGT

USING (SELECT ID, Site_ID, Name FROM dbo.STG_Test2) AS SRC
    ON  SRC.Site_ID = TGT.Site_ID
    AND SRC.ID      = TGT.ID

WHEN MATCHED THEN
    UPDATE 
        SET TGT.Name = SRC.Name

WHEN NOT MATCHED THEN
    INSERT (Site_ID, Name)
    VALUES (SRC.Site_ID, SRC.Name);

这两个语句在不同的Site_IDMERGE上运行(因此是两个不同的分区)。

分区表的一个性能优势是我们可以独立地操作分区(在合理范围内)。因此,在一个分区上执行类似INSERT或 的UPDATE操作不会阻止在其他分区上执行类似操作。

与未分区表相比,如果我们执行两个大型INSERT操作(或两个大型UPDATE操作),一旦操作的行数超过一定数量(例如 3k 或 5k 行),一个操作就会阻塞另一个操作,然后锁PAGE将升级为TABLOCK。因此INSERT阻塞INSERT(或UPDATE阻塞UPDATE)

为了避免将锁升级到TABLOCK,此表使用 LOCK_ESCALATION = AUTO 进行分区,这将锁限制在 HOBT 级别(而不是表)。但是使用MERGE,阻塞仍然会发生。

关于如何防止这种阻塞,您有什么想法吗?我们MERGE在这个大表的 10 个不同分区上运行了 10 个并行语句(它们相互阻塞)。

下图显示了阻塞的本质。当表被分区时,锁升级应该只升级到分区(而不是整个表)。当这些MERGE语句运行时,我看到每个语句MERGE正在查询(锁定)的 HOBT ID。在某些情况下,HOBT ID 与此表的分区 ID 不匹配。

在此处输入图片描述

我实际使用的表COLUMNSTORE CLUSTERED在分区方案上有一个索引。

sql-server
  • 2 2 个回答
  • 518 Views

2 个回答

  • Voted
  1. Best Answer
    Martin Smith
    2024-08-15T16:16:00+08:002024-08-15T16:16:00+08:00

    我看到的带有示例数据的执行计划没有进行任何分区消除,只是扫描了整个表。

    这意味着它最终会读取属于其他站点 ID 的页面,并在不需要读取的页面的页面锁上被阻止(您的屏幕截图不一定显示主要阻止程序正在持有对象锁)

    在此处输入图片描述

    您可以使用

    --Implicitly asserts that the staging table does not contain more than 
    --one distinct value as error will be thrown. 
    DECLARE @Site_ID INT = (SELECT DISTINCT Site_ID FROM dbo.STG_Test1);
    
    WITH TGT AS (SELECT * FROM dbo.PartitionedLargeTable WHERE Site_ID = @Site_ID)
    MERGE TGT
    USING dbo.STG_Test1 AS SRC
        ON  SRC.ID      = TGT.ID
    WHEN MATCHED AND TGT.Name <> SRC.Name OR (TGT.Name IS NULL AND SRC.Name IS NOT NULL) OR (SRC.Name IS NULL AND TGT.Name IS NOT NULL) THEN
        UPDATE 
            SET TGT.Name = SRC.Name
    WHEN NOT MATCHED THEN
        INSERT (Site_ID, Name)
        VALUES (@Site_ID, SRC.Name);
    

    在此处输入图片描述

    希望实际上您还拥有有用的分区对齐索引,这样它就不必扫描整个分区并将其哈希连接到暂存表。

    • 7
  2. Rodger Kong
    2024-08-15T17:53:59+08:002024-08-15T17:53:59+08:00

    您的示例中的“PartitionedLargeTable”没有索引,执行合并时会导致表扫描。无索引合并 当您的数据库的事务隔离级别为 READ COMMITTED 时,合并将长时间锁定目标表中存在的大行,这可能会阻止 UPDATE 的写入操作。我尝试在“PartitionedLargeTable”上构建一个 NonClusteredIndex,其中包括列“ID”和“Site_ID”,执行合并时,它使用索引扫描。与索引合并 通过这种方式,读取操作花费的时间更少,并且读取 I/O 移动到与表堆页分开的索引页。这将有助于减少锁冲突。

    • 3

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve