SQL Server,插入一行会锁定整个表

12

我们遇到了一些死锁问题,我发了这个问题

在得到一些帮助和自己大量搜索后,我相信我已经弄清楚了情况。为了解决死锁而不控制锁升级,我需要了解为什么 SQL Server 在插入一行时会锁定整个表。

这是我的插入语句(使用重命名变量):

DECLARE 
    @Type1 INT = 11,
    @Type2 INT = NULL,
    @Value1 VARCHAR(20) = '0',
    @Value2 VARCHAR(20) = '0',
    @Value3 VARCHAR(20) = '0',
    @Value4 VARCHAR(20) = '0',
    @Date1 DATETIME = '2011-11-25',
    @Date2 DATETIME = '2011-11-25',
    @Value5 NVARCHAR(50) = '',
    @Value6 NVARCHAR(50) = '',
    @Type3 INT = NULL,
    @Value7 VARCHAR(20) = '4',
    @Type4 INT = 4,
    @Type5 INT = 15153,
    @Type6 INT = 3,
    @Type7 INT = 31,
    @Type8 INT = 5976,
    @Type9 INT = 5044,
    @Guid1 UNIQUEIDENTIFIER = 'a8293471-3hb4-442b-844f-44t92f17n67s',
    @Value8 VARCHAR(200) = '02jfgg55savolhffr1mkjf45',
    @value10 INT = 1,
    @Option2 BIT = 0,
    @Value9 VARCHAR(20) = null,
    @Option1 BIT = 0

insert into dbo.OurTable
(
    Type1
    ,Type2
    ,Value1
    ,Value2
    ,Value3
    ,Value4
    ,Date1
    ,Date2
    ,Value5
    ,Value6
    ,Type3
    ,Value7
    ,Type4
    ,Type5
    ,Type6
    ,Type7
    ,Type8
    ,Type9
    ,value10
    ,Col1
    ,Col2
    ,Col3
    ,Col4
    ,Value8
    ,Option2
    ,Value9
)
values
(
    CASE
        WHEN [dbo].[GetType](@Type1, null) = 6 AND @Option1 = 1 AND [dbo].[GetType](@Type4, 0) <> 1
        THEN 7
        ELSE [dbo].[GetType](@Type1, null)
    END
    ,[dbo].[GetType](@Type2, null)
    ,case when @Value1 = 'null' then null else CAST(@Value1 as numeric(18, 6)) end
    ,case when @Value2 = 'null' then null else CAST(@Value2 as numeric(18, 6)) end
    ,case when @Value3 = 'null' then null else CAST(@Value3 as numeric(18, 6)) end
    ,case when @Value4 = 'null' then null else CAST(@Value4 as numeric(18, 6)) end
    ,[dbo].[GetDate](@Date1, null)
    ,[dbo].[GetDate](@Date2, null)
    ,@Value5
    ,@Value6
    ,[dbo].[GetType](@Type3, null)
    ,case when @Value7 = 'null' then null else CAST(@Value7 as numeric(18, 6)) end
    ,[dbo].[GetType](@Type4, null)
    ,@Type6
    ,case when LOWER(@Type7) = 'null' then null else @Type7 end
    ,@Type5
    ,@Type9
    ,@Type8
    ,@value10
    ,GETDATE()
    ,GETDATE()
    ,[dbo].[GetGuid](@Guid1)
    ,[dbo].[GetGuid](@Guid1)
    ,@Value8
    ,@Option2
    ,case when @Value9 = 'null' then null else CAST(@Value9 as int) end
)

如果我在事务中运行此语句,然后在提交之前查询sys.dm_tran_locks,我将获得属于该会话的10233行。

SELECT *
FROM sys.dm_tran_locks l
WHERE l.resource_type <> 'DATABASE' AND l.request_session_id = 65

测试时,65是我当前窗口的会话ID。

此外,如果我查看表锁定(这是死锁的原因),我可以看到它在OurTable表上放置了一个X锁。

