MSSQL-併發控制-1-Transaction


 
   MSSQL併發控制原先打算分爲兩個部分寫:隔離級別及鎖,寫的過程當中,發現須要說起下事務的相關內容,故加多一篇博文,共3篇。
 


 
    若是轉載,請註明博文來源:  www.cnblogs.com/xinysu/   ,版權歸 博客園 蘇家小蘿蔔 全部。望各位支持!
 


   

   併發控制,在於控制每個事務的操做過程以及它們對資源的佔用狀況,同時要保證事務的ACID特性。這裏簡單描述事務類別、ACID特性及對分佈式事務的簡要說明。
 

1 事務類別

    從提交方式:自動提交事務、手動提交事務
    從開啓方式:顯式事務、隱式事務
    其餘:批範圍事務、分佈式事務

1.1 顯式事務

    經過API函數或者發佈T-SQL begin transaction、commit transaction、commit work、rollback transaction、rollback work 、save transaction等明肯定義事務的開始和結束。
 
    這裏簡要說明commit、 save transaction 、rollback 、 xact_abort。

1.1.1 COMMIT

    commit,提交最近一次未提交事務,這裏注意,commit  transaction = commit work = commit tran [name]。每次commit,都須要把當前的@@trancount減去1。
   
 1 begin tran yu1
 2 select @@TRANCOUNT
 3        begin tran yu2
 4        insert into tbxin(name,age) select '第2層tran',100;
 5        select @@TRANCOUNT
 6              
 7                   begin tran yu3
 8                      insert into tbxin(name,age) select '第3層tran',100;
 9                      select @@TRANCOUNT
10                      commit tran --等同於 commit tran anyname
11                      select @@TRANCOUNT
12                      commit tran --等同於 commit tran anyname
13                      select @@TRANCOUNT
14                      commit tran --等同於 commit tran anyname
15                      select @@TRANCOUNT

1.1.2 ROLLBACK

    rollback,回滾事務,有2中語法:
  • 第一個,回滾當前全部未結束事務
    • rollback = rollback tran = rollback transaction = rollback work
    • 不管嵌套了多少事務,@@trancount爲多少,執行 rollback則直接回滾全部嵌套事務,設置@@trancount爲0
    • 常見錯誤案例:EXECUTE 後的事務計數指示 BEGIN 和 COMMIT 語句的數目不匹配。上一計數 = 1,當前計數 = 0。
  • 第二個,回滾到某個保存點的位置
    • rollback tran savepoint_name
    • 不影響@@trancount計算,回滾到 某個 save tran savepoint_name的位置
 錯誤案例:
 
 1 CREATE PROC p_count
 2 AS
 3 begin transaction
 4 insert into tbxin(name,age) select '第一層tran',200;
 5 rollback transaction
 6 GO
 7 
 8 BEGIN TRAN
 9 EXEC p_count
10 select @@TRANCOUNT
11 
12 消息 266,級別 16,狀態 2,過程 p_count,第 0 行 13 EXECUTE 後的事務計數指示 BEGIN 和 COMMIT 語句的數目不匹配。上一計數 = 1,當前計數 = 0。

1.1.3 SAVE TRANSACTION

   save transaction,定義在按條件取消某個事務的一部分後,該事務能夠返回的一個位置。 若是將事務回滾到保存點,則根據須要必須完成其餘剩餘的 Transact-SQL 語句和 COMMIT TRANSACTION 語句,或者必須經過將事務回滾到起始點徹底取消事務。 若要取消整個事務,使用窗體 ROLLBACK TRANSACTION transaction_name。 這將撤消事務的全部語句和過程。
  • save transaction [savepoint_name] 提供 用戶 在事務內設置保存點或標記,因此 save transaction 只能在事務內部執行;
  • save transaction [savepoint_nmae] 不影響 @@trancount 計數;
  • 注意 save transaction [savepoint_name] 只有對應 rollback transaction [savepoint_name],並無對應 commit transaction [savepoint_name],強調下:對應的rollback transaction [savepoint_name] 是有帶 保存點名字的,若是沒有帶名字,則會回滾整個最外部的事務;
  • save transaction 對應的 rollback transaction [savepoint_name]並不須要一一對應,能夠多個save tran 對應0到多個rollback tran
  • 事務內,能夠有多個 save transaction [savepoint_name],可以使用 rollback transaction [savepoint_name] 回滾到任意一個保存點
  • 支持savepoint_name重複命名,可是不建議。在事務中容許有重複的保存點名稱,但指定保存點名稱的 ROLLBACK TRANSACTION savepoint_name 語句只將事務回滾到使用該名稱的最近的 SAVE TRANSACTION savepoint_name;
  • 在使用 BEGIN DISTRIBUTED TRANSACTION 顯式啓動或從本地事務升級的分佈式事務中,不支持 SAVE TRANSACTION。
 
 1 begin tran
 2 select @@TRANCOUNT
 3 
 4        save tran yu1
 5        insert into tbxin(name,age) select '第1層save',100;
 6        select @@TRANCOUNT
 7              
 8                   save tran yu2
 9                      insert into tbxin(name,age) select '第2層save',100;
