是否有更有用的方法来检测 SQL Profiler 中的锁获取/释放统计信息?

RPM*_*984 9 sql-server-2008 sql-server

在我们的实时服务器上发生了一些死锁,我知道,这很糟糕。

无论如何,我试图弄清楚如何查看导致锁定(或持有时间过长)的 SQL 代码。

我已经添加了所有的Locks事件(获得、死锁、升级、释放、超时等),但我所看到的只是对获得/释放事件的轰炸,没有太多附加信息。

我很确定我知道导致死锁的“场景”,因为我们在插入/更新/删除后在表上有一个触发器,这做了很多额外的工作,有时高达 8 秒。

但是,在大部分额外工作中,数据从 TableA 中提取并插入到表变量中,大量工作在该表变量的内存中进行(大约 6 秒过去),然后最终插入到 TableB 中。

问题: 即使我只是将某些行选择到表变量中,这实际上会导致整个表的表锁定吗?

我已经添加了“触发跟踪”(插入表 x 值('嗨,我在这里')等),我基本上知道这个过程花费的时间最长(因此必须可能导致锁定?)

但我仍然不确定为什么会发生僵局。

问题: 我可以看到发生了 2 个锁升级,这是否意味着行锁已升级为表锁?

问题: 有人能给我一些关于如何进一步追踪这个僵局的提示吗?

编辑:

是死锁图

编辑2:

这是 [UpdatePostsCleanedUriUniqueUri] 的代码,这可能会导致表锁定?

ALTER PROC [dbo].[UpdatePostsCleanedUriUniqueUri]
(
    @PostIds IdentityType READONLY
)
AS
    SET NOCOUNT ON


    -- *************************************************************************
    -- ******************* Create the Cleaned Uri's,  first ********************
    -- *************************************************************************

    ---- "Remove" any existing cleaned uri's, per location
    UPDATE a
    SET a.CleanedUri = NEWID(),
        a.UniqueUri = NEWID()
    FROM [dbo].[Posts] a
        INNER JOIN @PostIds b ON a.PostId = b.Id


    -- ** Now add the cleaned uri.
    UPDATE a
    SET a.CleanedUri = [dbo].[ToFixedLengthToString](
        [dbo].[IsNullOrEmpty](LOWER([dbo].[ToAlphaNumericText]([Subject], '-', 1)), 'unknown'), 60, '')
    FROM [dbo].[Posts] a
        INNER JOIN @PostIds b ON a.PostId = b.Id



    -- *************************************************************************
    -- ******** Now create the Unique Uri from the cleaned one, above **********
    -- *************************************************************************

    -- Now Re-Add these unique uri's.
    ;WITH CTE AS (
        SELECT DISTINCT CleanedUri
        FROM [dbo].[Posts] a
            INNER JOIN @PostIds b ON a.PostId = b.Id
    )

    UPDATE a
    SET a.UniqueUri = Result.UniqueUri
    FROM [dbo].[Posts] a
        INNER JOIN (
            SELECT SubQuery.PostId, 
                CASE SubQuery.RowNumber
                WHEN 1 THEN SubQuery.CleanedUri
                ELSE SubQuery.CleanedUri + '-' + CAST(SubQuery.RowNumber - 1 AS NVARCHAR(20)) END AS UniqueUri 
            FROM (
                SELECT PostId, a.CleanedUri,
                    ROW_NUMBER() OVER (PARTITION BY a.CleanedUri ORDER BY a.CleanedUri) AS RowNumber
                FROM [dbo].[Posts] a
                    INNER JOIN CTE b ON a.CleanedUri = b.CleanedUri
            ) SubQuery
        ) Result ON a.PostId = Result.PostId
    ;
Run Code Online (Sandbox Code Playgroud)

因为它有一个 INNER JOIN,我会认为它只会锁定匹配的行?或者我应该使用WITH (ROWLOCK)

