4 sql-server insert concurrency
我们每秒从不同的平面文件导入多条记录。有时我们会遇到赛车条件,并重复唯一错误约束。我们正在插入和检索记录,
我听说有两种方法可以解决这个问题。这是更好的方法,我听说UPDLOCK,SERIALIZABLE是标准方法。但是,try catch 会阻止检查附加的 If 语句。两种方式都是完全证明,并且会停止重复插入吗?什么是最佳编码实践明智的,并且表现更好?
CREATE TABLE dbo.Customer
(
RowId bigint IDENTITY(1,1) NOT NULL,
CustomerId guid NOT NULL,
Name varchar(255) NOT NULL,
CONSTRAINT PK_RowId PRIMARY KEY CLUSTERED([RowId] ASC)
)
create unique nonclustered index [UN_CustomerId] ON [dbo].[Customer] ([CustomerId] ASC) include (Name)
create nonclustered index [UN_Name] ON [dbo].[Customer] ([Name] ASC) include (CustomerId)
Run Code Online (Sandbox Code Playgroud)
方法一:
IF NOT EXISTS
(
SELECT *
FROM dbo.Customer WITH (UPDLOCK, SERIALIZABLE)
WHERE Name = @Name
)
BEGIN
INSERT INTO dbo.Customer(CustomerId, Name) VALUES (@CustomerId, @Name)
SELECT @CustomerId
END
ELSE
BEGIN
SELECT CustomerId FROM dbo.Customer WHERE Name = @Name
END
Run Code Online (Sandbox Code Playgroud)
方法二:
BEGIN TRY
INSERT INTO dbo.Customer(CustomerId, Name) VALUES (@CustomerId, @Name)
SELECT @CustomerId
END TRY
BEGIN CATCH
SELECT CustomerId FROM dbo.Customer WHERE Name = @Name
END CATCH
Run Code Online (Sandbox Code Playgroud)
两种方式都是完全证明,并且会停止重复插入吗?
方法 2在并发下并不安全。当 catch 子句中的 select 运行时,无法保证导致插入失败的行将继续存在。
此外,catch 子句可以针对除重复键违规以外的错误执行,因为代码不检查错误号。
您还应该意识到注定交易失败的可能性。
Aaron Bertrand写了关于try/catch的开销。开销通常高于先检查。
什么是最佳编码实践明智的,并且表现更好?
方法 1 是一种常见的模式,但需要一个事务才能安全。性能取决于当地因素,因此您应该进行自己的测试。作为旁注,您可以通过使用 output 子句来避免一个查询:
DECLARE
@CustomerId uniqueidentifier = {guid '16D39773-9CC2-4CCF-A6A8-ACF1465030CC'},
@Name varchar(255) = 'name';
BEGIN TRANSACTION;
IF NOT EXISTS
(
SELECT *
FROM dbo.Customer WITH (UPDLOCK, SERIALIZABLE)
WHERE Name = @Name
)
BEGIN
INSERT dbo.Customer(CustomerId, [Name])
OUTPUT @CustomerId AS CustomerId
VALUES (@CustomerId, @Name);
END;
ELSE
BEGIN
SELECT CustomerId FROM dbo.Customer WHERE [Name] = @Name;
END;
COMMIT TRANSACTION;
Run Code Online (Sandbox Code Playgroud)
作为替代方案,您可能想要比较安全合并解决方案的性能:
DECLARE
@CustomerId uniqueidentifier = {guid '16D39773-9CC2-4CCF-A6A8-ACF1465030CC'},
@Name varchar(255) = 'name';
MERGE dbo.Customer WITH (SERIALIZABLE) AS C
USING (VALUES(@CustomerId, @Name)) AS I (CustomerId, [Name])
ON I.Name = C.Name
WHEN NOT MATCHED
THEN INSERT (CustomerId, [Name])
VALUES (I.CustomerId, I.[Name])
WHEN MATCHED THEN UPDATE
SET @CustomerId = C.CustomerId,
@Name = C.[Name]
OUTPUT @CustomerId AS CustomerId;
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1642 次 |
最近记录: |