10                      select @@TRANCOUNT
11                          
12                             save tran yu3
13                           insert into tbxin(name,age) select '第3層save',100;
14                           select @@TRANCOUNT
15 
16                                  save tran yu4
17                                insert into tbxin(name,age) select '第4層save',100;
18                                select @@TRANCOUNT
19 
20                             rollback tran yu3
21                             select @@TRANCOUNT
22 
23               commit tran yu2
24         select @@TRANCOUNT
25 
26 rollback tran

1.1.4 XACT_ABORT

    xact_abort用於設置環境屬性,默認爲關閉狀態。在關閉的狀態下,嵌套事務中,若某個嵌套事務異常,不影響整個事務的進行,需手動寫明錯誤後的處理方式(commit or rollback);啓動狀態下,當某個嵌套事務異常,回滾整個嵌套事務。
 
 

1.2 隱式事務

    爲鏈接將隱性事務模式設置爲打開以後,當數據庫引擎實例首次執行下列任何語句時,都會自動啓動一個事務:當鏈接以隱式事務模式進行操做時,數據庫引擎實例將在提交或回滾當前事務後自動啓動新事務。無須描述事務的開始,只需提交或回滾每一個事務。隱性事務模式生成連續的事務鏈。經過 API 函數或 Transact-SQL SET IMPLICIT_TRANSACTIONS ON 語句,將隱性事務模式設置爲打開。
  • ALTER TABLE
  • CREATE
  • DROP
  • OPEN
  • FETCH
  • GRANT
  • REVOKE
  • SELECT
  • UPDATE
  • DELETE
  • INSERT
  • TRUNCATE TABLE 

1.3 自動提交事務

--例子
INSERT INTO ...
--數據庫默認的事務管理模式,在沒有被顯式事務及隱式事務覆蓋的狀況下,自動在每一個Tsql完成時,提交或者回滾

1.4 手動提交事務

 
--例子
BEGIN TRAN
 
INSERT INTO ...
 
COMMIT / ROLLBACK
 
--數據庫默認的事務管理模式,顯式事務在完成時,手動指定SQL,說明提交或者回滾。

1.5 批範圍事務

   只能應用於多個活動結果集 (MARS),在 MARS 會話中啓動的 Transact-SQL 顯式或隱式事務變爲批處理級事務。當批處理完成時沒有提交或回滾的批處理級事務自動由 SQL Server 進行回滾。
 

1.6 分佈式事務

    分佈式事務跨越兩個或多個稱爲資源管理器的服務器,有同構分佈式及異構分佈式。在MSSQL中,能夠經過  BEGIN DISTRIBUTED TRANSACTION 命令開啓分佈式事務。
 
    這裏有一點須要注意一下:當事務內部操做跨越了多個服務器,可是並無使用 BEGIN DISTRIBUTED TRANSACTION 命令開頭的事務,也會自動轉化爲分佈式事務!
 
    假設A服務器上的數據庫 Orders 用於記錄訂單,B服務器上的 Stock 用於記錄庫存(不考慮程序創建兩個DB鏈接,按照僅創建一個 DB連接到 A 上)。當 Stock有庫存,並減去一個庫存後,Orders 正常能夠下一個訂單,則這個下單事務內,操做了兩個服務器,屬於分佈式事務,簡要操做以下:
 
