簡介
ACID,是指在可靠資料庫管理系統(DBMS)中,事務(transaction)所應該具有的四個特性:原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability).這是可靠資料庫所應具備的幾個特性.下面針對這幾個特性進行逐個講解.
理解原子性(Atomicity)
原子性意味著資料庫中的事務執行是作為原子。即不可再分,整個語句要麼執行,要麼不執行。
在SQL SERVER中,每一個單獨的語句都可以看作是預設包含在一個事務之中:
所以,每一個語句本身具有原子性,要麼全部執行,這麼全部不執行,不會有中間狀態:
上面說了,每一條T-SQL語句都可以看作是預設被包裹在一個事務之中的,SQL Server對於每一條單獨的語句都實現了原子性,但這種原子粒度是非常小的,如果使用者想要自己定義原子的大小,則需要包含在事務中來構成使用者自訂的原子粒度:
對於使用者來說,要用事務實現的自訂原子性往往是和業務相關的,比如銀行轉賬,從A賬戶減去100,在B賬戶增加100,如果這兩個語句不能保證原子性的話,比如從A賬戶減去100後,伺服器斷電,而在B賬戶中卻沒有增加100.雖然這種情況會讓銀行很開心,但作為開發人員的你可不希望這種結果.而預設事務中,即使出錯了也不會整個事務進行復原。而是失敗的語句拋出異常,而正確的語句成功執行。這樣會破壞原子性。所以SQL SERVER給予了一些選項來保證事務的原子性.
SQL SERVER提供了兩大類方式來保證自訂事務的原子性:
1.通過SET XACT_ABORT ON來設定事務必須符合原子性
利用設定XACT_ABORT選項設定為ON,來設定所有事務都作為一個原子處理.下面例子利用兩個語句插入到資料庫,可以看到開啟SET XACT_ABORT ON選項後,事務具有了原子性:
2.按照使用者佈建進行復原(ROLLBACK)
這種方式具有更高的靈活性,開發人員可以自訂在什麼情況進行ROLLBACK,利用TRY CATCH語句和@@ERROR進行判斷都屬於這種方式.
理解一致性(Consistency)
一致性,即在事務開始之前和事務結束以後,資料庫的完整性條件約束沒有被破壞。
一致性分為兩個層面
1.資料庫機制層面
資料庫層面的一致性是,在一個事務執行之前和之後,資料會符合你設定的約束(唯一約束,外鍵約束,Check約束等)和觸發器設定.這一點是由SQL SERVER進行保證的.
2.業務層面
對於業務層面來說,一致性是保持業務的一致性.這個業務一致性需要由開發人員進行保證.很多業務方面的一致性可以通過轉移到資料庫機制層面進行保證.比如,產品只有兩個型號,則可以轉移到使用CHECK約束使某一列必須只能存這兩個型號.
理解隔離性(Isolation)
隔離性。事務的執行是互不干擾的,一個事務不可能看到其他事務運行時,中間某一時刻的資料。
在Windows中,如果多個進程對同一個檔案進行修改是不允許的,Windows通過這種方式來保證不同進程的隔離性:
而SQL Server中,通過SQL SERVER對資料庫檔案進行管理,從而可以讓多個進程可以同時訪問資料庫:
SQL Server利用加鎖和阻塞來保證事務之間不同等級的隔離性.
一般情況下,完全的隔離性是不現實的,完全的隔離性要求資料庫同一時間只執行一條事務,這樣的效能可想而知.想要理解SQL Server中對於隔離性的保障,首先要瞭解事務之間是如何幹擾的.
事務之間的互相影響的情況分為幾種,分別為:髒讀(Dirty Read),不可重複讀取,幻讀
髒讀
髒讀意味著一個事務讀取了另一個事務未提交的資料,而這個資料是有可能復原的:
下面來看一個例子:
兩個事務,事務A插入一條資料,但未提交,事務B在此期間進行了讀取,讀取到了事務A未提交的資料,造成髒讀
不可重複讀取(Unrepeatable Read)
不可重複讀取意味著,在資料庫訪問中,一個事務範圍內兩個相同的查詢卻返回了不同資料。這是由於查詢時系統中其他事務修改的提交而引起的。
下面來看一個不可重複讀取的例子:
事務B中對某個查詢執行兩次,當第一次執行完時,事務A對其資料進行了修改。事務B中重新查詢時,資料發生了改變:
幻讀(phantom read)
幻讀,是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。同時,第二個事務也修改這個表中的資料,這種修改是向表中插入一行新資料。那麼,以後就會發生操作第一個事務的使用者發現表中還有沒有修改的資料行,就好象發生了幻覺一樣.
下面來看一個例子:
事務B更新表中所有的資料,在此期間事務A插入了一條資料,事務B重新查詢後,發現居然還有沒有修改的資料,產生幻讀:
理解SQL SERVER中的隔離等級
為了避免上述幾種事務之間的影響,SQL Server通過設定不同的隔離等級來進行不同程度的避免。因為高的隔離等級意味著更多的鎖,從而犧牲效能.所以這個選項開放給了使用者根據具體的需求進行設定。不過預設的隔離等級Read Commited符合了99%的實際需求.
SQL Server隔離事務之間的影響是通過鎖來實現的,這個概念比較繁雜,所以本文不會詳細對這個概念進行講解.通過阻塞來阻止上述效果
SQL Server提供了5種選項來避免不同層級的事務之間的影響
隔離等級由低到高分別為
Read Uncommited(最高的效能,但可能出現髒讀,不可重複讀取,幻讀)
Read commited(可能出現不可重複讀取,幻讀)
Repeatable Read(可能出現幻讀)
Serializable(最低的效能,Range鎖會導致並發下降)
SNOPSHOT(這個是通過在tempDB中建立一個額外的副本來避免髒讀,不可重複讀取,會給tempDB造成額外負擔,因為不是標準ANSI SQL標準,不詳細討論)
總之,不同的隔離等級是通過加不同的鎖,造成阻塞來實現的,來看一個例子:
SQL SERVER通過阻塞來阻止髒讀,所以保持獨立性會以付出效能作為代價:
理解持久性(Durability)
持久性,意味著在事務完成以後,該事務所對資料庫所作的更改便持久的儲存在資料庫之中,並不會被復原。
即使出現了任何事故比如斷電等,事務一旦提交,則持久化儲存在資料庫中.
SQL SERVER通過write-ahead transaction log來保證持久性。write-ahead transaction log的意思是,事務中對資料庫的改變在寫入到資料庫之前,首先寫入到交易記錄中。而交易記錄是按照順序排號的(LSN)。當資料庫崩潰或者伺服器斷點時,重啟動SQL SERVER,SQL SERVER首先會檢查日誌順序號,將本應對資料庫做更改而未做的部分持久化到資料庫,從而保證了持久性.
總結
本文簡單講述了ACID的概念和ACID在SQL SERVER中的實現.ACID只是一個理念,並不是某項具體的技術.對於健壯資料庫來說,保證ACID是可靠資料庫的前提.