sql-server – 选择/插入版本的Upsert:是否有高并发的设计模式?

前端之家收集整理的这篇文章主要介绍了sql-server – 选择/插入版本的Upsert:是否有高并发的设计模式?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我想做一个UPSERT的SELECT / INSERT版本.以下是现有代码的模板:
// CREATE TABLE Table (RowID INT NOT NULL IDENTITY(1,1),RowValue VARCHAR(50))

IF NOT EXISTS (SELECT * FROM Table WHERE RowValue = @VALUE)
BEGIN
   INSERT Table VALUES (@Value)
   SELECT @id = SCOPEIDENTITY()
END
ELSE
   SELECT @id = RowID FROM Table WHERE RowValue = @VALUE)

查询将从许多并发会话中调用.我的性能测试表明,它将一直在特定负载下抛出主键违规.

是否有一个高并发方法,这个查询将允许它保持性能,同时仍然避免插入已经存在的数据?

解决方法

您可以使用LOCKs来实现SERIALIZABLE,但这会降低并发性.为什么不先尝试共同的条件(“大多是插入或大部分选择”),然后安全处理“补救”行动?也就是说,“JFDI”模式…

预计大多数INSERT(球场70-80%):

只是尝试插入.如果失败,行已经创建.不需要担心并发,因为TRY / CATCH处理重复.

BEGIN TRY
   INSERT Table VALUES (@Value)
   SELECT @id = SCOPEIDENTITY()
END TRY
BEGIN CATCH
    IF ERROR_NUMBER() <> 2627
      RAISERROR etc
    ELSE -- only error was a dupe insert so must already have a row to select
      SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH

主要选择:

类似的,但是尝试先获取数据.没有数据=需要INSERT.再次,如果两个并发调用尝试插入,因为它们都发现该行缺少TRY / CATCH句柄.

BEGIN TRY
   SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
   IF @@ROWCOUNT = 0
   BEGIN
       INSERT Table VALUES (@Value)
       SELECT @id = SCOPEIDENTITY()
   END
END TRY
BEGIN CATCH
    IF ERROR_NUMBER() <> 2627
      RAISERROR etc
    ELSE
      SELECT @id = RowID FROM Table WHERE RowValue = @VALUE
END CATCH

第二个似乎重复,但它是高度并发的.锁将实现相同但以并发为代价…

编辑:

为什么不使用MERGE?

如果使用OUTPUT子句,它将只返回更新的内容.所以你需要一个虚拟UPDATE来为OUTPUT子句生成INSERTED表.如果您必须使用许多调用(由OP隐含)进行虚拟更新,那么这是很多日志写入才能够使用MERGE.

猜你在找的MsSQL相关文章