我发现了一个僵局,似乎表明了我认为不可能的事情。死锁涉及两个进程:
1. process8cf948 SPID 63
对临时表 #PB_Cost_Excp_Process_Invoices_Work 执行 ALTER TABLE。
拥有表 #PB_Cost_Excp_Process_Invoices_Work 的 IX 锁,对象 ID 为 455743580
2. process4cb3708 SPID 72
在临时表 #PB_Cost_Excp_Process_Invoices_Work 上执行更新,它应该是它自己唯一的表副本。
在#PB_Cost_Excp_Process_Invoices_Work 上拥有 Sch-M 锁,对象 ID 为 455743580!
这应该是不可能的。我错过了什么吗?#Temporary 表真的在这两个 SPID 之间重用了吗?
这是在 SQL Server 2008 R2 Service Pack 2 和累积更新 1(版本 10.50.4260)上。
完整的未更改死锁跟踪如下。请注意这两个进程如何在具有相同表名的相同对象 ID 上运行 #PB_Cost_Excp_Process_Invoices_Work_SNIP_0000000D8519:
12/14/2012 13:46:03,spid23s,Unknown,waiter id=process8cf948 mode=X requestType=wait
12/14/2012 13:46:03,spid23s,Unknown,waiter-list
12/14/2012 13:46:03,spid23s,Unknown,owner id=process4cb3708 mode=Sch-M
12/14/2012 13:46:03,spid23s,Unknown,owner-list
12/14/2012 13:46:03,spid23s,Unknown,objectlock lockPartition=0 objid=455743580 subresource=FULL dbid=2 objectname=tempdb.dbo.#PB_Cost_Excp_Process_Invoices_Work_________________________________________________________________________________0000000D8519 id=lock371705d00 mode=Sch-M associatedObjectId=455743580
12/14/2012 13:46:03,spid23s,Unknown,waiter id=process4cb3708 mode=Sch-M …
Run Code Online (Sandbox Code Playgroud) 我们有一个生成库存报告的流程。在客户端,进程拆分可配置数量的工作线程,为报告构建一个数据块,该数据块对应于许多商店中的一个(可能是数千个,通常是数十个)。每个工作线程调用一个执行存储过程的 Web 服务。
处理每个块的数据库进程将一堆数据收集到一个#Temporary 表中。在每个处理块结束时,数据将写入 tempdb 中的永久表。最后,在进程结束时,客户端的一个线程从永久 tempdb 表中请求所有数据。
运行此报告的用户越多,它的速度就越慢。我分析了数据库中的活动。有一次,我看到 35 个单独的请求在过程中的某一点都被阻止了。所有这些 SPIDLATCH_EX
对资源类型的等待时间大约为 50 毫秒METADATA_SEQUENCE_GENERATOR (00000010E13CA1A8)
。一个 SPID 拥有此资源,而所有其他 SPID 都处于阻塞状态。我在网络搜索中没有找到有关此等待资源的任何信息。
我们正在使用的 tempdb 中的表确实有一IDENTITY(1,1)
列。这些 SPID 是否在等待 IDENTITY 列?我们可以使用哪些方法来减少或消除阻塞?
服务器是集群的一部分。服务器在 64 位 Windows 2008 R2 Enterprise 上运行 64 位 SQL Server 2012 Standard Edition SP1。服务器有 64 GB RAM 和 48 个处理器,但数据库只能使用 16 个,因为它是标准版。
(请注意,我对在 tempdb 中使用永久表来保存所有这些数据的设计并不感到兴奋。改变这将是一个有趣的技术和政治挑战,但我愿意接受建议。)
更新 4/23/2013
我们已经与 Microsoft 建立了一个支持案例。随着我们了解更多,我会更新这个问题。
更新 5/10/2013
SQL Server 支持工程师同意等待是由 IDENTITY 列引起的。删除 IDENTITY 消除了等待。我们无法在 SQL 2008 R2 上复制该问题;它仅发生在 SQL 2012 …
我们有一个从商店获取数据并更新公司范围内的库存表的流程。该表按日期和按项目为每个商店提供行。对于拥有许多商店的客户,该表可能会变得非常大——大约有 5 亿行。
当商店输入数据时,此库存更新过程通常每天运行多次。这些运行仅从少数商店更新数据。但是,客户也可以运行此程序来更新过去 30 天内的所有商店。在这种情况下,该过程启动 10 个线程并在单独的线程中更新每个商店的库存。
客户抱怨这个过程需要很长时间。我分析了这个过程,发现插入到这个表中的一个查询消耗的时间比我预期的要多得多。此 INSERT 有时会在 30 秒内完成。
当我对这个表(由 BEGIN TRAN 和 ROLLBACK 限定)运行 ad-hoc SQL INSERT 命令时,ad-hoc SQL 以毫秒的数量级完成。
执行缓慢的查询如下。这个想法是插入不存在的记录,然后在我们计算各种数据位时更新它们。该过程的前一个步骤已确定需要更新的项目,进行一些计算,并将结果填充到 tempdb 表 Update_Item_Work 中。这个进程在 10 个独立的线程中运行,每个线程在 Update_Item_Work 中有自己的 GUID。
INSERT INTO Inventory
(
Inv_Site_Key,
Inv_Item_Key,
Inv_Date,
Inv_BusEnt_ID,
Inv_End_WtAvg_Cost
)
SELECT DISTINCT
UpdItemWrk_Site_Key,
UpdItemWrk_Item_Key,
UpdItemWrk_Date,
UpdItemWrk_BusEnt_ID,
(CASE UpdItemWrk_Set_WtAvg_Cost WHEN 1 THEN UpdItemWrk_WtAvg_Cost ELSE 0 END)
FROM tempdb..Update_Item_Work (NOLOCK)
WHERE UpdItemWrk_GUID = @GUID
AND NOT EXISTS
-- Only insert for site/item/date combinations that don't exist
(SELECT …
Run Code Online (Sandbox Code Playgroud) 我在我的 PC 上运行 SQL Server 2014 Developer Edition。我正在尝试查看 system_health 会话中的数据。在SSMS中,我已经连接到数据库,扩展了服务器/管理/扩展事件/会话。我看到 AlwaysON_health(停止)和 system_health(运行)。
当我右键单击 system_health 会话时,出现以下错误:
使用提供的参数初始化存储失败。(Microsoft.SqlServer.XEventStorage) 找不到名为“system_health”的扩展事件会话。确保会话存在并已启动。(Microsoft SQL Server,错误:25728)
我展开 system_health 并看到目标 package0.event_file 和 package0.ring_buffer。如果我右键单击任一目标并选择“查看目标数据”,则会出现此错误:
使用提供的参数初始化存储失败。(Microsoft.SqlServer.XEventStorage) 无法查看函数“fn_MSXe_read_event_stream”,因为它不存在或您没有权限。(Microsoft SQL Server,错误:15151)
该功能确实存在。我可以运行它:
select * from fn_MSXe_read_event_stream('system_health', 0);
Run Code Online (Sandbox Code Playgroud)
当我这样做时,我收到此错误:
Msg 25728, Level 16, State 10, Line 6
The Extended Events session named "system_health" could not be found. Make sure the session exists and is started.
Run Code Online (Sandbox Code Playgroud)
我知道 system_health 会话在那里。我在会议列表中看到:
select * from sys.dm_xe_sessions
address name name
------------------ -------------
0x00000001FF6510C1 system_health
Run Code Online (Sandbox Code Playgroud)
我已经用我自己的自定义事件会话尝试过这个。我也看不到他们的实时数据。
我可以从sys.dm_xe_session_targets
. …
我有一个关于这个查询计划的问题。
我们在测试环境中有一个表 Order_Details_Taxes,它有 11,225,799 行。该表有一个列 OrdTax_PLTax_LoadDtl_Key,它在每一行上都是 NULL。此测试环境的配置方式使此列始终为 NULL。此列上有一个索引。
我使用列的 NULL 值对该表运行了一些查询。NULL INNER JOIN 永远不会产生任何结果。
declare @Keys table (KeyValue decimal(15,0))
insert into @Keys (KeyValue) values (null)
select OrdTax_PLTax_LoadDtl_Key
from @Keys
inner join Order_Details_Taxes
on OrdTax_PLTax_LoadDtl_Key = KeyValue
select *
from @Keys
inner join Order_Details_Taxes
on OrdTax_PLTax_LoadDtl_Key = KeyValue
Run Code Online (Sandbox Code Playgroud)
这些是查询计划中的第一个查询。第一个select
从亿行表开始并连接到@Keys。第二个select
从@Keys 开始,但它对这个表进行聚集索引扫描。
我知道在大多数情况下临时@Tables 是有问题的,所以我将查询更改为使用临时 #Table:
if object_id ('tempdb..#Keys') is not null
drop table #Keys
create table #Keys (KeyValue decimal(15,0))
insert into #Keys (KeyValue) values (null)
select OrdTax_PLTax_LoadDtl_Key …
Run Code Online (Sandbox Code Playgroud) 我们的一位客户经常遇到僵局。死锁大多在同一个UPDATE
语句上。死锁遵循一种模式,其中两个 SPID 都已获取页上的更新 (U) 锁,并且都尝试将 U 页锁向上转换为意图排他 (IX) 锁。有时只涉及一页;有时几个。
我们使用跟踪标志 1222捕获了死锁跟踪。SQL Server 日志显示了许多具有以下模式的死锁(按自下而上的顺序):
waiter id=processe0dc2088 mode=U requestType=convert
waiter id=process2f9db2478 mode=U requestType=convert
waiter-list
owner id=processe0dc2088 mode=IX
owner id=process2f9db2478 mode=IX
owner-list
pagelock fileid=1 pageid=5794 dbid=2 objectname=tempdb.dbo.Item_Package_Site_Costs_Work id=lock1b22de480 mode=IX associatedObjectId=72057594131775488
resource-list
Run Code Online (Sandbox Code Playgroud)
两个进程都运行相同的 UPDATE 语句来在这个 tempdb 表上设置一个标志。此 tempdb 表包含需要在客户端调用之间保留的信息,直到客户端完成。该表有一个相当长的索引,以代表唯一进程 ID 的 GUID 开头。
我很难理解和模拟这种僵局。我用模拟数据尝试了各种数量的记录。
我的问题:
为什么这些进程获取U锁然后转换为IX? 我希望 DELETE 开始获取 IX 锁。
我怎样才能防止死锁?
导致死锁的语句如下。该流程刚刚完成了对单个商店中商品清单的成本查询。它试图指出发现了一个成本。
请注意,UPDATE 语句中有一个已弃用的 (NOLOCK)。这会是一个促成因素吗?
UPDATE tempdb..Item_Package_Site_Costs_Work
SET ItemPkgSiteCost_VINCostFound = 1,
ItemPkgSiteCost_VendCost_Key = SiteCosts_VendCost_Key
FROM …
Run Code Online (Sandbox Code Playgroud) 我们有一个配置为以用户“dbo”身份运行的 SQL 代理作业。此作业获取sys.databases中定义的与命名方案匹配的数据库列表。然后该作业执行一些动态 SQL 来清理一些旧的事务表。作业从 tempdb 开始。
代理作业在我自己的开发数据库(SQL Server 2008 R2 标准版 10.50.2500.0)上运行良好。作业正确获取数据库列表。但是,在客户系统(SQL Server 2008 R2 标准版 64 位 10.50.1600.1)上,尽管存在匹配的数据库,但作业找不到任何匹配的数据库。
在客户系统上,select * from sys.databases
在查询窗口中针对 tempdb的简单运行将返回系统中的所有 10 个数据库。
使用探查器,我EXECUTE AS USER = N'dbo' WITH NO REVERT
在运行此作业时看到 SQL 代理调用。我使用以下 SQL 复制了该问题:
use tempdb
-- results: tempdb, <my SQL user>, dbo
select
db_name() as [Current Database],
suser_name() as [Current Context],
user_name() as [Current User]
-- results: tempdb, 10 (which I expect)
select
db_name() as [Current Database],
count(*) …
Run Code Online (Sandbox Code Playgroud) 我有一个 SQL 脚本,当我想查看数据库中发生的事情时运行该脚本。该脚本有许多从 DMV 返回信息的查询,但我最常用的两个是“请求”(sys.dm_exec_requests、sys.dm_exec_sessions等)和“锁”(sys.dm_tran_locks)。输出类似于 SQL Server 活动监视器,但它显示更多信息。
有时,请求会出现在 Requests 查询中,但它会在 Locks 查询运行之前完成。例如,Requests 查询可能显示 SPID 51 正在等待锁定资源,但 Locks 查询不包含 SPID 51 的任何锁定信息。(我知道来自的wait_type
和wait_resource
列sys.dm_exec_requests
。)
有没有办法确保这两个单独的查询显示数据库活动的连贯快照?
我预计商业数据库监控应用程序一定会遇到同样的问题。
我已经尝试在 SERIALIZABLE 并发中运行这些查询并向 DMV 连接添加锁定提示,但查询未获取锁定。无论如何,我不想在生产中这样做。
到目前为止,我最好的想法是:
从不同的会话同时运行这些查询。
在一个查询中加入请求和锁。考虑到我一次看到超过 100,000 个锁,这个连接会返回很多重复的请求和会话数据,但它可能会起作用。
我对扩展事件不够熟悉,不知道它们是否会更好地工作——也许是事件配对?
我看到时间戳(rowversion) 列出现了一些意外行为。我创建了一个测试表:
create table Test
(
Test_Key int identity(1,1) primary key clustered,
Test_Value int,
Test_Thread int,
ts timestamp
)
create nonclustered index IX_Test_Value on Test (Test_Value) -- probably irrelevant
Run Code Online (Sandbox Code Playgroud)
我启动了两个线程同时运行插入到这个表中。第一个线程正在运行以下代码:
declare @i int = 0
while @i < 100
begin
insert into Test (Test_Value, Test_Thread) select n, 1 from dbo.fn_GenerateNumbers(10000)
set @i = @i + 1
end
Run Code Online (Sandbox Code Playgroud)
第二个线程正在运行相同的代码,只是它正在select n, 2
从函数中插入其线程 ID。
先说一下函数。这使用了一系列带有 ROW_NUMBER() 的交叉连接的公共表表达式来非常快速地按顺序返回大量数字。我从Itzik Ben-Gan的一篇文章中学到了这个技巧,所以要归功于他。我认为函数的实现并不重要,但无论如何我都会包含它:
CREATE FUNCTION dbo.fn_GenerateNumbers(@count int)
RETURNS TABLE WITH SCHEMABINDING
AS …
Run Code Online (Sandbox Code Playgroud) sql-server ×9
concurrency ×2
deadlock ×2
dmv ×1
join ×1
null ×1
performance ×1
security ×1
t-sql ×1
timestamp ×1
wait-types ×1