六月婷婷综合激情-六月婷婷综合-六月婷婷在线观看-六月婷婷在线-亚洲黄色在线网站-亚洲黄色在线观看网站

明輝手游網(wǎng)中心:是一個免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

SQL Server并發(fā)處理存在就更新處理方案探討_MsSql

[摘要]這篇文章主要和大家一起探討了SQL Server并發(fā)處理存在就更新的7種解決方案,具有一定的參考價值,感興趣的小伙伴們可以參考一下前言本節(jié)我們來講講并發(fā)中最常見的情況存在即更新,在并發(fā)中若未存在行記錄則插入,此時未處理好極容易出現(xiàn)插入重復(fù)鍵情況,本文我們來介紹對并發(fā)中存在就更新行記錄的七種方案并且...
這篇文章主要和大家一起探討了SQL Server并發(fā)處理存在就更新的7種解決方案,具有一定的參考價值,感興趣的小伙伴們可以參考一下

前言

本節(jié)我們來講講并發(fā)中最常見的情況存在即更新,在并發(fā)中若未存在行記錄則插入,此時未處理好極容易出現(xiàn)插入重復(fù)鍵情況,本文我們來介紹對并發(fā)中存在就更新行記錄的七種方案并且我們來綜合分析最合適的解決方案。

探討存在就更新七種方案

首先我們來創(chuàng)建測試表

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

解決方案一(開啟事務(wù))

我們統(tǒng)一創(chuàng)建存儲過程通過來SQLQueryStress來測試并發(fā)情況,我們來看第一種情況。

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


SQL Server并發(fā)處理存在就更新解決方案探討_MsSql

SQL Server并發(fā)處理存在就更新解決方案探討_MsSql

同時開啟100個線程和200個線程出現(xiàn)插入重復(fù)鍵的幾率比較少還是存在。

解決方案二(降低隔離級別為最低隔離級別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

此時問題依舊和解決方案一無異(如果降低級別為最低隔離級別,如果行記錄為空,前一事務(wù)如果未進(jìn)行提交,當(dāng)前事務(wù)也能讀取到該行記錄為空,如果當(dāng)前事務(wù)插入進(jìn)去并進(jìn)行提交,此時前一事務(wù)再進(jìn)行提交此時就會出現(xiàn)插入重復(fù)鍵問題)

SQL Server并發(fā)處理存在就更新解決方案探討_MsSql

解決方案三(提升隔離級別為最高級別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

在這種情況下更加糟糕,直接到會導(dǎo)致死鎖

SQL Server并發(fā)處理存在就更新解決方案探討_MsSql

此時將隔離級別提升為最高隔離級別會解決插入重復(fù)鍵問題,但是對于更新來獲取排它鎖而未提交,而此時另外一個進(jìn)程進(jìn)行查詢獲取共享鎖此時將造成進(jìn)程間相互阻塞從而造成死鎖,所以從此知最高隔離級別有時候能夠解決并發(fā)問題但是也會帶來死鎖問題。

解決方案四(提升隔離級別+良好的鎖)

此時我們再來在添加最高隔離級別的基礎(chǔ)上增添更新鎖,如下:

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


SQL Server并發(fā)處理存在就更新解決方案探討_MsSql

運(yùn)行多次均未發(fā)現(xiàn)出現(xiàn)什么異常,通過查詢數(shù)據(jù)時使用更新鎖而非共享鎖,這樣的話一來可以讀取數(shù)據(jù)但不阻塞其他事務(wù),二來還確保自上次讀取數(shù)據(jù)后數(shù)據(jù)未被更改,這樣就解決了死鎖問題。貌似這樣的方案是可行得,如果是高并發(fā)不知是否可行。

解決方案五(提升隔離級別為行版本控制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

上述解決方案也會出現(xiàn)插入重復(fù)鍵問題不可取。

解決方案六(提升隔離級別+表變量)

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


SQL Server并發(fā)處理存在就更新解決方案探討_MsSql

SQL Server并發(fā)處理存在就更新解決方案探討_MsSql

經(jīng)過多次認(rèn)證也是零錯誤,貌似通過表變量形式實(shí)現(xiàn)可行。

解決方案七(提升隔離級別+Merge)

通過Merge關(guān)鍵來實(shí)現(xiàn)存在即更新否則則插入,同時我們應(yīng)該注意設(shè)置隔離級別為SERIALIZABLE否則會出現(xiàn)插入重復(fù)鍵問題,代碼如下:

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

多次認(rèn)證無論是并發(fā)100個線程還是并發(fā)200個線程依然沒有異常信息。

總結(jié)

本節(jié)我們詳細(xì)討論了在并發(fā)中如何處理存在即更新,否則即插入問題的解決方案,目前來講以上三種方案可行。

解決方案一(最高隔離級別 + 更新鎖)

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

暫時只能想到這三種解決方案,個人比較推薦方案一和方案三, 請問您有何高見,請留下您的評論若可行,我將進(jìn)行后續(xù)補(bǔ)充。

解決方案二(最高隔離級別 + 表變量)

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

暫時只能想到這三種解決方案,個人比較推薦方案一和方案三, 請問您有何高見,請留下您的評論若可行,我將進(jìn)行后續(xù)補(bǔ)充。

以上就是SQL Server并發(fā)處理存在就更新解決方案探討_MsSql的詳細(xì)內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!


學(xué)習(xí)教程快速掌握從入門到精通的SQL知識。




主站蜘蛛池模板: 亚洲国产成人精品久久 | 欧美又大又粗又长又美 | 视频二区日韩 | 污污视频在线免费观看 | 欧美性色黄大片四虎影视 | 天天操婷婷 | 亚洲福利影院 | 午夜资源在线 | 视频二区在线观看 | 小说区 亚洲 自拍另类 | 欧美一级在线视频 | 日韩精品第一区 | 日韩高清一级毛片 | 亚洲草逼| 午夜亚洲视频 | 欧美视频高清 | 亚洲精品乱码久久久久久麻豆 | 伊人动漫 在线播放 | 一区二区三区视频在线 | 日韩第一页在线 | 日韩在线欧美 | 香蕉久久夜色精品国产小优 | 五月婷婷爱 | 亚洲国产精品成人综合色在线婷婷 | 四虎影院入口 | 欧日韩不卡在线视频 | 香蕉欧美成人精品a∨在线观看 | 天堂在线中文网 | 日日碰夜夜操 | 雪白丰满丝袜长腿白嫩在线 | 亚洲欧美日韩精品永久在线 | 日韩丝袜亚洲国产欧美一区 | 欧美线在线精品观看视频 | 午夜精品久久久久久久四虎 | 欧美一区二区影院 | 亚洲mv在线观看 | 亚洲二区视频 | 欧美一级高清视频在线播放 | 欲香欲色 | 一级做a爱片特黄在线观看 一级做a爱片久久蜜桃 | 亚洲欧美中文日韩在线v日本 |