resource_type   resource_associated_entity_id   Name    resource_lock_partition request_mode    request_type    request_status
OBJECT  290100074   OurTable    0   X   LOCK    GRANT
OBJECT  290100074   OurTable    1   X   LOCK    GRANT
OBJECT  290100074   OurTable    2   X   LOCK    GRANT
OBJECT  290100074   OurTable    3   X   LOCK    GRANT
OBJECT  290100074   OurTable    4   X   LOCK    GRANT
OBJECT  290100074   OurTable    5   X   LOCK    GRANT
OBJECT  290100074   OurTable    6   X   LOCK    GRANT
OBJECT  290100074   OurTable    7   X   LOCK    GRANT
OBJECT  290100074   OurTable    8   X   LOCK    GRANT
OBJECT  290100074   OurTable    9   X   LOCK    GRANT
OBJECT  290100074   OurTable    10  X   LOCK    GRANT
OBJECT  290100074   OurTable    11  X   LOCK    GRANT
OBJECT  290100074   OurTable    12  X   LOCK    GRANT
OBJECT  290100074   OurTable    13  X   LOCK    GRANT
OBJECT  290100074   OurTable    14  X   LOCK    GRANT
OBJECT  290100074   OurTable    15  X   LOCK    GRANT

我不知道这是由于锁升级还是从一开始就请求对表的独占锁。无论如何,这会导致死锁问题。
在单个表上有16个锁行的原因是锁分区
我的问题是,为什么它不请求对表的意向独占锁(IX)?而是请求独占锁。如何防止这种情况发生?我在调整顾问中没有获得任何调整提示,我已经尝试过了。
编辑 OurTable上有一个插入触发器,它更新OurTable3上的一个字段。它看起来像这样:
UPDATE OurTable3 SET Date1 = NULL
    FROM OurTable3 as E 
        JOIN OurTable2 as C on E.Id = C.FKId
        JOIN OurTable as ETC on ETC.FKId = C.Id
            AND (ETC.Date2 IS NULL OR CAST(ETC.Date2 AS DATE) > E.Date1) 
            AND ETC.Type1 = 1

正如您所看到的,它不会直接更新OurTable,而是查询OurTable以便在OurTable3中更新正确的行。


1
你看过事务隔离级别了吗?http://msdn.microsoft.com/zh-cn/library/ms173763.aspx - dash
1
表结构是什么?你有聚集键吗? - marc_s
1
如果您只插入不引用函数的常量,您是否看到相同的锁定行为? - Martin Smith
1
你在这个表上有插入触发器吗? - George Mastros
1
不确定这是否会解决锁定问题,但我会在触发器中添加e.Date1不为空的条件。你尝试在插入语句上加一个提示符with (rowlock)吗?在你的插入语句中尝试包含提示符(nolock可能会消除锁定问题,但看不到新数据,所以需要考虑一下)。 - paparazzo
显示剩余5条评论
1个回答

16
我找到了答案。我们团队的一位开发者犯了一个小错误(我总是责怪别人 :-) 其实我应该已经知道答案,因为Martin Smith在另一个问题中指出我应该检查ALLOW_ROW_LOCKS和ALLOW_PAGE_LOCKS。但当时我们认为partitionid与索引id有关,所以我只检查了那个索引。
我做的是创建了一个具有相同数据的新表。这样就消除了影响,我只在新表上拥有正确的IX锁。然后我创建了每个索引并在每次创建之间进行测试,直到突然又出现了这种情况。
我在OurTable上找到了这个索引:
CREATE NONCLUSTERED INDEX [IX_OurTable] ON [dbo].[OurTable] 
(
    [Col1] ASC,
    [Col2] ASC,
    [Col3] ASC,
    [Col4] ASC,
    [Col5] ASC
)
INCLUDE ( [Col6],
[Col7],
[Col8],
[Col9]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = OFF, ALLOW_PAGE_LOCKS  = OFF, FILLFACTOR = 90) ON [PRIMARY]
GO

如果指定 ALLOW_ROW_LOCKS = OFF 和 ALLOW_PAGE_LOCKS = OFF,那么插入和选择操作显然会受到影响。

感谢您的评论,并特别感谢 Martin 帮助我解决这些死锁问题。


2
感谢分享你的解决方案,看起来很棘手,我相信你的帖子会帮助其他人。 - jpierson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接