在 SQL Server 中,锁通常从行或页 -> 表升级。从 SQL Server 2008 开始,添加了一个新级别的锁升级 -分区级别。
但是,这不会为分区表自动启用 - 默认情况下,该表设置为跳过分区锁定并从行或页 -> 表开始。为什么会这样?有什么原因我不想把我所有的表从 切换TABLE
到AUTO
以便它们将锁升级到分区级别而不是表级别?
由于默认值仍然是TABLE
,我认为AUTO
.
我有一项任务要更新生产表中的 500 万行,而无需长时间锁定整个表
所以,我使用了多次帮助我的方法 - 一次更新前 (N) 行,块之间的间隔为 1-N 秒
这次从一次更新前 (1000) 行开始,监视扩展事件会话中的lock_escalation
事件
lock_escalation
在每次更新操作期间出现,所以我开始将每个块1000 -> 500 -> 200 -> 100 -> 50
行的行数降低到 1
之前(不是使用这个表,并且对于删除操作 - 不是更新),将行数降低到 200 或 100,有助于摆脱lock_escalation
事件
但是这一次,即使每 1 次更新操作有 1 行,表lock_escalation
仍然显示。每次更新操作的持续时间大致相同,无论是一次 1 行还是 1000 行
在我的情况下如何摆脱表锁升级?
@@TRANCOUNT 为零
扩展事件:
代码:
set nocount on
declare
@ChunkSize int = 1000, -- count rows to remove in 1 chunk
@TimeBetweenChunks char(8) = '00:00:01', -- interval between chunks
@Start datetime,
@End …
Run Code Online (Sandbox Code Playgroud) sql-server extended-events lock-escalation sql-server-2017 batch-processing
我有一个大的删除存储过程,并且在删除不会删除任何内容的情况下重现了死锁。
看起来遇到死锁的存储过程的部分是这样的(更改了表名):
DELETE d
FROM Table1 d
inner join dbo.Table2 orc on orc.id = d.Table2Id
inner join dbo.Table3 orr on orr.id = orc.Table3Id
inner join Table4 oeh on oeh.id = orr.Table4Id
inner join @deleteEntities de on de.id = oeh.EntityId
Run Code Online (Sandbox Code Playgroud)
在我看来,尝试从这个大表中删除时,两个删除操作同时运行并且彼此死锁。对于这些项目,我知道不会有非常大的表 table1、table2、table3 的记录。
我想知道这是否可以通过更改为:
DELETE d
FROM Table1 d WITH(rowlock)
inner join dbo.Table2 orc on orc.id = d.Table2Id
inner join dbo.Table3 orr on orr.id = orc.Table3Id
inner join Table4 oeh on oeh.id = orr.Table4Id
inner join @deleteEntities de on de.id = …
Run Code Online (Sandbox Code Playgroud) 我试图理解为什么在某些情况下锁定计数sys.dm_tran_locks
和sqlserver.lock_acquired
扩展事件存在差异。这是我的重现脚本,我StackOverflow2013
在 SQL Server 2019 RTM 上使用数据库,兼容性级别为 150。
/* Initial Setup */
IF OBJECT_ID('dbo.HighQuestionScores', 'U') IS NOT NULL
DROP TABLE dbo.HighQuestionScores;
CREATE TABLE dbo.HighQuestionScores
(
Id INT PRIMARY KEY CLUSTERED,
DisplayName NVARCHAR(40) NOT NULL,
Reputation BIGINT NOT NULL,
Score BIGINT
)
INSERT dbo.HighQuestionScores
(Id, DisplayName, Reputation, Score)
SELECT u.Id,
u.DisplayName,
u.Reputation,
NULL
FROM dbo.Users AS u;
CREATE INDEX ix_HighQuestionScores_Reputation ON dbo.HighQuestionScores (Reputation);
Run Code Online (Sandbox Code Playgroud)
接下来我用一个大的假行数更新表统计信息
/* Chaotic Evil. */
UPDATE STATISTICS dbo.HighQuestionScores WITH ROWCOUNT = 99999999999999;
DBCC …
Run Code Online (Sandbox Code Playgroud) 我一直在运行dbo.sp_BlitzIndex
并且在我的数据库的主表上有 4 个有点相似的索引。每个都有大量的等待和升级尝试。我不确定这些来自哪里。我没有缺少索引。我没有看到这个表上的搜索需要很长时间,所以不确定这些是来自选择还是插入。需要一些关于下一步看哪里的指示。
CORE.tblCase.idx_tblCase_bClosed_nUserID_PrimaryAgent (2):
行锁等待:1;总持续时间:0 秒;平均持续时间:0 秒;
页面锁定等待:85;总时长:92分钟;平均时长:1分钟;
锁升级尝试:100,965;
实际升级:1. 表上的 NC 索引:1CORE.tblCase.idx_tblCase_bClosed_nActionID_Last_nWorkflowID (27):
行锁等待:3;总持续时间:26 秒;平均持续时间:8 秒;
页面锁定等待:15;总时长:10分钟;平均持续时间:42 秒;
锁升级尝试:31,908;
实际升级:0。表上的 NC 索引:1CORE.tblCase.idx_tblCase_bClosed_nWorkflowID_nActionID_Last(28):
页面锁定等待:112;总时长:85 分钟;平均持续时间:46 秒;
锁升级尝试:31,748;
实际升级:0。表上的 NC 索引:1CORE.tblCase.idx_tblCase_bClosed_nCaseID_INC (35):
行锁等待:2;总时长:13分钟;平均时长:6 分钟;
页面锁定等待:307;总时长:399分钟;平均时长:1分钟;
锁升级尝试:43,090;
实际升级:0。表上的 NC 索引:1
假设您有一个包含数千万行的大表。
您想要UPDATE large_table SET col=value WHERE col=other_value
...但未col
建立索引,并且EXPLAIN
显示该查询将对整个表执行 seq 扫描。
这里的锁行为是什么?根据大多数说法,Postgres 仅锁定 UPDATE 查询受影响的行,并且没有锁升级。那么它是否首先搜索要更新的行,然后只锁定找到的行?不过,在这种情况下,其他查询同时更新行似乎可能会出现问题。它是否“在找到每一行时”锁定它们,即在进行 seq 扫描时逐步锁定行?
因此,我认为这里最好的情况是它在找到行时锁定行,并且(仅)受影响的行将被锁定,直到 UPDATE 查询完成为止。
但我担心此查询可能最终会阻止对表的所有写入,直到完成为止。
我读过这篇文章: https: //habr.com/en/company/postgrespro/blog/503008/我认为最坏的情况不会发生,但在这里https://blog.heroku.com/curious-case-table -locking-update-query是类似信息的可能不准确表示,这让我有些怀疑。
该应用程序仅使用SELECT
,SELECT FOR UPDATE
和UPDATE
查询(即除这些之外没有其他显式锁)。该表有其他表的外键,其他表也有该表的外键。
我们使用的是 Postgres 11。
我使用定制的 Stack Overflow 数据库(180GB)并运行一个简单的更新查询:(Users 表上只有一个聚集索引)
Begin Tran
Update U set U.Reputation=100000
from StackOverflow.dbo.Users as U
where U.CreationDate = '2008-10-10 14:26:33.540'
Run Code Online (Sandbox Code Playgroud)
查询计划:
此查询会导致锁升级。我无法在另一个窗口中使用同一个表运行查询:
select * from StackOverflow.dbo.Users as U where U.id=11
Run Code Online (Sandbox Code Playgroud)
如果我option (maxdop 1)
在查询末尾添加以避免并行,则一切都很好(计划)。
在较小的 Stack Overflow DB (StackOverflow2013 - 52GB) 中不会发生锁升级(计划)。
如何确定导致升级的数据量?
我使用 SQL Server 2019。数据库兼容级别为 150。
表信息:
sql-server ×7
locking ×2
deadlock ×1
delete ×1
index-tuning ×1
parallelism ×1
partitioning ×1
postgresql ×1