BEGIN DISTRIBUTED TRANSACTION
SELECT ... FROM Stock... WHERE ...
UPDATE Stock ...
INSERT INTO ORDERS ...
COMMIT

 

    在SQL SERVER中,若是沒有配置服務器的DTC服務,使用分佈式事務的時候,會報錯以下:html

(1 行受影響)
消息 8501,級別 16,狀態 3,第 4 行 服務器 'XINYSU\MSSQL' 上的 MSDTC 不可用。
 
(1 行受影響)
連接服務器"test_xinysu"的 OLE DB 訪問接口 "SQLNCLI11" 返回了消息 "該夥伴事務管理器已經禁止了它對遠程/網絡事務的支持。"。 消息 7391,級別 16,狀態 2,第 4 行 沒法執行該操做,由於連接服務器 "test_xinysu" 的 OLE DB 訪問接口 "SQLNCLI11" 沒法啓動分佈式事務。

 

    若是實例須要支持分佈式事務,則須要在雙方的服務器其上開啓DTC服務,運行XA事務。這裏注意一點,若是連接服務器是MySQL數據庫,由於mysql的odbc不支持 XA事務,因此,會報錯 沒法啓動分佈式事務。官網解釋以下:mysql

MySQL OLE DB driver does not support MSDTC because it doesn’t support auto-enlistment in the ambient COM+ transaction. If you really want to, you can write your own XA.DLL to wrap MySQL OLEDB driver in an XA transaction.
 
windows啓動DTC服務 ,配置以下:
 
"管理工具" -> "組件服務" ,按照下圖操做,打開本地的DTC配置,啓動網絡DTC,啓動XA事務,以下圖。
 
      配置結束後點擊應用,則會提示MSDTC服務會被重啓,同時,依賴DSDTC的應用程序可能須要重啓才能使用新的配置,這個時候就須要重啓 SQL SERVER的服務了。
    
 
      若是是正在跑的數據庫,建議先從庫配置,切換主從,主庫配置的順序來啓用。最好是在搭建服務器一開始,就瞭解程序層面是否會使用到這個功能。正常狀況下,都是在程序中配置多個DB鏈接,由程序來控制分佈式事務。
 
     這裏多說一個平常須要注意的事項,在使用連接服務器對另一個服務器上的DB作增刪改操做的時候,都是一行一行傳遞過去作操做的。好比:
 
1 INSERT INTO mssql_xinysu.dbname.dbo.tbid(id) select id from sys.sysobjects
2 或者
3 INSERT INTO openquery([mysql_xinysu],'select id from tbid') select id from sys.sysobjects

    假設 select id from sys.sysobjects 的結果有2k行,則在 第一個SQL的 ODBC 是這麼處理的:sql

  • 分爲2000個INSERT 語句 
  • (@Param000004 int)INSERT [dbname].[dbo].[tbid]([id]) VALUES(@Param000004)
  • 一條一條INSERT
    第二個SQL的ODBC是這麼處理的:
  • 分爲2000個INSERT 語句 
  • INSERT INTO tbid ([id]) VALUES(單個的ID值)
  • 一條條INSERT
    經過這個操做說明,可想而知,鏈接服務器操做是很是慢的過程,不建議在有性能要求的業務上進行這樣的使用操做。

2 ACID特性

2.1 Atomicity

    簡稱爲A,事務的原子性。要求 在同個事務中,單個SQL或者多個SQL對數據進行的修改操做,要麼一塊兒執行提交,要麼所有回滾不提交。存儲引擎中,經過undo log 來實現事務的原子性。
    例子:
    

2.2 Consistency

    簡稱爲 C,事務的一致性。事務在完成時,必須使全部的數據都保持一致狀態 。在相關數據庫中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。事務結束時,全部的內部數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。 
 
    一致性,能夠分爲兩個層次來講明。一個是存儲引擎層,一個是業務層。
     在存儲引擎層,當某個表格發現了數據修改,那麼修改的行數據應該符合表格的約束條件、外鍵條件,涉及到索引頁數據也要作相應修改,保證數據修改後的完整性。
     在業務層,事務的一致性更多的在於程序設計,好比在一個事務內,帳號A給帳號B轉帳100元,那麼這個事務結束後,A的帳號須要少100元,B的帳號須要增長100元。    

