簡介
SQL Server中的事務日志無疑是SQL Server中最重要的部分之一。因為SQL SERVER利用事務日志來確保持久性(Durability)和事務回滾(Rollback)。從而還部分確保了事務的ACID屬性.在SQL Server崩潰時,DBA還可以通過事務日志將數據恢復到指定的時間點。當SQL Server運轉良好時,多了解一些事務日志的原理和概念顯得並不是那么重要。但是,一旦SQL SERVER發生崩潰時,了解事務日志的原理和概念對於快速做出正確的決策來恢復數據顯得尤為重要.本系列文章將會從事務日志的概念,原理,SQL Server如何使用日志來確保持久性屬性等方面來談SQL Server的事務日志.
事務日志的物理組織構架
事務日志僅僅是記錄與其對應數據庫上的事務行為和對數據庫修改的日志文件.在你新建數據庫時,伴隨着數據庫文件,會有一個默認以ldf為擴展名的事務日志文件. 當然,一個數據庫也可以配有多個日志文件,但是在邏輯上,他們可以看成一個.
在SQL Server對於日志文件的管理,是將邏輯上一個ldf文件划分成多個邏輯上的虛擬日志文件(virtual log files,簡稱VLFs).以便於管理。用個類比方法來看,日志文件(ldf)好比一趟火車,每一節車廂都是一個虛擬日志文件(VLFs):
那為什么SQL Server要把日志文件划分出多個VLFS呢?因為SQL Server通過這種方式使得存儲引擎管理事務日志更加有效.並且對於日志空間的重復利用也會更加高效。使用VLF作為收縮數據庫的最小單位比使用ldf文件作為最小單位無疑是更加高效的.
VLFS的個數和大小無法通過配置進行設定,而是由SQL Server進行管理.當Create或Alter數據庫時,SQL Server通過ldf文件的大小來決定VLFS的大小和數量。在日志文件增長時,SQL Server也會重新規划VLFS的數量.
注意:根據這個原理不難看書,如果設置日志文件的增量過小,則會產生過多的VLFS,也就是日志文件碎片,過多的日志文件碎片會拖累SQL Server性能.
SQL Server創建數據庫時,根據日志文件(ldf)的大小,生成VLF的數量公式如下:
| ldf文件的大小 |
VLF的數量 |
| 1M到64M |
4 |
| 64M到1GB |
8 |
| 大於1GB |
16 |
下面我們來看一個例子:
創建數據庫,指定日志大小為65M
通過DBCC,我們可以看到,對應的有8個VLFs:
再次創建數據庫,指定日志初始大小為28M:
可以看到,對應的,VLF的數量變為4:
而對於日志文件的增長,SQL Server使用了和創建數據庫時相同的公式,也就是每次增長比如為2M,則按照公式每次增長4個VLFs.
我們創建一個TestGrow數據庫,指定日志文件為2M,此時有4個VLFS:
當我們增長2M時,這個2M則是按照公式,再次分配4個VLFs:
此時,這時能看到的VLFs數量應該為4+4=8個:
由此可以看出,指定合適的日志文件初始大小和增長,是減少日志碎片最關鍵的部分.
事務日志的邏輯組織構架
當針對數據庫對象所做的任何修改保存到數據庫之前,相應的日志首先會被記錄到日志文件。這個記錄會被按照先后順序記錄到日志文件的邏輯末尾,並分配一個全局唯一的日志序列號(log sequence number,簡稱LSN),這個序列號完全是按照順序來的,如果日志中兩個序列號LSN2>LSN1,則說明LSN2所在LSN1之后發生的.
由此可以看出,將日志文件分為多個文件除了磁盤空間的考慮之外。完全不會像數據那樣可以並行訪問,所以將日志文件分為多個完全不會有性能上的提升.
LSN號可以看作是將日志文件和其記錄數據之間的紐帶.每一條日志不僅有LSN號,還有其對應事務的事務日志:
一個簡單的圖片示例如下:
許多類型的操作都記錄在事務日志中。這些操作包括:
-
每個事務的開始和結束。
-
每次數據修改(插入、更新或刪除)。這包括系統存儲過程或數據定義語言 (DDL) 語句對包括系統表在內的任何表所做的更改。
-
每次分配或釋放區和頁。
-
創建或刪除表或索引。
對於LSN如何在ROLLBACK或者是ROLL FORWARD中以及在備份恢復過程中起作用,會在后續文章中提到
總結
本篇文章從事務日志的邏輯和物理構架簡單介紹了事務日志的構成.這是理解SQL Server如何利用日志保證持久性和數據備份恢復的基礎。下一篇文章將會介紹SQL Server在操作中會如何使用到日志文件
簡介
每一個SQL Server的數據庫都會按照其修改數據(insert,update,delete)的順序將對應的日志記錄到日志文件.SQL Server使用了Write-Ahead logging技術來保證了事務日志的原子性和持久性.而這項技術不僅僅保證了ACID中的原子性(A)和持久性(D),還大大減少了IO操作,把對數據的修改提交到磁盤的工作交給lazy-writer和checkpoint.本文主要講述了SQL Server修改數據時的過程以及相關的技術。
預寫式日志(Write-Ahead Logging (WAL))
SQL Server使用了WAL來確保了事務的原子性和持久性.實際上,不光是SQL Server,基本上主流的關系數據庫包括oracle,mysql,db2都使用了WAL技術.
WAL的核心思想是:在數據寫入到數據庫之前,先寫入到日志.
因為對於數據的每筆修改都記錄在日志中,所以將對於數據的修改實時寫入到磁盤並沒有太大意義,即使當SQL Server發生意外崩潰時,在恢復(recovery)過程中那些不該寫入已經寫入到磁盤的數據會被回滾(RollBack),而那些應該寫入磁盤卻沒有寫入的數據會被重做(Redo)。從而保證了持久性(Durability)
但WAL不僅僅是保證了原子性和持久性。還會提高性能.
硬盤是通過旋轉來讀取數據,通過WAL技術,每次提交的修改數據的事務並不會馬上反映到數據庫中,而是先記錄到日志.在隨后的CheckPoint和lazy Writer中一並提交,如果沒有WAL技術則需要每次提交數據時寫入數據庫:
而使用WAL合並寫入,會大大減少磁盤IO:
也許你會有疑問,那每次對於修改的數據還是會寫入日志文件.同樣消耗磁盤IO。上篇文章講過,每一筆寫入日志的記錄都是按照先后順序,給定順序編號的LSN進行寫入的,日志只會寫入到日志文件的邏輯末端。而不像數據那樣,可能會寫到磁盤的各個地方.所以,寫入日志的開銷會比寫入數據的開銷小很多。
SQL Server修改數據的步驟
SQL Server對於數據的修改,會分為以下幾個步驟順序執行:
1.在SQL Server的緩沖區的日志中寫入”Begin Tran”記錄
2.在SQL Server的緩沖區的日志頁寫入要修改的信息
3.在SQL Server的緩沖區將要修改的數據寫入數據頁
4.在SQL Server的緩沖區的日志中寫入”Commit”記錄
5.將緩沖區的日志寫入日志文件
6.發送確認信息(ACK)到客戶端(SMSS,ODBC等)
可以看到,事務日志並不是一步步寫入磁盤.而是首先寫入緩沖區后,一次性寫入日志到磁盤.這樣既能在日志寫入磁盤這塊減少IO,還能保證日志LSN的順序.
上面的步驟可以看出,即使事務已經到了Commit階段,也僅僅只是把緩沖區的日志頁寫入日志,並沒有把數據寫入數據庫.那將要修改的數據頁寫入數據庫是在何時發生的呢?
Lazy Writer和CheckPoint
上面提到,SQL Server修改數據的步驟中並沒有包含將數據實際寫入到磁盤的過程.實際上,將緩沖區內的頁寫入到磁盤是通過兩個過程中的一個實現:
這兩個過程分別為:
1.CheckPoint
2.Lazy Writer
任何在緩沖區被修改的頁都會被標記為“臟”頁。將這個臟頁寫入到數據磁盤就是CheckPoint或者Lazy Writer的工作.
當事務遇到Commit時,僅僅是將緩沖區的所有日志頁寫入磁盤中的日志文件:
而直到Lazy Writer或CheckPoint時,才真正將緩沖區的數據頁寫入磁盤文件:
前面說過,日志文件中的LSN號是可以比較的,如果LSN2>LSN1,則說明LSN2的發生時間晚於LSN1的發生時間。CheckPoint或Lazy Writer通過將日志文件末尾的LSN號和緩沖區中數據文件的LSN進行對比,只有緩沖區內LSN號小於日志文件末尾的LSN號的數據才會被寫入到磁盤中的數據庫。因此確保了WAL(在數據寫入到數據庫之前,先寫入日志)。
Lazy Writer和CheckPoint的區別
Lazy Writer和CheckPoint往往容易混淆。因為Lazy Writer和CheckPoint都是將緩沖區內的“臟”頁寫入到磁盤文件當中。但這也僅僅是他們唯一的相同點了。
Lazy Writer存在的目的是對緩沖區進行管理。當緩沖區達到某一臨界值時,Lazy Writer會將緩沖區內的臟頁存入磁盤文件中,而將未修改的頁釋放並回收資源。
而CheckPoint存在的意義是減少服務器的恢復時間(Recovery Time).CheckPoint就像他的名字指示的那樣,是一個存檔點.CheckPoint會定期發生.來將緩沖區內的“臟”頁寫入磁盤。但不像Lazy Writer,Checkpoint對SQL Server的內存管理毫無興趣。所以CheckPoint也就意味着在這個點之前的所有修改都已經保存到了磁盤.這里要注意的是:CheckPoint會將所有緩沖區的臟頁寫入磁盤,不管臟頁中的數據是否已經Commit。這意味着有可能已經寫入磁盤的“臟頁”會在之后回滾(RollBack).不過不用擔心,如果數據回滾,SQL Server會將緩沖區內的頁再次修改,並寫入磁盤。
通過CheckPoint的運作機制可以看出,CheckPoint的間歇(Recovery Interval)長短有可能會對性能產生影響。這個CheckPoint的間歇是一個服務器級別的參數。可以通過sp_config進行配置,也可以在SSMS中進行配置:
恢復間歇的默認參數是0,意味着由SQL Server來管理這個回復間隔。而自己設置恢復間隔也是需要根據具體情況來進行界定。更短的恢復間歇意味這更短的恢復時間和更多的磁盤IO,而更長的恢復間歇則帶來更少的磁盤IO占用和更長的恢復時間.
除了自動CheckPoint之外,CheckPoint還會發生在Alter DataBase以及關閉SQL Server服務器時。sysadmin和db_backupoperator組的成員以及db_owner也可以使用CheckPoint指令來手動保存CheckPoint:
通過指定CheckPoint后的參數,SQL Server會按照這個時間來完成CheckPoint過程,如果時間指定的短,則SQL Server會使用更多的資源優先完成CheckPoint過程。
通常情況下,將“臟”頁寫入磁盤的工作,Lazy Writer要做的比CheckPoint會多出許多。
總結
本文簡單介紹了WAL的概念和修改數據庫對象時,日志所扮演的角色。還分別介紹了CheckPoint和Lazy Writer,對於這些概念的理解是理解SQL Server DBA工作的基礎。下篇文章將會講述在簡單恢復模式下日志的機制
簡介
在簡單恢復模式下,日志文件的作用僅僅是保證了SQL Server事務的ACID屬性。並不承擔具體的恢復數據的角色。正如”簡單”這個詞的字面意思一樣,數據的備份和恢復僅僅是依賴於手動備份和恢復.在開始文章之前,首先要了解SQL Server提供的幾種不同備份類型。
SQL Server提供的幾種備份類型
SQL Server所提供的幾種備份類型基本可以分為以下三種(文件和文件組備份以及部分備份不在本文討論之列):
1.完整(Full)備份:直接將所備份的數據的所有區(Extent)進行復制。這里值得注意的有2點:
- 完整備份並不像其名字“完整”那樣備份所有部分,而是僅備份數據庫本身,而不備份日志(雖然僅僅備份少量日志用於同步)
- 完整備份在備份期間,數據庫是可用的。完整備份會記錄開始備份時的LSN號,結束備份時的LSN號,以便在備份結束時將這期間的改動應用到備份,所以完整備份后數據的時間點是備份結束的時間。
2.差異(Differential)備份:只備份上次完整備份后,做修改的部分。備份單位是區(Extent)。意味着某個區內即使只有一頁做了變動,則在差異備份里會被體現.差異備份依靠一個BitMap進行維護,一個Bit對應一個區,自上次完整備份后,被修改的區會被置為1,而BitMap中被置為1對應的區會被差異備份所備份。而到下一次完整備份后,BitMap中所有的Bit都會被重置為0。
3.日志(Log)備份:僅僅備份自上次完整備份或日志備份之后的記錄。在簡單模式下,日志備份毫無意義(SQL Server不允許在簡單恢復模式下備份日志),下文會說明在簡單恢復模式下,為什么日志備份沒有意義。
簡單恢復模式(Simple Recovery Mode)
在簡單恢復模式下,日志僅僅是為了保證SQL Server事務的ACID。並沒有恢復數據的功能.
比如,我們有一個備份計划,如下:
我們在每周一0點做一次完整備份,在周三0點和周五0點分別做差異備份。在簡單恢復模式下,如果周六數據庫崩潰。我們的恢復計划只有根據周一0點的做的完整備份恢復后,再利用周五0點的差異備份進行恢復.而周五0點之后到服務器崩潰期間所有的數據將會丟失。
正如”簡單”這個詞所涵蓋的意思,在簡單恢復模式下,日志可以完全不用管理。而備份和恢復完全依賴於我們自己的完整和差異備份.
恢復模式是一個數據庫級別的參數,可以通過在SSMS里或通過SQL語句進行配置:
簡單恢復模式下日志的空間使用
在本系列文章的第一篇文章提到過,日志文件會划分成多個VLF進行管理,在邏輯上記錄是線性的,給每個記錄一個順序的,唯一的LSN。
而在簡單恢復模式下,為了保證事務的持久性,那些有可能回滾的數據會被寫入日志。這些日志需要被暫時保存在日志以確保在特定條件下事務可以順利回滾。這就涉及到了一個概念—最小恢復LSN(Minimum Recovery LSN(MinLSN) )
MinLsn是在還未結束的事務記錄在日志中最小的LSN號,MinLSN是下列三者之一的最小值:
-
CheckPoint的開始LSN
-
還未結束的事務在日志的最小LSN
-
尚未傳遞給分發數據庫的最早的復制事務起點的 LSN.
下圖是一個日志的片段:
(圖片摘自MSDN)
可以看到,最新的LSN是148,147是CheckPoint,在這個CheckPoint之前事務1已經完成,而事務2還未完成,所以對應的MinLSN應該是事務2的開始,也就是142.
而從MinLSN到日志的邏輯結尾處,則稱為活動日志(Active Log)。
而活動日志分布在物理VLF上的關系可以用下圖表示:
因此,VLF的狀態是源自其上所含有的LSN的狀態,可以分為兩大類:活動VLF和不活動VLF
而更加細分可以將VLF的狀態分為以下四類:
- 活動(Active) –在VLF 上存儲的任意一條LSN是活動的時,則VLF則為活動狀態,即使一個200M的VLF只包含了一條LSN,如上圖的VLF3
- 可恢復(Recoverable) – VLF是不活動的,VLF上不包含活動LSN,但還未被截斷(truncated)
- 可重用(Reusable) – VLF是不活動的,VLF上不包含活動LSN,已經被截斷(truncated),可以重用
- 未使用(Unused) – VLF是不活動的,並且還未被使用過
概念如下圖:
而所謂的截斷(truncated)只是將可恢復狀態的VLF轉換到可重用狀態。在簡單恢復模式下,每一次CheckPoint會引發一次截斷.而每一次CheckPoint都會將MinLSN向后推.所以當事務結束后並且過了CheckPoint點,其相關的日志將會被截斷以便重復利用空間。
在日志達到日志文件(ldf文件)末尾時,也就是上圖的VLF8時,會重新循環到VLF1開始,以便讓空間進行重復利用.所以日志雖然可以從物理順序上是從VLF1到VLF8,但邏輯順序可以是從VLF6開始到VLF2結束:
因此可以看出,簡單恢復模式下日志是不保存的(當事務結束后,相關的會被截斷)。僅僅是用於保證事務回滾和崩潰恢復的用途.所以備份日志也就無從談起,更不能利用日志來恢復數據庫。
總結
本文介紹了簡單恢復模式下日志的原理,並簡單的引出了一些備份或者恢復數據的基礎。而實際上,除了在開發或測試環境下。使用簡單恢復模式的場景並不多,因為在現實生活中,在生產環境允許幾個小時的數據丟失的場景幾乎沒有.下篇文章將會講述在完整恢復模式下,日志的作用
簡介
生產環境下的數據是如果可以寫在資產負債表上的話,我想這個資產所占的數額一定不會小。而墨菲定律(事情如果有變壞的可能,無論這種可能性有多小,它總會發生)仿佛是給DBA量身定做的。在上篇文章介紹的簡單恢復模式下,從最近一次備份到當前的數據都會存在丟失的風險。而完整備份模式使得數據丟失的風險大大減少。本文主要介紹在完整備份模式下概念原理和日志所處的角色。
完整(Full)恢復模式
完整恢復模式通過將對數據庫的任何修改記錄到日志來給予數據最大程度的保護。在完整恢復模式下,日志的作用不僅僅是保證了數據庫事務的ACID。並且還可以使數據恢復到在日志范圍內的任何時間點。
在上一篇文章中說過,在簡單恢復模式下,日志幾乎是不用進行管理的。每一次CheckPoint都有可能截斷日志,從而來回收不活動的VLF以便重復利用空間。因此在簡單恢復模式下,日志的空間使用幾乎可以不去考慮。與之相反,在完整恢復模式下,日志作為恢復數據的重要組成部分,日志的管理和對日志空間使用的管理則需要重視。
在完整恢復模式下,CheckPoint不會截斷日志。只有對日志的備份才會將MinLSN向后推並截斷日志。因此在一個業務量稍大的系統中,日志的增長速度將會變得很快。
因此日志備份的目的分為以下兩個:
- 減少活動日志的大小
- 減少日志損壞的風險
通過從MSDN中摘自的下圖可以看到:
在DB_1處做了完整備份,並且接下來兩次分別做了兩次日志備份(Log_1和Log_2),在Log_2備份完不久服務器由於數據所在磁盤損壞。這時如果日志文件完好,則可以通過備份尾部日志(Tail of log)后,從DB_1開始恢復,依次恢復Log_1,Log_2,尾部日志來將數據庫恢復到災難發生時的時間點。理論上可以使數據的損失為0。
從日志恢復數據的原理是Redo,也就是將日志中記載的事務再重做一遍。這個開銷和從完整或差異備份中恢復相比,要大很多。因此盡可能的減少利用日志的恢復量。而使用完整或者差異備份來恢復更多的數據。
大容量日志(Bulk-logged)恢復模式
大容量恢復模式在很多地方和完整恢復模式相同。但由於在完整恢復模式下,對數據庫的每一項操作都會記錄在日志中。而對於某些大量數據的導入導出操作,無疑會在日志中留下大量記錄。很多情況下,我們並不需要將這些信息記錄在日志中。
而大容量日志恢復模式作為完整恢復模式的備選方案。微軟推薦的最佳實踐是在進行大量數據操作時(比如索引的創建和rebuilt,select into操作等),暫時由完整恢復模式切換到大容量恢復模式來節省日志。這個轉換並不會破壞日志鏈。
本文不會深入探討這個模式,僅僅是對這個概念做簡單解釋。假設我要插入一批數據,則完整恢復模式和大容量日志恢復模式在日志中所記錄的信息如下:
由此可以看出,在日志中,大容量恢復模式將這類操作變為一個原子。所以在大容量日志恢復模式下,不能redo大容量日志中的這類操作(select into之類的)
日志鏈(Log Chain)
連續的日志備份被稱之為日志鏈。表示日志是連續的.這個概念可以用下圖表示:
假設上面兩個日志備份可以簡單抽象成如上2個備份,則日志備份1的末尾LSN必須大於等於日志備份二的第一個LSN(通常情況下是第一個末尾LSN等於第二個日志備份的第一個LSN,但由於存在“只備份日志”選項只備份日志,並不截斷日志,所以有可能重疊)。則這兩個備份的日志鏈是連續的。
下圖是一個生產環境下,在SSMS中查看日志鏈連續的例子:
可以看出,第一次完整備份后,備份多次事務日志,每一個事務日志的開始LSN都等於上一個事務日志的結束LSN。因此可以從第一次完整備份開始,恢復到最后一個日志備份期間的任何時間點。
完整的日志鏈以第一次完整備份或由簡單恢復模式轉為完整或大容量日志模式開始,到當前的時間點結束。
而從日志恢復數據要求從最近一次完整或差異備份到所恢復的時間點之間的日志鏈是連續的。
恢復次序
從備份恢復數據需要經歷如下幾步驟:
1.復制數據階段:從完整備份和差異備份中將數據,索引頁和日志復制到被恢復數據庫文件。
2.Redo(roll forward)階段:將記錄在日志中的事務應用到從備份中復制過來的數據。使數據Roll Forward到指定的時間點.這個階段完成后,數據庫還處於不可使用階段:
上面兩部就是Restore
3.Undo(Roll Back)階段:這也是傳說中的Recovery,將任何未提交的事務回滾。這個階段過后,數據庫處於可用狀態。任何后續備份將不能接着應用到當前數據庫。
這個概念比如:
在連續兩個日志鏈的日志備份,在第一個事務日志備份中定義的事務,在第二個事務日志備份中Commit.如果在第一個事務日志還原后使用了Recovery選項.也就是經歷了Undo階段。則事務1在Undo階段會被回滾:
可見,日志備份1中的T1被回滾,在日志備份2中的Commit也就毫無意義。這也就是為什么經歷過Undo階段后不允許再恢復后續備份。因此,微軟推薦的最佳實踐是使用NoRecovery選項不進行Undo階段。而在所有備份恢復后單獨進行Undo階段,這個操作可以通過還原日志尾部時,指定Recovery選項進行。
總結
本文簡單介紹了在完整恢復模式下,日志的作用以及對數據恢復的一些概念。理解完整恢復模式的概念對於減少數據丟失的風險是無可替代的。



























