SQL Server 2016 - 创建不存在的记录时存储过程锁定
SQL Server 2016 - stored procedure lock when creating not existing records
我的数据库中有以下表格:
------------------------------------------
| EventDefinitions |
------------------------------------------
| EventDefinitionId: uniqueidentifier |
| Code: varchar(63) |
| EventSystem: int |
------------------------------------------
------------------------------------------
| Event |
------------------------------------------
| EventId: uniqueidentifier |
| EventDateTime: datetime2 |
| EventDefinitionId: uniqueidentifier |
------------------------------------------
事件定义 (1) <- 事件 (n)
EventDefinition 对 EventSystem 和 Code 有唯一约束。
我正在开发一个存储过程,该过程应根据指定的代码和事件系统创建一个事件记录并将其映射到正确的 EventDefinition 记录。
如果不存在匹配的 EventDefinition,该过程应创建一个新的 EventDefinition。
该程序将被多个应用程序同时执行。
我的目标:
- 该过程必须是并行可执行的。
使用尽可能少的锁,以便可以同时写入多个Event记录。
- 当 EventDefinition 不存在时,它应该只由一个进程创建。
该过程应创建某种锁以确保具有指定代码和事件系统的事件定义记录仅创建一次(否则数据库将抛出唯一约束违规异常)。
其他进程应将事件记录映射到新创建的 EventDefinition 记录。
这是我到目前为止尝试过的方法:
CREATE PROCEDURE dbo.procWriteEvent @eventCode varchar(63), @eventSystem int
AS
DECLARE @eventDefinitionId uniqueidentifier
DECLARE @insertedEventDefinition table(EventDefinitionId uniqueidentifier)
DECLARE @currentLevel varchar(255)
BEGIN TRANSACTION
-- Try read event definition
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
-- Create event definition if not exists
IF @eventDefinitionId IS NULL
BEGIN
BEGIN TRANSACTION
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
-- Check if event definition was created by an other process
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
-- Create event definition
IF @eventDefinitionId IS NULL
BEGIN
INSERT INTO EventDefinitions (Code, EventSystem, Severity)
OUTPUT Inserted.EventDefinitionId INTO @insertedEventDefinition
VALUES (@eventCode, @eventSystem, 0)
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM @insertedEventDefinition)
END
COMMIT TRANSACTION
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
END
INSERT INTO Events (EventDefinitionId, EventDateTime)
VALUES (@eventDefinitionId, GETDATE())
COMMIT TRANSACTION
问题是,我当前的 SQL 代码在程序并行执行时会产生死锁。
您的代码过于复杂。 Sql 服务器进行最多的检查并阻止自己。
只需 try/catch 插入,如果由于违反 UNIQUE 约束而失败,请重新阅读 EventDefinitions。种类
CREATE PROCEDURE dbo.procWriteEvent @eventCode varchar(63), @eventSystem int
AS
DECLARE @eventDefinitionId uniqueidentifier
DECLARE @insertedEventDefinition table(EventDefinitionId uniqueidentifier)
DECLARE @currentLevel varchar(255)
-- Try read event definition
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
-- Create event definition if not exists
IF @eventDefinitionId IS NULL
BEGIN TRY
INSERT INTO EventDefinitions (Code, EventSystem, Severity)
OUTPUT Inserted.EventDefinitionId INTO @insertedEventDefinition
VALUES (@eventCode, @eventSystem, 0);
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM @insertedEventDefinition)
END TRY
BEGIN CATCH
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
END CATCH
-- proceed with @eventDefinitionId value
我的数据库中有以下表格:
------------------------------------------
| EventDefinitions |
------------------------------------------
| EventDefinitionId: uniqueidentifier |
| Code: varchar(63) |
| EventSystem: int |
------------------------------------------
------------------------------------------
| Event |
------------------------------------------
| EventId: uniqueidentifier |
| EventDateTime: datetime2 |
| EventDefinitionId: uniqueidentifier |
------------------------------------------
事件定义 (1) <- 事件 (n)
EventDefinition 对 EventSystem 和 Code 有唯一约束。
我正在开发一个存储过程,该过程应根据指定的代码和事件系统创建一个事件记录并将其映射到正确的 EventDefinition 记录。
如果不存在匹配的 EventDefinition,该过程应创建一个新的 EventDefinition。
该程序将被多个应用程序同时执行。
我的目标:
- 该过程必须是并行可执行的。
使用尽可能少的锁,以便可以同时写入多个Event记录。 - 当 EventDefinition 不存在时,它应该只由一个进程创建。
该过程应创建某种锁以确保具有指定代码和事件系统的事件定义记录仅创建一次(否则数据库将抛出唯一约束违规异常)。 其他进程应将事件记录映射到新创建的 EventDefinition 记录。
这是我到目前为止尝试过的方法:
CREATE PROCEDURE dbo.procWriteEvent @eventCode varchar(63), @eventSystem int
AS
DECLARE @eventDefinitionId uniqueidentifier
DECLARE @insertedEventDefinition table(EventDefinitionId uniqueidentifier)
DECLARE @currentLevel varchar(255)
BEGIN TRANSACTION
-- Try read event definition
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
-- Create event definition if not exists
IF @eventDefinitionId IS NULL
BEGIN
BEGIN TRANSACTION
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
-- Check if event definition was created by an other process
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
-- Create event definition
IF @eventDefinitionId IS NULL
BEGIN
INSERT INTO EventDefinitions (Code, EventSystem, Severity)
OUTPUT Inserted.EventDefinitionId INTO @insertedEventDefinition
VALUES (@eventCode, @eventSystem, 0)
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM @insertedEventDefinition)
END
COMMIT TRANSACTION
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
END
INSERT INTO Events (EventDefinitionId, EventDateTime)
VALUES (@eventDefinitionId, GETDATE())
COMMIT TRANSACTION
问题是,我当前的 SQL 代码在程序并行执行时会产生死锁。
您的代码过于复杂。 Sql 服务器进行最多的检查并阻止自己。 只需 try/catch 插入,如果由于违反 UNIQUE 约束而失败,请重新阅读 EventDefinitions。种类
CREATE PROCEDURE dbo.procWriteEvent @eventCode varchar(63), @eventSystem int
AS
DECLARE @eventDefinitionId uniqueidentifier
DECLARE @insertedEventDefinition table(EventDefinitionId uniqueidentifier)
DECLARE @currentLevel varchar(255)
-- Try read event definition
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
-- Create event definition if not exists
IF @eventDefinitionId IS NULL
BEGIN TRY
INSERT INTO EventDefinitions (Code, EventSystem, Severity)
OUTPUT Inserted.EventDefinitionId INTO @insertedEventDefinition
VALUES (@eventCode, @eventSystem, 0);
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM @insertedEventDefinition)
END TRY
BEGIN CATCH
SET @eventDefinitionId = (SELECT TOP 1 EventDefinitionId FROM EventDefinitions WHERE EventSystem = @eventSystem AND Code = @eventCode )
END CATCH
-- proceed with @eventDefinitionId value