對SQLSERVER數據庫事務日志的疑問
摸不透SQLSERVER了
實驗環境:SQLSERVER2005 SP4,Windows7
本來沒什么心情寫文章,反正沒人看,關於我文章中提到的問題,有些可以從文章結尾的MSDN補充那里找到答案,而有些還沒有答案
根據CSDN博客的這篇文章介紹,大家可以先看一下,然后再繼續往下看,因為下面會引用到CSDN博客里的一些內容
第一個問題:為什麽我完整備份數據庫的時候,為什麽事務日志記錄數會增加的,再備份一次又減少,再備份一次又增加?
備份之后變成了100行記錄
有時候不是固定會增加50行記錄的,有時候當你再次備份的時候又會變回50行記錄
而虛擬日志文件的數量會減少了,從原來的45個變成35個
備份之后
我是這樣備份的
當我備份事務日志並截斷日志之后,剩下10行記錄,備份事務日志不會減少事務日志的物理大小,需要收縮事務日志才行
當我每次執行完整備份之后,log space used都會增大
1 DBCC SQLPERF(LOGSPACE)
還有根據我上面提供的文章《SQLSERVER事務日志介紹》
當SQL Server把虛擬日志文件1和2作為可重用區域時,事務日志也相應被截斷(Truncate)
需要注意的是,物理日志大小也會隨着變動。如果數據庫運行在完整或是批量日志恢復模型下,那么從LSN45到49之間的區域將被刪除(delete),
而且直到事務日志被備份后,這段區域的空間才會被重用。
但是剛才也備份過事務日志並截斷日志,但是log space used跟日志文件物理大小沒有改變
SQLSERVER應該會以虛擬日志文件作為單位,如果某個虛擬日志文件里沒有任何LSN了,那么截斷事務日志會把
沒有任何LSN的虛擬日志文件那部分里面的LSN刪除
這里說一下截斷跟重用
截斷:如果執行了備份事務日志並截斷日志的操作,那么這部分虛擬日志文件里的LSN日志記錄會被刪除,但是整個事務日志文件的物理大小不會變小
重用:你需要執行備份事務日志並截斷日志的操作,這部分虛擬日志文件才會重用,這樣就不用增加事務日志文件的大小
就是說,你要備份事務日志並截斷日志,然后那些被刪除了LSN日志記錄的虛擬日志文件才能重用,但是SQLSERVER會不會刪除虛擬日志文件呢?
MSDN並沒有說到,MSDN只是說SQLSERVER會創建並擴展虛擬日志文件,但是沒有提到會自動刪除虛擬日志文件
重用回以前的空間,反復循環使用。
問題二:但是每個虛擬日志文件裝載多少個LSN,這里我也不知道,有可能是隨機的?
而重用的時候SQLSERVER應該不會不連續的
比如 像下面的情況
LSN LSN LSN LSN LSN LSN LSN -》
1 2 3 10 5 6 7
LSN 1、2、3都歸屬於一個虛擬日志文件A
LSN 5、6、7都歸屬於另一個虛擬日志文件B
LSN 10 歸屬於一個虛擬日志文件C
由於虛擬日志文件C里的LSN沒有被刪除,那么重用日志的時候就會跳過LSN 10 然后繼續重用日志,但是我覺得SQLSERVER不會是這樣的
我覺得SQLSERVER重用日志應該是下面這樣的
LSN LSN LSN LSN LSN LSN LSN -》
1 2 3 4 5 6 7
一直連續下去,不會出現中間有些虛擬日志文件沒有刪除的情況,確保LSN序列的順序性
我備份了事務日志之后,並截斷之后,物理大小跟使用空間都沒有變化
----------------------------------------------------------------------------------------------------------
第一個LSN:倒數第二個checkpoint之后的第一個LSN
最后一個LSN:最后一個checkpoint之前的那個LSN
檢查點LSN:跟第一個LSN是一樣的
完整LSN:跟第一個LSN是一樣的
上面這個圖是我還原數據庫的時候的界面,大家可以先備份一下數據庫,然后還原數據庫,看一下還原的備份集是不是你剛剛備份的時候的一樣
很簡單的畫了一張圖
----------------------------------------------------------------------------------------------------------------
1 DBCC LOG([DLGPOS])
在這里說一下事務日志里的 operation字段,operation字段說明這個LSN是做什么操作的
例如:LOP_BEGIN_CKPT,CKPT:checkpoint,如果看到operation是LOP_BEGIN_CKPT,表明這個LSN在做checkpoint操作
因為備份數據庫的時候,SQLSERVER會做checkpoint,所以你會看到第一個LSN會跟LOP_BEGIN_CKPT操作的那個LSN聯系起來
把16進制翻譯成10進制跟還原數據庫的時候的第一個LSN是一樣的
而Current LSN只是顯示當前每個日志記錄的LSN號碼,沒有特別的意思,所以昨天我說的,currentLSN等於第一個LSN是錯的
-------------------------------------------------------------------------------------------------------
還有一個“位置”的東東, POSITION字段跟還原數據庫的時候的位置是一一對應的,當我每完整備份一次數據庫就會增加一,或者備份事務日志一次就會增加一
但是完整備份跟事務日志備份是分開的,看下圖
1 USE [msdb] 2 GO 3 SELECT [backup_set_id], [backup_finish_date], [database_name], [first_lsn], 4 last_lsn, [checkpoint_lsn], [is_copy_only],[position] 5 FROM [dbo].[backupset] WHERE [database_name]='dlgpos' 6 ORDER BY [backup_finish_date]
問題3:但是為什麽從64突然又回到9呢?為什麽會斷裂呢?這個我還不搞不清楚,這個位置字段有什么用呢?
參考的文章:
http://msdn.microsoft.com/zh-cn/library/ms190925.aspx
http://msdn.microsoft.com/zh-cn/library/ms189085(v=SQL.90).aspx
http://msdn.microsoft.com/zh-cn/library/ms179355(v=SQL.105).aspx
---------------------------------------------------------------------------------------------------
從MSDN補充來的
在上面的一篇參考文章里
我摘抄重要部分吧
SQL Server 數據庫引擎在內部將每一物理日志文件分成多個虛擬日志文件。虛擬日志文件沒有固定大小,且物理日志文件所包含的虛擬日志文件數不固定。數據庫引擎在創建或擴展虛擬日志文件時動態選擇虛擬日志文件的大小。數據庫引擎嘗試維護少量的虛擬日志文件。在擴展虛擬日志文件后,虛擬日志文件的大小是現有日志大小和新文件增量大小之和。管理員不能配置或設置虛擬日志文件的大小或數量。
只有當日志文件使用較小的 size 和 growth_increment 值定義時,虛擬日志文件才會影響系統性能。如果這些日志文件由於許多微小增量而增長到很大,則它們將具有很多虛擬日志文件。這會降低數據庫啟動以及日志備份和還原操作的速度。建議您為日志文件分配一個接近於最終所需大小的 size 值,並且還要分配一個相對較大的 growth_increment 值
事務日志是一種回繞的文件
看一下這幅圖
問題4:而minLSN有沒有可能跟第一個LSN是一樣的呢?而第一個LSN有沒有可能跟minLSN處於相同的位置或者比第一個LSN排在后面?
我覺得有可能的,就像<SQLSERVER事務日志介紹>里面,假定LSN48是一個checkpoint,那么minLSN就排在第一個LSN后面
為什麽LSN50是一個minLSN呢?
根據msdn說的,minLSN是界定截斷日志的邊界點
minLSN前面的是日志記錄是可以截斷的,minLSN后面的日志記錄是不可以截斷的
-----------------------------------------------------------------------------------------------------------------
更正一下今天說的,無論你進行完整備份還是差異備份還是日志備份,事務日志文件里的所有日志都會保存到bak文件里
不管你進行多少次數據庫備份、還原,這些事務日志都不會消失,收縮事務日志文件也不行的
除非你進行備份事務日志並截斷日志
所以就不存在只保存到checkpoint的那部分,而是當你執行備份命令的時候,最后的那個LSN也會保存到bak文件里!!!!!!!
不信的話,你可以在完整備份數據庫前執行下面的SQL語句,備份完畢之后再執行一下,看事務日志記錄是不是一樣的
1 USE [AdventureWorks] 2 GO 3 DBCC LOG([AdventureWorks])
還有截斷事務日志,看一下前后是否一樣
MSDN上說物理日志文件,邏輯日志文件,虛擬日志文件,LSN,checkpoint應該像下圖這樣
-----------------------------------------------------------------------------------------------------------------
很反感國內某些書本照抄MSDN的內容,當說到事務日志的時候就照抄MSDN,本來MSDN就說得很籠統
只能對它們說一句“他媽的”
由於文檔以及相關文章實在太少,我也研究不下去了,連MSDN論壇里的rmaio大俠都說文檔很少
關於fn_dblog的用法,沒有什么人能夠給出,只是說微軟沒有公開
-------------------------------------------------------------------------------------------------------
20130611補充
msdn文章:事務日志體系結構和管理
這個第一個日志記錄的日志序列號 (LSN),稱為最小恢復 LSN (MinLSN)
那么究竟是不是第一個LSN就是minLSN呢?我們做一個實驗
先運行下面語句
1 USE [GPOSDB] --需要查看minLSN日志信息的數據庫 2 GO 3 --DBCC LOG與[fn_dblog]的記錄數是一樣的,只是[fn_dblog]比DBCC LOG更加詳細 4 SELECT * FROM [sys].[fn_dblog](NULL,NULL) 5 DBCC LOG([GPOSDB])
然后執行一次完整數據庫備份
1 BACKUP DATABASE [GPOSDB] TO DISK='D:\GPOSDBFULLBACKUP20130611.BAK'
你會發現備份之前跟備份之后的minLSN不同了,而且看下圖
就是說 minLSN = 第一個LSN
我把fn_dblog的結果做了一下調整,還有更多有意思的東西
1 SELECT * INTO ##TDBLOG FROM SYS.[fn_dblog](NULL,NULL) 2 SELECT * FROM [##TDBLOG] 3 SELECT [Current LSN], [Operation], [Checkpoint Begin], [Checkpoint End], 4 [Minimum LSN], [Dirty Pages], [SPID], [Transaction Name], 5 [Description] 6 FROM [##TDBLOG] ORDER BY [Current LSN] ASC
上圖說明了,第一個LSN和最后一個LSN在兩個checkpoint中間的,注意operation :LOP_BEGIN_CKPT和 LOP_END_CKPT
checkpoint的時候,有7個臟頁寫到磁盤里去
大家再執行一下下面這個語句
1 SELECT [Current LSN], [Operation] ,[Transaction ID] 2 [Minimum LSN], [Dirty Pages], [SPID], [Transaction Name], 3 [Description] 4 FROM [##TDBLOG] ORDER BY [Current LSN] ASC
大家注意看,我在SSMS里執行backup database的查詢窗口的spid是59,SQLSERVER是先做checkpoint,然后再做backup database的操作的
我是怎麽知道,fn_dblog輸出的結果對應於哪個數據庫呢?
因為剛開始使用這個函數,不知道怎麽指定輸出的某個數據庫的日志信息,而這個函數有一個字段是[Database Name],但是這個字段輸出都是NULL
然后我通過觀察SPID這個字段,發現其中某一行有數據庫啟動的信息,因為我在查詢窗口里指定了如下sql語句,use AdventureWorks,
再看一下SQL ERROR LOG,真的是SPID20這個進程啟動的數據庫,然后我use 其他數據庫,發現也是一樣的
所以你要看某一個數據庫的日志信息的時候先要 use 你要查看日志的那個數據庫
1 USE [AdventureWorks] 2 GO
----------------------------------------------------------------------------------------------------------------------------------
關於問題3,其實這個“位置”就是指的是“日志鏈”
日志鏈
連續的日志備份序列稱為“日志鏈”。 日志鏈從數據庫的完整備份開始。 通常,僅當第一次備份數據庫時,或者將恢復模式從簡單恢復模式切換到完整恢復模式或大容量日志恢復模式之后,才會開始一個新的日志鏈。 除非在創建完整數據庫備份時選擇覆蓋現有備份集,否則現有的日志鏈將保持不變。 在該日志鏈保持不變的情況下,便可從介質集中的任何完整數據庫備份還原數據庫,然后再還原相應恢復點之前的所有后續日志備份。 恢復點可以是上次日志備份的結尾,也可以是任何日志備份中的特定恢復點。 有關詳細信息,請參閱事務日志備份 (SQL Server)。
若要將數據庫還原到故障點,必須保證日志鏈是完整的。 也就是說,事務日志備份的連續序列必須能夠延續到故障點。 此日志序列的開始位置取決於您所還原的數據備份類型:數據庫備份、部分備份或文件備份。 對於數據庫備份或部分備份,日志備份序列必須從數據庫備份或部分備份的結尾處開始延續。 對於一組文件備份,日志備份序列必須從整組文件備份的開頭開始延續。 有關詳細信息,請參閱應用事務日志備份 (SQL Server)。
因為我的電腦里安裝了yoursqldba,他會自動每天自動執行事務日志備份跟完整備份的job,所以就形成了這個日志鏈了
-----------------------------------------------------------------------------------------------------------------
補充:其實checkpoint也屬於一個日志記錄,圖片應該像下面這樣
minLSN:可以看到,最新的LSN是148,147是CheckPoint,在這個CheckPoint之前事務1已經完成,而事務2還未完成,
所以對應的MinLSN應該是事務2的開始,也就是142.
而從MinLSN到日志的邏輯結尾處,則稱為活動日志(Active Log)。
-----------------------------------------------------------------------------------------------
根據園子里的careyson大俠說的,第一個問題
為什麽完整備份之后日志記錄會增加,有時候備份完之后會增加,有時候備份完之后會減少
要說這個問題,大家要先看一下這篇文章:SQL Server 2008 存儲結構之DCM、BCM
里面說到數據庫有一個系統頁面DCM頁,這個頁面記錄了一個文件中的哪一個區在最新一次完整數據庫備份以后被修改過
我們做一個實驗,先做一次完整數據庫備份,然后再做一次完整數據庫備份,兩次完整數據庫備份之間,你都不要對數據庫做任何操作
然后在SSMS里輸入下面SQL語句
1 DBCC TRACEON(3604,-1) 2 DBCC PAGE([DLGPOS],1,6,3) --實際上是第7頁
你會看到就算你沒有對數據庫做任何操作,但是數據庫里的某些數據頁依然也會被修改
但是究竟修改了多少數據,沒有人知道,但是可以肯定的是SQLSERVER修改了一些數據,以至於在完整備份之后事務日志有所增加或減少
而在日志記錄里會看到
LOP_SET_BITS->LCX_DIFF_MAP 位圖
LOP_FILE_HDR_MODIFY->LCX_FILE_HEADER 數據庫文件頭部
LOP_MODIFY_ROW->LCX_BOOT_PAGE_CKPT 數據庫啟動頁
以上這些都是數據庫系統頁的修改
如果對DCM頁不是很理解可以看一下careyson里的文章
差異備份依靠一個BitMap進行維護,一個Bit對應一個區,自上次完整備份后,被修改的區會被置為1,
而BitMap中被置為1對應的區會被差異備份所備份。而到下一次完整備份后,BitMap中所有的Bit都會被重置為0。
http://www.cnblogs.com/CareySon/archive/2012/02/17/2355200.html#2702773
還有事務日志里有很多 LOP_LOCK_XACT->LCX_NULL ,估計這些操作是用來鎖住系統保留頁面的吧
-------------------------------------------------------------------------------------------------
至此,4個問題都有了答案,希望大家粽子節快樂!!
如果大家有什么需要補充的,或者文章有不正確的,歡迎大家拍磚!!