這篇文章主要和大家一起探討了SQL Server並發處理存在就更新的7種解決方案,具有一定的參考價值,有興趣的小伙伴們可以參考一下
前言
本節我們來講講並發中最常見的情況存在即更新,在並發中若未存在行記錄則插入,此時未處理好極容易出現插入重複鍵情況,本文我們來介紹對並發中存在就更新行記錄的七種方案並且我們來綜合分析最合適的解決方案。
探討存在就更新七種方案
首先我們來建立測試表
IF OBJECT_ID('Test') IS NOT NULL DROP TABLE Test CREATE TABLE Test ( Id int, Name nchar(100), [Counter] int,primary key (Id), unique (Name) ); GO
解決方案一 (開啟事務)
我們統一建立預存程序透過來SQLQueryStress來測試並發狀況,我們來看第一種情況。
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) BEGIN TRANSACTION IF EXISTS ( SELECT 1 FROM Test WHERE Id = @Id ) UPDATE Test SET [Counter] = [Counter] + 1 WHERE Id = @Id; ELSE INSERT Test ( Id, Name, [Counter] ) VALUES ( @Id, @Name, 1 ); COMMIT GO
#同時開啟100個執行緒與200個執行緒出現插入重複鍵的幾率比較少還是存在。
解決方案二(降低隔離等級為最低隔離等級UNCOMMITED)
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED BEGIN TRANSACTION IF EXISTS ( SELECT 1 FROM Test WHERE Id = @Id ) UPDATE Test SET [Counter] = [Counter] + 1 WHERE Id = @Id; ELSE INSERT Test ( Id, Name, [Counter] ) VALUES ( @Id, @name, 1 ); COMMIT GO
此時問題依舊和解決方案一無異(如果降低級別為最低隔離級別,如果行記錄為空,前一事務如果未進行提交,當前事務也能讀取到該行記錄為空,如果當前事務插入進去並進行提交,此時前一事務再進行提交此時就會出現插入重複鍵問題)
解決方案三(提升隔離等級為最高等級SERIALIZABLE)
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) SET TRANSACTION ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION IF EXISTS ( SELECT 1 FROM dbo.Test WHERE Id = @Id ) UPDATE dbo.Test SET [Counter] = [Counter] + 1 WHERE Id = @Id; ELSE INSERT dbo.Test ( Id, Name, [Counter] ) VALUES ( @Id, @Name, 1 ); COMMIT GO
在這種情況下更加糟糕,直接到會導致死鎖
#此時將隔離等級提升為最高隔離等級會解決插入重複鍵問題,但是對於更新來獲取排它鎖而未提交,而此時另外一個進程進行查詢獲取共享鎖此時將造成進程間相互阻塞從而造成死鎖,所以從此知最高隔離等級有時候能夠解決並發問題但是也會帶來死鎖問題。
解決方案四(提升隔離等級+良好的鎖定)
此時我們再來在新增最高隔離等級的基礎上增添更新鎖,如下:
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) SET TRANSACTION ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION IF EXISTS ( SELECT 1 FROM dbo.Test WITH(UPDLOCK) WHERE Id = @Id ) UPDATE dbo.Test SET [Counter] = [Counter] + 1 WHERE Id = @Id; ELSE INSERT dbo.Test ( Id, Name, [Counter] ) VALUES ( @Id, @Name, 1 ); COMMIT GO
#運行多次均未發現出現什麼異常,透過查詢資料時使用更新鎖而非共享鎖,這樣的話一來可以讀取資料但不阻塞其他事務,二來還確保自上次讀取資料後資料未被更改,這樣就解決了死鎖問題。似乎這樣的方案是可行得,如果是高並發不知是否可行。
解決方案五(提升隔離等級為行版本控制SNAPSHOT)
ALTER DATABASE UpsertTestDatabase SET ALLOW_SNAPSHOT_ISOLATION ON ALTER DATABASE UpsertTestDatabase SET READ_COMMITTED_SNAPSHOT ON GO IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) BEGIN TRANSACTION IF EXISTS ( SELECT 1 FROM dbo.Test WHERE Id = @Id ) UPDATE dbo.Test SET [Counter] = [Counter] + 1 WHERE Id = @Id; ELSE INSERT dbo.Test ( Id, Name, [Counter] ) VALUES ( @Id, @Name, 1 ); COMMIT GO
上述解決方案也會出現插入重複鍵問題不可取。
解決方案六(提升隔離等級+表格變數)
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) DECLARE @updated TABLE ( i INT ); SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION UPDATE Test SET [Counter] = [Counter] + 1 OUTPUT DELETED.Id INTO @updated WHERE Id = @Id; IF NOT EXISTS ( SELECT i FROM @updated ) INSERT INTO Test ( Id, Name, counter ) VALUES ( @Id, @Name, 1 ); COMMIT GO
經過多次認證也是零錯誤,似乎透過表格變數形式實現可行。
解決方案七(提升隔離等級+Merge)
透過Merge關鍵來實現存在即更新否則則插入,同時我們應該注意設定隔離等級為SERIALIZABLE否則會出現插入重複鍵問題,程式碼如下:
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) SET TRAN ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION MERGE Test AS [target] USING ( SELECT @Id AS Id ) AS source ON source.Id = [target].Id WHEN MATCHED THEN UPDATE SET [Counter] = [target].[Counter] + 1 WHEN NOT MATCHED THEN INSERT ( Id, Name, [Counter] ) VALUES ( @Id, @Name, 1 ); COMMIT GO
多次認證無論是並發100個執行緒或並發200個執行緒依然沒有例外資訊。
總結
本節我們詳細討論了在並發中如何處理存在即更新,否則即插入問題的解決方案,目前來講以上三種方案可行。
解決方案一(最高隔離等級+ 更新鎖定)
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) BEGIN TRANSACTION; UPDATE dbo.Test WITH ( UPDLOCK, HOLDLOCK ) SET [Counter] = [Counter] + 1 WHERE Id = @Id; IF ( @@ROWCOUNT = 0 ) BEGIN INSERT dbo.Test ( Id, Name, [Counter] ) VALUES ( @Id, @Name, 1 ); END COMMIT GO
# 暫時只能想到這三種解決方案,個人比較推薦方案一和方案三, 請問問您有何高見,請留下您的評論若可行,我將進行後續補充。
解決方案二(最高隔離等級 + 表格變數)
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) DECLARE @updated TABLE ( i INT ); SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; BEGIN TRANSACTION UPDATE Test SET [Counter] = [Counter] + 1 OUTPUT DELETED.Id INTO @updated WHERE Id = @Id; IF NOT EXISTS ( SELECT i FROM @updated ) INSERT INTO Test ( Id, Name, counter ) VALUES ( @Id, @Name, 1 ); COMMIT GO
解决方案三(最高隔离级别 + Merge)
IF OBJECT_ID('TestPro') IS NOT NULL DROP PROCEDURE TestPro; GO CREATE PROCEDURE TestPro ( @Id INT ) AS DECLARE @Name NCHAR(100) = CAST(@Id AS NCHAR(100)) SET TRAN ISOLATION LEVEL SERIALIZABLE BEGIN TRANSACTION MERGE Test AS [target] USING ( SELECT @Id AS Id ) AS source ON source.Id = [target].Id WHEN MATCHED THEN UPDATE SET [Counter] = [target].[Counter] + 1 WHEN NOT MATCHED THEN INSERT ( Id, Name, [Counter] ) VALUES ( @Id, @Name, 1 ); COMMIT GO
暂时只能想到这三种解决方案,个人比较推荐方案一和方案三, 请问您有何高见,请留下您的评论若可行,我将进行后续补充。
以上是SQL Server並發處理存在就更新解決方案探討_MsSql的詳細內容。更多資訊請關注PHP中文網其他相關文章!