编辑 3:

    USE [XWing]
    GO

    /****** Object:  Table [dbo].[Posts]    Script Date: 02/17/2012 13:29:05 ******/
    SET ANSI_NULLS ON
    GO

    SET QUOTED_IDENTIFIER ON
    GO

    SET ANSI_PADDING ON
    GO

    CREATE TABLE [dbo].[Posts](
        [PostId] [int] IDENTITY(1,1) NOT NULL,
        [Subject] [nvarchar](300) NULL,
        [CleanedUri] [nvarchar](70) NOT NULL,
        [UniqueUri] [nvarchar](70) NOT NULL,
        [Content] [nvarchar](max) NULL,
        [Source] [nvarchar](50) NULL,
        [LocationTypeId] [tinyint] NOT NULL,
        [CreatedOn] [smalldatetime] NOT NULL,
        [ModifiedOn] [smalldatetime] NOT NULL,
        [IsEditorsChoice] [bit] NOT NULL,
        [IsVisible] [bit] NOT NULL,
        [UserId] [int] NOT NULL,
        [LatLongPoint] [geography] NULL,
        [UserLastIpAddress] [varchar](15) NULL,
        [OldPostId] [int] NULL,
        [OldUniqueUri] [nvarchar](250) NULL,
        [ThemeId] [int] NOT NULL,
     CONSTRAINT [PK_Posts] PRIMARY KEY CLUSTERED 
    (
        [PostId] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
    ) ON [PRIMARY]

    GO

    SET ANSI_PADDING OFF
    GO

    ALTER TABLE [dbo].[Posts]  WITH CHECK ADD  CONSTRAINT [FK_Posts_Themes] FOREIGN KEY([ThemeId])
    REFERENCES [dbo].[Themes] ([ThemeId])
    GO

    ALTER TABLE [dbo].[Posts] CHECK CONSTRAINT [FK_Posts_Themes]
    GO

    ALTER TABLE [dbo].[Posts]  WITH CHECK ADD  CONSTRAINT [FK_Posts_Users] FOREIGN KEY([UserId])
    REFERENCES [dbo].[Users] ([UserId])
    GO

    ALTER TABLE [dbo].[Posts] CHECK CONSTRAINT [FK_Posts_Users]
    GO

    ALTER TABLE [dbo].[Posts] ADD  CONSTRAINT [DF_Posts_IsEditorsChoice]  DEFAULT ((0)) FOR [IsEditorsChoice]
    GO

    ALTER TABLE [dbo].[Posts] ADD  CONSTRAINT [DF_Posts_ThemeId]  DEFAULT ((1)) FOR [ThemeId]
    GO

USE [XWing]
GO

/****** Object:  Index [IX_Posts_IsEditorsChoice_Include_PostId_LocationTypeId]    Script Date: 02/17/2012 13:31:59 ******/
CREATE NONCLUSTERED INDEX [IX_Posts_IsEditorsChoice_Include_PostId_LocationTypeId] ON [dbo].[Posts] 
(
    [IsEditorsChoice] ASC
)
INCLUDE ( [PostId],
[LocationTypeId]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
GO

USE [XWing]
GO

/****** Object:  Index [IX_Posts_IsVisible]    Script Date: 02/17/2012 13:32:02 ******/
CREATE NONCLUSTERED INDEX [IX_Posts_IsVisible] ON [dbo].[Posts] 
(
    [IsVisible] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
GO

USE [XWing]
GO

/****** Object:  Index [IX_Posts_LocationTypeId]    Script Date: 02/17/2012 13:32:07 ******/
CREATE NONCLUSTERED INDEX [IX_Posts_LocationTypeId] ON [dbo].[Posts] 
(
    [LocationTypeId] ASC
)
INCLUDE ( [PostId]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
GO

USE [XWing]
GO

/****** Object:  Index [IX_Posts_OldPostId]    Script Date: 02/17/2012 13:32:12 ******/
CREATE NONCLUSTERED INDEX [IX_Posts_OldPostId] ON [dbo].[Posts] 
(
    [OldPostId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
GO

USE [XWing]
GO

/****** Object:  Index [IX_Posts_UserId]    Script Date: 02/17/2012 13:32:17 ******/
CREATE NONCLUSTERED INDEX [IX_Posts_UserId] ON [dbo].[Posts] 
(
    [UserId] ASC
)
INCLUDE ( [PostId]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
GO

USE [XWing]
GO

/****** Object:  Index [SX_Posts_Location]    Script Date: 02/17/2012 13:32:22 ******/
CREATE SPATIAL INDEX [SX_Posts_Location] ON [dbo].[Posts] 
(
    [LatLongPoint]
)USING  GEOGRAPHY_GRID 
WITH (
GRIDS =(LEVEL_1 = LOW,LEVEL_2 = LOW,LEVEL_3 = MEDIUM,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 16, PAD_INDEX  = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

USE [XWing]
GO

/****** Object:  Index [UIX_Posts_UniqueUri]    Script Date: 02/17/2012 13:32:41 ******/
CREATE UNIQUE NONCLUSTERED INDEX [UIX_Posts_UniqueUri] ON [dbo].[Posts] 
(
    [UniqueUri] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

编辑 4

是为 CleanedUri添加以下索引UpdatePostsCleanedUriUniqueUri 的执行计划:

CREATE NONCLUSTERED INDEX [IX_Posts_CleanedUri_Include_PostId] ON [dbo].[Posts] 
(
    [CleanedUri] ASC
)
INCLUDE ( [PostId]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)

Rem*_*anu 14

即使我只是将某些行选择到表变量中,这实际上会导致整个表的表锁定吗?

锁定升级。如果您的“选择”不是......选择性的,如果它必须扫描表的大部分,那么引擎可能会升级为表锁。请参阅锁升级(数据库引擎)

我可以看到发生了 2 个锁升级,这是否意味着行锁已升级为表锁?

他们是锁定升级事件,还是只是锁定升级尝试?如果成功,则事务已锁定整个表。

有人能给我一些关于如何进一步追踪这个僵局的提示吗?

从单独的锁 POV 来解决这个问题并不是很有成效。典型的调查侧重于捕获和分析死锁图。请参阅使用 SQL Server Profiler 分析死锁。捕获死锁图并将其张贴在这里,也许我们可以提供帮助。上传死锁图XML,而不是死锁的图片,请参阅如何:保存死锁图 (SQL Server Profiler)

死锁图:

Spid 58 在 IX 模式下具有页面 7:1:11066,并且正在阻止希望它在 S 模式下使用的 spid 61。Spid 61 的页面 7:1:1932345 处于 S 模式,并且正在阻止 spid 58 上有 IU 模式锁定但希望将其转换为 IX。由于增加了并行性和一长串“我也是”服务员,事情变得复杂了,但基本问题可以简化为我上面描述的问题。

这是典型的索引缺失模式。您有一个更新程序,XWingNew.dbo.UpdatePostsCleanedUriUniqueUri它发出可能扫描整个XWingNew.dbo.Posts表的更新。同时,您有几个读者可能也在一些 linq 生成的查询中扫描整个表,这些查询在 XML: 中被截断SELECT [UnionAll3].[C2] AS [C1], [UnionAll3].[C3] AS [C2], [UnionAll3].[C4] AS [C3], [UnionAll3].[C5] AS [C4],... 。因为更新和选择发出表扫描,当更新尝试将它在扫描阶段获得的 IU 页锁转换为更新合格行所需的 IX 锁时,它们几乎可以保证死锁。

您现在有两种选择:

  • 修复您的应用程序。查询显然没有调整,也没有考虑索引策略。您不仅可以解决死锁问题,而且您的应用程序也会更快。
  • 简单的解决办法:启用读提交快照。这不会修复应用程序缺陷,但会隐藏它们并消除死锁。