2.3 Isolation

    簡稱爲 I,事務的隔離性。由併發事務所作的修改必須與任何其餘併發事務所作的修改隔離。在SQL SERVER中,經過設置隔離級別來保證。事務識別數據時數據所處的狀態,要麼是另外一併發事務修改它以前的狀態,要麼是第二個事務修改它以後的狀態,事務不會識別中間狀態的數據。這稱爲可串行性,由於它可以從新裝載起始數據,而且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。
 
    根據業務的實際狀況和需求,能夠對不一樣的業務選擇不一樣的隔離級別 來 控制 該事務 與其餘併發事務之間的 隔離關係,隔離級別越高,併發能力就相應越低。    

2.4 Durability

    簡稱爲D,事務的持久性。完成徹底持久的事務以後,它的影響將永久存在於系統中。該修改即便出現系統故障也將一直保持。存儲引擎中,經過redo log 來實現事務的持久性。    

3 協議

    在事務內,除了保持ACID的特性外,在MSSQL中,會遵循相應的兩階段鎖跟XA協議。

3.1 2PL

    兩階段鎖2PL,這個在以前的博文  http://www.cnblogs.com/xinysu/p/7260227.html 中有所說起。
 
    2-PL,也就是兩階段鎖,鎖的操做分爲兩個階段:加鎖、解鎖。先加鎖,後解鎖,不相交。加鎖時,讀操做會申請並佔用S鎖,寫操做會申請並佔用X鎖,若是對所在記錄加鎖有衝突,那麼會處於等待狀態,知道加鎖成功才驚醒下一步操做。解鎖時,也就是事務提交或者回滾的時候,這個階段會釋放該事務中全部的加鎖狀況,進行一一釋放鎖。
 
     假設事務對記錄A和記錄B都有操做,那麼,其加鎖解鎖按照逐行加鎖解鎖順序,以下:
     
BEGIN
LOCK A
READ A
A:A+100
WRITE A
UNLOCK A
LOCK B
READ B
UNLOCK B
COMMIT

     兩階段鎖還有幾種特殊狀況:conservative(保守)、strict(嚴格)、strong strict(強嚴格),這三種類型在加鎖和釋放鎖的處理有些不同。數據庫

  • conservative
    • 在事務開始的時候,獲取須要的記錄的鎖,避免在操做期間逐個申請鎖可能形成的鎖等待,conservative 2PL 能夠避免死鎖
  • strict 
    • 僅在事務結束的時候(commit or rollback),才釋放全部 write lock,read lock 則正常釋放
  • strong strict
    • 僅在事務結束的時候(commit or rollback),才釋放全部鎖,包括write lock 跟 read lock 都是結束後才釋放。

3.2 XA

    說XA協議,不得不提2PC、3PC協議,而提這兩個協議,又不得不說說CAP理論。

3.2.1 CAP理論

     CAP分別是Consitency(一致性)、Availability(可用性)、Partition tolerance(分區容錯性),在分佈式存儲系統裏邊,最多隻能同時知足其中二者。
  • Consitency
    • 一致性,在分佈式存儲系統中,對於每一次的讀操做,對於讀到的數據,要麼都是最新的,要麼則返回一個錯誤
    • 這裏的CAP的C跟ACID的C雖然是一個單詞,可是含義不同哦,記得區分
    • 在關係型數據庫裏邊,一般優先考慮到是一致性
  • Availability
    • 可用性,保證每次請求都正常,但不要求返回的結果是最新的數據
  • Partition tolerance
    • 分區容錯性,當各個分區之間由於網絡發生消息丟失或者延遲是,分佈式存儲系統仍能正常運行。
    • 則是在操做 涉及多個服務器的事務 過程當中,

