加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MsSql教程 > 正文

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

发布时间:2020-12-12 07:49:20 所属栏目:MsSql教程 来源:网络整理
导读:我想做一个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 @
我想做一个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.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读