问题
I have a large migration script (about 2000 lines) that fails because of a deadlock on some metadata, and the lock types are SCH_S and SCH_M according to some xEvents data I've captured on it. The script is quite complicated, so I'm trying to recreate the same scenario using minimal scripting, so I can investigate it further. One difference between my scenario and some of the ones I've found online is that my migration script is being executed in a single process, as opposed to many examples online which use one window to make some schema changes and another window to query.
The following script is a little messy, but is my attempt at recreating the issue, but it doesn't work.
SET NOEXEC OFF;
DROP FUNCTION IF EXISTS dbo.testfunc;
DROP TABLE IF EXISTS dbo.test;
-- Create some objects.
BEGIN TRANSACTION;
CREATE TABLE test
(
testid INT
);
GO
IF @@error <> 0
AND @@trancount > 0
SET NOEXEC ON;
GO
CREATE FUNCTION testfunc
()
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
RETURN
(
SELECT 2 * SUM(testid) FROM dbo.test
);
END;
GO
IF @@error <> 0
AND @@trancount > 0
SET NOEXEC ON;
GO
IF @@error <> 0
AND @@trancount > 0
ROLLBACK TRANSACTION;
ELSE IF @@trancount > 0
COMMIT TRANSACTION;
GO
-- Try to induce a SCH_S x SCH_M deadlock.
BEGIN TRANSACTION;
GO
IF @@error <> 0
AND @@trancount > 0
SET NOEXEC ON;
GO
ALTER FUNCTION dbo.testfunc
()
RETURNS INT
AS
BEGIN
RETURN
(
SELECT 2 * SUM(testid) FROM dbo.test
);
END;
GO
EXECUTE sp_lock
IF @@error <> 0
AND @@trancount > 0
ROLLBACK TRANSACTION;
GO
ALTER TABLE dbo.test ADD teststring VARCHAR(12) NULL;
GO
IF @@error <> 0
AND @@trancount > 0
SET NOEXEC ON;
GO
SELECT TOP (1)
testid
FROM test;
GO
EXECUTE sp_lock
go
ALTER FUNCTION testfunc
()
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
RETURN
(
SELECT TOP 1 testid FROM dbo.test ORDER BY testid DESC
);
END;
GO
EXECUTE sp_lock
IF @@error <> 0
AND @@trancount > 0
SET NOEXEC ON;
GO
ALTER TABLE dbo.test ADD test_bit BIT NULL;
GO
EXECUTE sp_lock
IF @@error <> 0
AND @@trancount > 0
ROLLBACK TRANSACTION;
ELSE IF @@trancount > 0
COMMIT TRANSACTION;
GO
Can someone help me recreate this kind of scenario in single process?
Here is the deadlock xml report:
<deadlock>
<victim-list>
<victimProcess id="process1fbf61b8ca8" />
</victim-list>
<process-list>
<process id="process1fbf61b8ca8" taskpriority="0" logused="0" waitresource="METADATA: database_id = 5 USER_TYPE(user_type_id = 264), lockPartitionId = 11" waittime="2517" ownerId="46927562" transactionname="@OptionIDs" lasttranstarted="2020-01-16T13:03:44.790" XDES="0x1fb0b790490" lockMode="Sch-S" schedulerid="12" kpid="20032" status="suspended" spid="63" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2020-01-16T13:03:44.787" lastbatchcompleted="2020-01-16T13:03:44.783" lastattention="1900-01-01T00:00:00.783" clientapp="Microsoft SQL Server Management Studio - Query" hostname="userhost" hostpid="11492" loginname="CORP\user" isolationlevel="serializable (4)" xactid="46926609" currentdb="5" lockTimeout="4294967295" clientoption1="671287392" clientoption2="390200">
<executionStack>
<frame procname="unknown" line="39" stmtstart="-1" sqlhandle="0x0300050048c3ee382a43d70044ab000000000000000000000000000000000000000000000000000000000000">
unknown </frame>
<frame procname="adhoc" line="66" stmtstart="-1" sqlhandle="0x010005009a7e7609c0be4a35fb01000000000000000000000000000000000000000000000000000000000000">
CREATE FUNCTION [dbo].[tvf_GetRawPOLineDataRelatedToOption]
(
@CommunityID INT,
@FloorPlanID INT,
@OptionID INT,
@RelatedIncludedOptionIDToRemove INT
)
RETURNS @ReturnData TABLE
(
[POTemplateID] INT NOT NULL,
[POTemplateItemID] INT NOT NULL,
[POTemplateItemQuantityTypeID] INT NOT NULL,
[Quantity] DECIMAL(12, 5) NULL,
[FloorPlanQuantityTypeID] INT NULL,
[DynamicQuantityPercentage] INT NULL,
[QuantityForFlooringMaterialTypeID] INT NULL,
[HomesiteQuantityTypeID] INT NULL
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @DependencyData dbo.DependencyDataKeyValueTable;
DECLARE @POTemplateDependencyFilterData dbo.DependencyFilterKeyValueTable;
DECLARE @POTemplateItemDependencyFilterData dbo.DependencyFilterKeyValueTable;
DECLARE @POTemplateIDs dbo.IndexedIntTable;
/*
* Get the default dependency data for this floor plan in this community.
*/
INSERT INTO @DependencyData
SELECT [Key],
[Value]
FROM dbo. </frame>
</executionStack>
<inputbuf>
CREATE FUNCTION [dbo].[tvf_GetRawPOLineDataRelatedToOption]
(
@CommunityID INT,
@FloorPlanID INT,
@OptionID INT,
@RelatedIncludedOptionIDToRemove INT
)
RETURNS @ReturnData TABLE
(
[POTemplateID] INT NOT NULL,
[POTemplateItemID] INT NOT NULL,
[POTemplateItemQuantityTypeID] INT NOT NULL,
[Quantity] DECIMAL(12, 5) NULL,
[FloorPlanQuantityTypeID] INT NULL,
[DynamicQuantityPercentage] INT NULL,
[QuantityForFlooringMaterialTypeID] INT NULL,
[HomesiteQuantityTypeID] INT NULL
)
WITH SCHEMABINDING
AS
BEGIN
DECLARE @DependencyData dbo.DependencyDataKeyValueTable;
DECLARE @POTemplateDependencyFilterData dbo.DependencyFilterKeyValueTable;
DECLARE @POTemplateItemDependencyFilterData dbo.DependencyFilterKeyValueTable;
DECLARE @POTemplateIDs dbo.IndexedIntTable;
/*
* Get the default dependency data for this floor plan in this community.
*/
INSERT INTO @DependencyData
SELECT [Key],
[Value]
FROM dbo </inputbuf>
</process>
</process-list>
<resource-list>
<metadatalock subresource="USER_TYPE" classid="user_type_id = 264" dbid="5" lockPartition="11" id="lock1fb8a56df00" mode="Sch-M">
<owner-list>
<owner id="process1fbf61b8ca8" mode="Sch-M" />
<owner id="process1fbf61b8ca8" mode="Sch-S" requestType="wait" />
</owner-list>
<waiter-list>
<waiter id="process1fbf61b8ca8" mode="Sch-S" requestType="wait" />
</waiter-list>
</metadatalock>
</resource-list>
</deadlock>
回答1:
Can someone help me recreate this kind of scenario in single process?
There is a simple demo of this issue here.
BEGIN TRAN
go
CREATE TYPE dbo.OptionIDs AS TABLE( OptionID INT PRIMARY KEY )
go
DECLARE @OptionIDs dbo.OptionIDs;
go
ROLLBACK
It is certainly unusual to have a deadlock involving a single process and single resource.
The resource is metadatalock subresource="USER_TYPE" classid="user_type_id = 264"
The session already holds a SCH-M
metadata lock on a user defined type (probably as you created it earlier in the script) and this blocks an attempt to get a SCH-S
lock on the same object by the same session later.
The transaction name shown in the deadlock graph is @OptionIDs
- this is an internal system transaction that writes to the tempdb
transaction log when creating the instance of the table type corresponding with the DECLARE @OptionIDs
above. This is a separate transaction from the surrounding user transaction which is why the session is unexpectedly unable to get a SCH-S
lock despite already holding the SCH-M
lock.
This is blogged about by Aaron Bertrand here
来源:https://stackoverflow.com/questions/59793114/how-to-replicate-a-sch-s-sch-m-deadlock