3.2.2 2PC

    兩階段提交,全程是 Two Phase Commitment Protocol。也就是將分佈式事務分爲了兩個階段:Prepare 跟 Commit/Rollback。
 
    在分佈式系統中, 若是其中一個服務器上的操做失敗,則其餘服務器上的操做也須要回滾,即在一個事務內,多個跨服務器的操做中,要麼全部都成功,要麼全部都失敗。可是呢,實際上每一個節點均可以對自身的操做作控制,可是卻不能控制同個分佈式系統的其餘節點的操做,這也就致使了,同個事務中,若是A節點操做成功,可是B節點操做失敗,若是沒有相應的處理方式,則會出現,某些操做成功,某些操做失敗,形成數據不一致的狀況。
 
     如何處理這個問題呢?這就須要引入一個第三方組件來統一接收各個節點的操做狀況,而後再根據各個操做的結果來判斷各個節點的操做是否提交或者回滾。這個就是 2PC的雛形了。
 
 
    2PC的簡要處理說明以下:
  • Prepare
    • 事務協調器coordinator 對 涉及到的節點 發起 操做申請;
    • 各個節點獲取到 操做後,直接在 數據庫中執行,並存放相關的日誌到redo / undo log中,注意注意,這裏僅是操做,並無提交或者回滾該操做;
    • 各節點將處理日誌寫入磁盤;
    • 返回信息給coordinnator
      • 若是節點能夠正常執行,則返回 Ready 通知 coordinator;
      • 若是節點不能夠正常執行,則該節點本地回滾該操做,並返回 Not Ready 通知 coordinator
  • Comiit/Rollback
    • coordinator 根據各個節點 的反饋信息,來決定 該事務操做的結果
    • coordinator將 操做結果記錄到日誌中
    • 反饋操做結果給各個節點
      • 若是出現一個或者一個以上的節點 反饋回來 Not Ready的通知,則coordinator會通知 正常執行操做的節點 回滾事務
      • 若是沒有出現 Not Ready 的反饋,則coordinator會通知全部節點 COMMIT 操做。 
2PC如何處理異常?
  • 事務協調器宕機
    • 這裏須要引入一個新角色:coordinator watchdog,事務協調器看門狗
    • 不管是coordinator 仍是分佈式系統的各個節點,在操做過程當中,都會記錄當前操做的狀態日誌。當出現異常或者恢復時,能夠經過日誌來判斷當前的狀況。
    • 當 coordinator 發起提議後宕機,而此時各個節點開始操做,而後反饋給 coordinator,可是 遲遲沒有接收到 coordinator 的迴應,那麼各個節點的操做就沒法回滾或者提交,處於堵塞狀況。而 coordinator watchdog 則能夠解決這個堵塞現象,當coordinator宕機必定時間後,看門狗會自動 擔任 coordinator 的工做,接收各個節點的 反饋狀況,而後再根據反饋結果傳遞 COMMIT/ROLLBAK給各個節點。
  • 節點宕機
    • prepare階段宕機,則coordinator接收到事務後發送給各個節點須要作的 操做時,節點發生宕機,這個時候,則該節點沒法返回 Ready 的消息,coordinator則默認接受該節點發出的 abort 信息,coordinator通知其餘各個節點 Rollback 操做;
    • Comiit/Rollback階段宕機,因爲各個節點及coordinator都有日誌記錄,coordinator會記錄這個事務是會提交仍是回滾,當 節點宕機後,其餘節點根據coordinator的通知執行ROLLBACK或者COMMIT,而宕機節點本地會記錄該事務操做未執行提交或者回滾,節點恢復後,會從 coordinator 日誌中讀取日誌,從新處理該操做。

3.2.3 XA協議

    XA協議是  X/Open DTP Group 定義的兩階段提交協議,規定了事務管理器跟資源管理器的接口。
 
    事務管理器指的是 二階段協議中的 coordinator ,而資源管理器則指的是各個數據庫系統。
    
    通常狀況下,各個數據庫系統並不知道彼此之間作了什麼,這個時候,就須要一個第三方來作信息的接受跟傳達,由它通知和協調相關數據庫的提交或回滾。XA就是用來定義這個第三方的協議,詳細定義了交易中間件與數據庫之間的接口規範(即接口函數),交易中間件用它來通知數據庫事務的開始、結束以及提交、回滾等。
 
    XA接口函數由數據庫廠商提供。一般狀況下,交易中間件與數據庫經過XA 接口規範,使用兩階段提交來完成一個全局事務,XA規範的基礎是兩階段提交協議。注意,XA事務的性能相對較差。
     
   
 
 參考文檔:
相關文章
相關標籤/搜索