MSSQL併發控制原先打算分爲兩個部分寫:隔離級別及鎖,寫的過程當中,發現須要說起下事務的相關內容,故加多一篇博文,共3篇。
併發控制,在於控制每個事務的操做過程以及它們對資源的佔用狀況,同時要保證事務的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 分佈式事務
這裏有一點須要注意一下:當事務內部操做跨越了多個服務器,可是並無使用 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 來實現事務的持久性。
2-PL,也就是兩階段鎖,鎖的操做分爲兩個階段:加鎖、解鎖。先加鎖,後解鎖,不相交。加鎖時,讀操做會申請並佔用S鎖,寫操做會申請並佔用X鎖,若是對所在記錄加鎖有衝突,那麼會處於等待狀態,知道加鎖成功才驚醒下一步操做。解鎖時,也就是事務提交或者回滾的時候,這個階段會釋放該事務中全部的加鎖狀況,進行一一釋放鎖。
兩階段提交,全程是 Two Phase Commitment Protocol。也就是將分佈式事務分爲了兩個階段:Prepare 跟 Commit/Rollback。
在分佈式系統中, 若是其中一個服務器上的操做失敗,則其餘服務器上的操做也須要回滾,即在一個事務內,多個跨服務器的操做中,要麼全部都成功,要麼全部都失敗。可是呢,實際上每一個節點均可以對自身的操做作控制,可是卻不能控制同個分佈式系統的其餘節點的操做,這也就致使了,同個事務中,若是A節點操做成功,可是B節點操做失敗,若是沒有相應的處理方式,則會出現,某些操做成功,某些操做失敗,形成數據不一致的狀況。
如何處理這個問題呢?這就須要引入一個第三方組件來統一接收各個節點的操做狀況,而後再根據各個操做的結果來判斷各個節點的操做是否提交或者回滾。這個就是 2PC的雛形了。
通常狀況下,各個數據庫系統並不知道彼此之間作了什麼,這個時候,就須要一個第三方來作信息的接受跟傳達,由它通知和協調相關數據庫的提交或回滾。XA就是用來定義這個第三方的協議,詳細定義了交易中間件與數據庫之間的接口規範(即接口函數),交易中間件用它來通知數據庫事務的開始、結束以及提交、回滾等。
XA接口函數由數據庫廠商提供。一般狀況下,交易中間件與數據庫經過XA 接口規範,使用兩階段提交來完成一個全局事務,XA規範的基礎是兩階段提交協議。注意,XA事務的性能相對較差。