簡介
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(最低的性能,一次只能執行一個事務,但避免了上述所有情況)
SNOPSHOT(這個是通過在tempDB中創建一個額外的副本來避免臟讀,不可重復讀,會給tempDB造成額外負擔,因為不是標准ANSI SQL標准,不詳細討論)
總之,不同的隔離級別是通過加不同的鎖,造成阻塞來實現的,來看一個例子:
SQL SERVER通過阻塞來阻止臟讀,所以保持獨立性會以付出性能作為代價:
理解持久性(Durability)
持久性,意味着在事務完成以后,該事務所對數據庫所作的更改便持久的保存在數據庫之中,並不會被回滾。
即使出現了任何事故比如斷電等,事務一旦提交,則持久化保存在數據庫中.
SQL SERVER通過write-ahead transaction log來保證持久性。write-ahead transaction log的意思是,事務中對數據庫的改變在寫入到數據庫之前,首先寫入到事務日志中。而事務日志是按照順序排號的(LSN)。當數據庫崩潰或者服務器斷點時,重啟動SQL SERVER,SQL SERVER首先會檢查日志順序號,將本應對數據庫做更改而未做的部分持久化到數據庫,從而保證了持久性.
"若要了解預寫日志的工作原理,最重要的是了解如何將修改的數據寫入磁盤。SQL Server 維護當必須檢索數據時,將數據頁讀入的緩沖區高速緩存。數據修改不是直接在磁盤上進行,而是修改高速緩沖存儲器中的頁副本。直到數據庫中出現檢查點,或者必須將修改寫入磁盤才能使用緩沖區來容納新頁時,才將修改寫入磁盤。將修改后的數據頁從高速緩沖存儲器寫入磁盤的操作稱為刷新頁。在高速緩存中修改但尚未寫入磁盤的頁稱為“臟頁”。
對緩沖區中的頁進行修改時,將在記錄修改的日志高速緩存中生成一條日志記錄。在將關聯的臟頁從高速緩沖存儲器刷新到磁盤之前,必須將這條日志記錄寫入磁盤。如果在寫入日志記錄前刷新臟頁,則該臟頁便會在磁盤上創建修改。如果服務器在將日志記錄寫入磁盤前失敗,則不能回滾此修改。SQL Server 具有防止在寫入關聯的日志記錄前刷新臟頁的邏輯。日志記錄將在提交事務時寫入磁盤。"(MSDN)
總結
本文簡單講述了ACID的概念和ACID在SQL SERVER中的實現.ACID只是一個理念,並不是某項具體的技術.對於健壯數據庫來說,保證ACID是可靠數據庫的前提.