ACID 是數據庫的基本屬性。其中的D是指"持久性":只要事務已經提交,對應的數據修改就會被保存下來,即使出現斷電等情況,當系統重啟后之前已經提交的數據依然能夠反映到數據庫中。
那么D特性是如何在SQL Server中實現的呢?SQL Server使用write-ahead logging的方式,保證日志記錄會先於數據記錄固化到磁盤中。當事務提交后,只有當日志記錄固化到磁盤時,才會向客戶端返回提交成功的消息,至於相應的數據記錄,會通過異步的方式后續寫入到磁盤中。如果在此期間發生斷電等故障,那么就會出現以下兩種情況:
-
日志已經寫入到磁盤(committed),但數據沒有寫入:
系統重啟后進行redo操作,通過讀取日志,來將沒有固化到數據文件的信息寫入到數據文件。
-
部分日志已經寫入到磁盤(uncommitted),數據部分寫入或沒有寫入
系統重啟后執行undo操作,將沒有提交的事務對應的數據從數據文件中清除。
這樣就保證了已經提交的事務不會丟失。
Delayed durability
SQL Server 2014中引入了一個新的特性,叫做Delayed durability(也稱作lazy commit),顛覆了之前提到的概念。通過Delayed durability,可以讓日志記錄按照一定規律異步地寫入到日志文件中,避免日志磁盤寫入過於頻繁。這樣就以犧牲Durability來換取性能。
應用場景:
使用該特性的前提是您的應用可以容忍一定程度的數據丟失。
日志磁盤出現系統瓶頸。
由於日志磁盤性能問題,導致事務無法提交,導致相應的資源(memory,lock等)無法釋放引發的資源競爭
Delayed Durability有以下特性:
- 一旦事務提交,事務中的數據變更對其他事務(包含full durable transaction和delayed durability transaction)可見。具體請參考isolation level http://msdn.microsoft.com/en-us/library/dn133175.aspx
- 事務的持久性(durability)依賴於日志記錄是否固化到磁盤。
-
內存中的日志記錄只有在任意以下情況發生時才會固化到磁盤:
- )Full durable transaction進行了數據變更,並且commit.
- )執行了sp_flush_log存儲過程.
- )Log buffer滿了,日志記錄也會固化到磁盤.
如果1)或2)出現兩次,那么SQLSERVER會保證第一次之前的Delayed durability transaction的數據變更已經被固化到了磁盤。
如何使用Delayed durability
Delayed durability是一個數據庫級別的特性,默認是禁用的,我們首先要開啟這個選項。
ALTER DATABASE [DDtest] SET DELAYED_DURABILITY = FORCED|Allowed|Disabled |
如果是forced,那么該數據庫內所有的事務都強制使用delayed durability;如果是allowed,那么delayed durability和full durable transaction可以同時存在;如果是disabled,那么無法使用delayed durability.
當該屬性發生變化后,errorlog中也會有相應的記錄
Setting database option delayed_durability to forced for database 'DDtest'.
Setting database option delayed_durability to allowed for database 'DDtest'.
Setting database option delayed_durability to disabled for database 'DDtest'.
如果數據庫的DELAYED_DURABILITY為Allowed,我們可以在語句級別進行控制,否則就要遵循數據庫的設定了(如果語句的設定和數據庫級別設定沖突,那么SQL Server會使用數據庫級別的設定)。
事例
將數據的 DELAYED_DURABILITY設置為Allowed
ALTER DATABASE [DDtest] SET DELAYED_DURABILITY = Allowed
創建一張表,並循環插入1000行數據,每次插入都是一個單獨的事務
create table ta(col int)
declare @N int=0
while @n<1000
begin
begin tran
insert ta values(1)
commit tran with(delayed_durability=off)
set @N+=1
print cast(@N as varchar(1000))
end
開啟Process Monitor,監控對數據庫日志文件的操作。
一共對日志文件進行了1012次的寫入操作,也就是每次commit都會立刻固化到日志文件
下面比較一下使用delayed durability的情況
declare @N int=0
while @n<1000
begin
begin tran
insert ta values(1)
commit tran with(delayed_durability=on)
set @N+=1
print cast(@N as varchar(1000))
end
1000個事務只觸發了32次寫入,大大地減少了對日志文件的寫入操作。
注意事項
- 當系統不忙時,也會主動將delayed durability的日志記錄固化到磁盤。但目前不知道如何判斷"不忙"這個標准。
- checkpoint不會將delayed durability的日志記錄固化到磁盤。
- SQL Server正常關閉不會將delayed durability的日志記錄固化到磁盤,也就說正常關閉也可能會導致數據丟失,建議之前先執行sp_flush_log。