win7补丁包(UpdatePack7)是俄罗斯大神制作的一款Win7系统补丁自动安装精灵,它支持Win764位和32位,集成了Win7发布至今所有补丁,还包含了NVME协议补丁,USB3.0补丁等等。win7补丁包(UpdatePack7)【64位+32位】下载UpdatePack7参数介绍/NVMe(集成NVMe驱动)/S(静默安装,不更改IE版本,不重启)/Silent(自动安装,界面显示安装进度)/Temp(指定释放路径到临时文件夹)/IE11(更新安装InternetExplorer1

WindowsServerBackup是WindowsServer操作系统自带的一个功能,旨在帮助用户保护重要数据和系统配置,并为中小型和企业级企业提供完整的备份和恢复解决方案。只有运行Server2022及更高版本的用户才能使用这一功能。在本文中,我们将介绍如何安装、卸载或重置WindowsServerBackup。如何重置Windows服务器备份如果您的服务器备份遇到问题,备份所需时间过长,或无法访问已存储的文件,那么您可以考虑重新设置WindowsServer备份设置。要重置Windows

win10电脑老是提醒更新重启怎么办?win10的更新问题一直是大家比较头疼的,无论是更新前还是更新后,系统老是提醒更新重启,十分烦人。其实我们只要将对应服务关闭就可以了,下面就一起来看看具体方法吧。win10电脑老是提醒更新重启解决办法一、更新前提示1、首先我们在开始菜单中打开设置。2、选择更新和安全。3、再点击高级选项。4、将更新通知关闭即可。二、更新后提醒1、其实我们在完成更新之后,系统也有可能会老是提醒我们重启。2、这时候我们需要先右键计算机,选择理3、在系统工具中找到图所示。4、然后我

CakePHP是一个流行的PHP框架,它提供了方便的ORM(对象关系映射)功能,使得查询和更新数据库变得非常容易。本文将介绍如何在CakePHP中进行数据查询和更新。我们将从简单的查询和更新开始,逐步深入,了解如何使用条件和关联的模型来更复杂地查询和更新数据。基本查询首先,让我们看看如何进行最简单的查询。假设我们有一个名为“Users”的数据表,并且我们想要

Vue是一个流行的JavaScript框架,它通过使用组件化开发模式,使得我们可以轻松地构建可重用的交互式用户界面。但是某些情况下,我们需要手动更新组件而不是等待数据驱动更新,这时候可以使用Vue提供的$forceUpdate方法。在这篇文章中,我们将详细讨论Vue中如何使用$forceUpdate方法强制更新组件。Vue组件的渲染是由Vue的响应式系统驱动

Win11bios怎么更新?更新BIOS可以支持最新的硬件,也可以对以往的一些硬件进行优化。近期有部分Win11用户想要更新BIOS,但是不太清楚应该如何操作,对于这一情况,下面小编为大家带来了详细的Win11更新bios的方法,我们一起来看看吧。 Win11更新bios的方法 在进行更新之前,您需要执行一些任务。首先,您需要检查您的BIOS版本并记下它。然后,您可以继续为您的特定系统下载正确的BIOS。 1、检查您的BIOS版本 同时按下Windows和R键。 键入msinfo3

在发布WindowsServer的build26040版本之际,微软公布了该产品的官方名称:WindowsServer2025。一同推出的,还有Windows11WindowsInsiderCanaryChannel版本的build26040。有些朋友可能还记得,多年前有人成功将WindowsNT从工作站模式转换为服务器模式,显示微软操作系统各版本之间的共性。尽管现在微软的服务器操作系统版本和Windows11之间有明显区别,但关注细节的人可能会好奇:为什么WindowsServer更新了品牌,

微软6月24号正式公布了win11系统,可以看到用户界面、开始菜单等和Windows10X中发现的非常相似。有的朋友在使用预览版的时候发现用的不习惯,想要改win10系统开使用,那么我们要如何操作呢,下面我们就来看看win11改win10系统教程,一起来学习一下吧。1、第一步是从Windows11打开新设置。在这里,您需要转到图像中显示的系统设置。2、在系统设置下,选择“恢复”选项。在这里,您将能够看到“以前版本的窗口”选项。您还可以在它旁边看到一个“返回”按钮,单击此按钮。3、您可以指定要返回


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

Dreamweaver Mac版
視覺化網頁開發工具

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

記事本++7.3.1
好用且免費的程式碼編輯器