1. 程式人生 > >探討SQL Server併發處理存在就更新七種解決方案

探討SQL Server併發處理存在就更新七種解決方案

前言

本節我們來講講併發中最常見的情況存在即更新,在併發中若未存在行記錄則插入,此時未處理好極容易出現插入重複鍵情況,本文我們來介紹對併發中存在就更新行記錄的七種方案並且我們來綜合分析最合適的解決方案。

探討存在就更新七種方案

首先我們來建立測試表

複製程式碼
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 [
            
           

相關推薦

探討SQL Server併發處理存在更新解決方案

前言本節我們來講講併發中最常見的情況存在即更新,在併發中若未存在行記錄則插入,此時未處理好極容易出現插入重複鍵情況,本文我們來介紹對併發中存在就更新行記錄的七種方案並且我們來綜合分析最合適的解決方案。探討存在就更新七種方案首先我們來建立測試表IF OBJECT_ID('Tes

sql server批量插入與更新解決方案

對於sql 來說操作集合型別(一行一行)是比較麻煩的一件事,而一般業務邏輯複雜的系統或專案都會涉及到集合遍歷的問題,通常一些人就想到用遊標,這裡我列出了兩種方案,供大家參考 1.遊標方式 1 DECLARE @Data NVARCHAR(max) 2 SET @Da

SQL Server 效能優化實戰系列(一) SQL Server擴充套件函式的基本概念 使用SQL Server 擴充套件函式進行效能優化 SQL Server Url正則表示式 記憶體常駐 完美解決方案

資料庫伺服器主要用於儲存、查詢、檢索企業內部的資訊,因此需要搭配專用的資料庫系統,對伺服器的相容性、可靠性和穩定性等方面都有很高的要求。        下面是進行籠統的技術點說明,為的是讓大家有一個整體的概念,如果想深入可以逐個擊破;&n

SQL server安裝及連線時出錯分析及解決方案……

    在安裝資料庫時提示:“你的系統中沒有要更新的Sql Server軟體……”點選下一步,此時會出現滾動條回滾的現象。這是因為你之前安裝過資料庫,但又將它下載了。(自以為下載的很乾淨。如果你這樣想那就太低估微軟的實力了。不知你是否注意到當你在剛開始安裝的時候,雖然貌似可以更改Sql 的安裝路徑,比如說你安

SQL Server中用While迴圈替代遊標(Cursor)的解決方案

By行處理資料,推薦2種方式: 1、遊標 2、While迴圈 我們來了解下這兩種方案處理1w行資料分別需要多長時間。 一、遊標。 首先我們填充一個表,用優雅的遞迴方式填充。 create table Orders(OrderID int,CostValue

SQL Server 資料庫沒有有效所有者的三解決辦法

問題: 開發的過程中,作業系統出了問題,決定重灌系統。但是沒有將SQL Server中的資料庫檔案分離出來,直接將系統格了。在新系統資料庫中附加了資料庫檔案,一切還算正常,但當開啟資料庫關係圖的時候

Sql Server 中查詢儲過程的修改時間

lai 名稱 lec code str name class 指定 number 1、按最近修改排序所有存儲過程 SELECT [name], [create_date], [modify_date] FROM [sys].[objects] WHERE [type

SQL Server 2008內及I/O性能監控

構建 sas ati 虛擬內存 it168 convert 最佳實踐 如果 res 來源: it168 發布時間: 2011-04-12 11:04 閱讀: 10820 次 推薦: 1 原文鏈接 [收藏]   以下均是針對Window 32位系統環

SQL server觸發器、儲過程操作遠程數據庫插入數據,解決服務器已存在的問題

定義 ims val rom 記錄 插入記錄 其它 pre 項目 近期弄了一個小項目,也不是非常復雜,須要將一個數據庫的一些數據備份到另外一個庫。不是本地,可能是網絡上其它的數據庫。想了一下,用了存儲過程和觸發器。也不是非常復雜,首先我須要操作遠程數據庫,於是寫了一個存

SQL Server 查詢處理中的各個階段(SQL執行順序)

派生 步驟 sel 合成 emp lec 限制 對象 沒有 SQL 不同於與其他編程語言的最明顯特征是處理代碼的順序。在大數編程語言中,代碼按編碼順序被處理,但是在SQL語言中,第一個被處理的子句是FROM子句,盡管SELECT語句第一個出現,但是幾乎總是最後被處

sql server分頁儲過程

order span exe 存儲 數據 reat 過大 where 排序類 因項目需要,一次性查詢出來的數據過大,內存hold不住,所以特意寫成分頁查詢,減小占用內存。 存儲過程如下: USE [XXX] GO SET ANSI_NULLS ON GO SET QU

Performance Monitor3:監控SQL Server的內壓力

gre server 異常 基本 cati -c eba 技術 buffer SQL Server 使用的資源受到操作系統的調度,同時,SQL Server在內部實現了一套調度算法,用於管理從操作系統獲取的資源,主要是對內存和CPU資源的調度。一個好的數據庫系統,必定在內存

SQL Server基礎之儲過程

調用 mic 情況 sele 環境 record total true 孔子 SQL Server基礎之存儲過程 閱讀目錄 一:存儲過程概述 二:存儲過程分類 三:創建存儲過程 1.創建無參存儲過程 2.修改存儲過程 3.刪除存儲過程 4.重命名存儲過程 5.創

SQL Server Update:使用 TOP 限制更新的數據

小時 新的 img providers host param tool 匈牙利 inpu 原文 使用 TOP 限制更新的數據 可以使用 TOP 子句來限制 UPDATE 語句中修改的行數。當 TOP (n) 子句與 UPDATE 一起使用時,將針對隨機選擇的 n 行執行刪

遍歷SQL SERVER中所有儲過程和觸發器

server text 查找 所有 and from obj where serve 如果需要查找某個存儲過程或觸發器中是否含有某段文本(比如:你想知道有哪些存儲過程操作了某個表) 可以這麽寫 select name from sysobjects o, syscomm

SQL Server 異常處理機制(Begin try Begin Catch) 摘錄

RoCE nsa lan seve -- isa weight roc 錯誤信息 begin try --SQL end try begin catch --sql (處理出錯動作) end catch 我們將可能會出錯的sql 寫在begin try..

SQL Server創建儲過程——動態SQL

動態sql 利用 parameter 技術 多個 create 類型 結果 com 簡介: 存儲過程(stored procedure)是一組為了完成特定功能的SQL語句集合,經編譯後存儲在服務器端的數據庫中,利用存儲過程可以加速SQL語句的執行。 自定義存儲過程,由用戶創

sql server 操作(不定期更新

要求:基本的語法要清楚。 sql server疑難點:  1、Partition by可以理解為 對多行資料分組後排序取每個產品的第一行資料 先處理內查詢,由內向外處理,外層查詢利用內層查詢的結果巢狀查詢不僅僅可以用於父查詢select語句使用。還可以用於insert、update、delet

Sql Server併發和事務

鎖的作用範圍通常在事務中,事務是建立在併發模式下。 從SQL Server 2005開始,加入了一種新的併發模式-----樂觀併發。不管使用哪種併發模式,如果多個會話同時修改相同的資料,都會產生資源爭用,然後引發一系列的問題。 1.存在的讀現象:包括髒讀、不可重複讀和幻讀。 2.丟失更新:一個會話的修改

SQL Server如何處理檔案的檔案組

在今天的文章裡,我想談下SQL Server裡非常重要的話題:SQL Server如何處理檔案的檔案組。當你用CREATE DATABASE命令建立一個簡單的資料庫時,SQL Server為你建立2個檔案: 一個數據檔案(.mdf) 一個事務日誌檔案(.ldf) 資料檔案本身