(4.19)深入理解SQLSERVER的日誌鏈
您真的理解了SQLSERVER的日誌鏈了嗎?
轉自:https://www.cnblogs.com/lyhabc/p/3460272.html
先感謝宋沄劍給本人指點迷津,還有郭忠輝童鞋今天在QQ群裏拋出的問題
這個問題跟宋沄劍討論了三天,再次感謝宋沄劍
一直以來,SQLSERVER提供了一個非常好的管理工具:SSMS
又因為這個管理工具太好了,所有操作的簡單化,以至於使我們中毒太深,
對於SQLSERVER內部的一些概念搞得不清不楚
比如這些概念:日誌備份鏈,備份日誌鏈,日誌鏈,備份鏈,備份集
大部分都是由於SSMS的界面所導致,有時候有些問題做一下實驗就可以驗證了,偏偏我們信賴了GUI
閱讀下文之前大家可以先看一下宋沄劍的文章
SQL Server CheckPoint的幾個誤區
再談SQL Server中日誌的的作用
SQL Server誤區30日談-Day20-破壞日誌備份鏈之後,需要一個完整備份來重新開始日誌鏈
先說清楚這些概念吧
SQLSERVER只有日誌鏈,備份記錄(有些人也叫備份鏈)本人覺得叫備份記錄更合適
下面三個東西說的都是同一樣東西
備份集=備份記錄=備份鏈
備份集:比如備份的集合,比如有對一個數據庫的完備1、差備、日備1、完備2、日備2,這些數據庫的備份的集合就是備份集
不過我更喜歡叫備份記錄
備份記錄實際上指 SELECT * FROM [msdb].[dbo].[backupset]
截斷日誌跟日誌鏈斷裂是否是同一樣東西?
截斷日誌跟日誌鏈斷裂不是同一樣東西
什麽是日誌鏈
其實大家可以把bak文件理解成一個壓縮包,完整備份和差異備份的時候會把數據和日誌一起帶進壓縮包,
而日誌備份的時候只會把日誌帶進壓縮包
我們先從一個實驗開始吧
測試環境:SQLSERVER2012 開發版
腳本
為了不產生額外的日誌,所以腳本裏面沒有select into語句,本來想select into進去臨時表再對臨時表進行排序
但是因為select into會產生額外的日誌,只有直接對fn_dblog進行排序了
創建數據庫
1 USE master 2 GO 3 --創建數據庫 4 CREATE DATABASE LogChainTest; 5 GO 6 --改為完整恢復模式 7 ALTER DATABASE LogChainTest SET RECOVERY FULL; 8 GO
查看當前的事務日誌
1 USE [LogChainTest] 2 GO 3 SELECT * FROM [sys].[fn_dblog](NULL,NULL) ORDER BY [Begin Time] ASC
進行完整備份
1 --第一個完整備份 2 DECLARE @strbackup NVARCHAR(100) 3 --改為日期加時間的 4 SET @strbackup = ‘C:\LogChainTest_full1_‘ 5 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 6 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 7 BACKUP DATABASE LogChainTest TO DISK =@strbackup WITH INIT,CHECKSUM ; 8 GO
查看bak文件中的事務日誌
1 SELECT * 2 FROM fn_dump_dblog(NULL, NULL, N‘DISK‘, 1, 3 N‘c:\LogChainTest_full1_20131206202536.bak‘, DEFAULT, 4 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 5 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 6 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 7 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 8 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 9 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 10 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 11 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 12 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 13 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 14 DEFAULT, DEFAULT)
我們再查看此時的數據庫事務日誌
1 USE [LogChainTest] 2 GO 3 SELECT * FROM [sys].[fn_dblog](NULL,NULL) ORDER BY [Begin Time] ASC
發現完整備份之後事務日誌比之前少了69-10=59行
我們發現bak文件中只記錄AllocUnitId,而不記錄表名,可能因為bak文件裏的日誌給SQLSERVER還原用的
而不是給用戶查看事務日誌用的,所以SQLSERVER幹脆不記錄表名了,以節省備份時間
看到這裏大家會有問題了,為什麼日誌會截斷了?完整備份之後事務日誌比之前少了69-10=59行
這裏只能說明SQLSERVER把一些跟本數據庫無關緊要的日誌截斷了,例如創建數據庫時候修改master數據庫的表
而不能說完整備份可以截斷日誌
而paul的文章給出了解釋:
If you switch recovery models to FULL or BULK_LOGGED, until you take the first full backup,
you are still essentially in the SIMPLE recovery model, and so the log will truncate on checkpoint.
文章地址:
http://www.sqlskills.com/blogs/paul/misconceptions-around-the-log-and-log-backups-how-to-convince-yourself/
問題:為什麼bak文件裏的日誌的最後的三條記錄會是
LOP_BEGIN_CKPT
LOP_XACT_CKPT
LOP_END_CKPT
我們用下圖來表示吧
這裏大家可以看一下宋沄劍的文章:再談SQL Server中日誌的的作用
將CheckPoint標記寫入日誌(標記中包含當前數據庫中活動的事務信息),並將Log Block寫入持久化存儲
我在開頭說過事務日誌中會放進去bak文件裏,但是並不是整個事務日誌文件裏的日誌記錄全部放進去
而是把(1)已經checkpoint了的 (2)LAZY WRITTER (3)EAGER WRITTER
還是看宋沄劍的文章吧,這麼復雜的過程我就不概括了:再談SQL Server中日誌的的作用
還有paul的文章:
Debunking a couple of myths around full database backups(揭穿一系列數據庫完備的誤區)
More on how much transaction log a full backup includes(數據庫完備包含了多少事務日誌)
實際上checkpoint和數據庫備份有著密切聯系,備份的時候SQLSERVER需要將哪些數據存入去bak文件
而在備份期間所新生成的事務和變化的數據要不要存入bak文件,這裏面比較復雜,就不詳細說了
不過有一點要說的是:在數據庫備份之前,數據庫引擎會自動執行checkpoint,以便在備份中包含對數據庫頁的全部更改。
我摘抄了網上的一些資料
1 http://blog.csdn.net/tjvictor/article/details/5209604 2 導致CheckPoint檢查點的事件: 1.在數據庫備份之前,數據庫引擎會自動執行checkpoint,以便在備份中包含對數據庫頁的全部更改。 3 4 2.日誌的活動部分超出了服務器在 recovery interval 服務器配置選項中指定的時間內可以恢復的大小。 5 6 3.日誌的 70% 已滿,並且數據庫處於日誌截斷模式。 7 8 當下列條件都為 TRUE 時,數據庫就處於日誌截斷模式:數據庫使用的是簡單恢復模式,並且在執行上一條引用數據庫的 BACKUP DATABASE 語句後,發生下列事件之一: 9 10 在數據庫中執行一項最小日誌記錄大容量復制操作或一條最條小日誌記錄的 WRITETEXT 語句。 11 12 執行一個在數據庫中添加或刪除文件的 ALTER DATABASE 語句。 13 14 4.停止服務器也會在服務器上的每個數據庫中發出一個檢查點命令。下列停止 SQL Server 的方法將為每個數據庫執行檢查點: 15 16 使用 SQL Server 配置管理器。 17 18 使用 SQL Server Management Studio。 19 20 使用 SHUTDOWN 語句。 21 -------------------------------------------------------------------------- 22 http://www.cnblogs.com/CareySon/p/3315041.html 23 5.將恢復間隔設置為1分鐘,意味著每1分鐘會對所有的數據庫做一次CheckPoint 24 25 錯誤。將恢復間隔設置為1分鐘不能想成建立一個Agent,每分鐘寫一個CheckPoint命令,這是兩碼事。這只是意味著每分鐘去檢查一次是否需要做CheckPoint,如果期間積累的日誌量足夠,才會對積累足夠日誌量的數據庫去做CheckPoint。即使中間積累了巨量的日誌,不到1分鐘也不會做CheckPoint。
那麽大家可以將bak文件裏的事務日誌當作為數據庫事務日誌
備份腳本
1 USE master 2 GO 3 --創建數據庫 4 CREATE DATABASE LogChainTest; 5 GO 6 --改為完整恢復模式 7 ALTER DATABASE LogChainTest SET RECOVERY FULL; 8 GO 9 10 11 12 13 14 15 --第一個完整備份 16 DECLARE @strbackup NVARCHAR(100) 17 --改為日期加時間的 18 SET @strbackup = ‘C:\LogChainTest_full1_‘ 19 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 20 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 21 BACKUP DATABASE LogChainTest TO DISK =@strbackup WITH INIT,CHECKSUM ; 22 GO 23 24 25 26 27 28 --第一個差異備份 29 USE LogChainTest 30 GO 31 CREATE TABLE tt(id INT) 32 INSERT INTO tt 33 SELECT 1 34 DECLARE @strbackup NVARCHAR(100) 35 --改為日期加時間的 36 SET @strbackup = ‘C:\LogChainTest_diff_‘ 37 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 38 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 39 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT, DIFFERENTIAL; 40 GO 41 42 43 44 --第一個日誌備份 45 USE LogChainTest 46 GO 47 INSERT INTO tt 48 SELECT 2 49 DECLARE @strbackup NVARCHAR(100) 50 --改為日期加時間的 51 SET @strbackup = ‘C:\LogChainTest_log1_‘ 52 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 53 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 54 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT; 55 GO 56 57 58 59 60 --第二個完整備份 61 USE master 62 GO 63 DECLARE @strbackup NVARCHAR(100) 64 --改為日期加時間的 65 SET @strbackup = ‘C:\LogChainTest_full2_‘ 66 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 67 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 68 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT; 69 GO 70 71 72 --第二個日誌備份 73 USE LogChainTest 74 GO 75 INSERT INTO tt 76 SELECT 3 77 DECLARE @strbackup NVARCHAR(100) 78 --改為日期加時間的 79 SET @strbackup = ‘C:\LogChainTest_log2_‘ 80 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 81 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 82 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT; 83 GO
備份策略:完整備份1-》差異備份-》日誌備份1-》完整備份2-》日誌備份2
還原腳本
1 --差異備份和日誌備份1打亂 2 USE master 3 GO 4 --還原第一個完整備份 5 RESTORE DATABASE LogChainTest FROM DISK=‘C:\LogChainTest_full1_20131206230857.bak‘ 6 WITH REPLACE ,CHECKSUM, NORECOVERY 7 GO 8 9 --還原第一個日誌備份 10 RESTORE LOG LogChainTest FROM DISK=‘c:\LogChainTest_diff_20131206230920.bak‘ 11 WITH NORECOVERY 12 GO 13 14 --還原差異備份 15 RESTORE DATABASE LogChainTest FROM DISK=‘c:\LogChainTest_diff_20131205222718.bak‘ 16 WITH NORECOVERY 17 GO 18 19 消息 3136,級別 16,狀態 3,第 1 行 20 無法還原此差異備份,因為該數據庫尚未還原到正確的早期狀態。 21 消息 3013,級別 16,狀態 1,第 1 行 22 RESTORE DATABASE 正在異常終止。 23 24 25 26 27 --還原第二個日誌備份,沒有報錯 28 RESTORE LOG LogChainTest FROM DISK=‘C:\LogChainTest_log2_20131206230927.bak‘ 29 WITH RECOVERY 30 GO 31 32 33 34 35 --可以查詢出id列有三行記錄 36 USE [LogChainTest] 37 GO 38 SELECT * FROM [dbo].[tt]
上面的還原腳本,我先還原日誌備份1,再還原差異備份結果就報錯了
1 消息 3136,級別 16,狀態 3,第 1 行 2 無法還原此差異備份,因為該數據庫尚未還原到正確的早期狀態。 3 消息 3013,級別 16,狀態 1,第 1 行 4 RESTORE DATABASE 正在異常終止。
還有,為什麼不用還原完整備份2數據也沒有丟失??
我們每次備份的時候,無論是完備、差備、日備都會把日誌拷貝到bak文件裏
而拷貝的時候會有一個last lsn確保日誌順序
當我先還原日誌備份1,然後還原差異備份的時候因為last lsn的順序不對所以就報錯了
為什麼不用還原完整備份2數據也沒有丟失??
這裏先說一下完備、差備、日備的大概方式
完備:復制數據和少量的log到bak
差備:復制有差異的數據和少量的log到bak
日備:不復制數據,如果是第一次日備,會把所有的log復制到bak,如果是第二次日備,會把自上一次日備到這次日備的log復制到bak
paul的文章裏有解釋:
http://www.sqlskills.com/blogs/paul/misconceptions-around-the-log-and-log-backups-how-to-convince-yourself/
A log backup is *ALL* the log generated since the last log backup
備份策略:完整備份1-》差異備份-》日誌備份1-》完整備份2-》日誌備份2
我們沒有還原完整備份2(相當於丟失了完整備份2),我們的還原順序是
還原完整備份1(復制數據,根據redo/undo log保證事務一致性)
還原差異備份(復制差異數據,根據redo/undo log保證事務一致性)
還原日誌備份1(數據全靠redo/undo log來恢復,根據redo/undo log保證事務一致性)
還原日誌備份2(數據全靠redo/undo log來恢復,根據redo/undo log保證事務一致性)
因為日誌備份2裏面已經包含了從日誌備份1到日誌備份2的所有log,所以SQLSERVER可以憑借這些log來把數據恢復
而日誌備份1裏面已經包含了從完整備份1到日誌備份1的所有log
所以,按理說,我們只需要還原完備1,日備1,日備2就可以恢復全部數據
測試:
我們使用下面備份腳本和還原腳本,看一下不還原日誌備份1,直接還原日誌備份2看有沒有問題
備份腳本
View Code還原腳本
View Code插入的數據太少,日誌太少,搞得文件的size不那麽明顯
結果報錯
1 消息 4305,級別 16,狀態 1,第 2 行 2 此備份集中的日誌開始於 LSN 35000000017200001,該 LSN 太晚,無法應用到數據庫。可以還原包含 LSN 35000000008600001 的較早的日誌備份。 3 消息 3013,級別 16,狀態 1,第 2 行 4 RESTORE LOG 正在異常終止。
因為沒有還原日誌備份1,缺少了完備1到日備1之間的日誌,所以就報錯了
我們使用下面的腳本來進行還原,只還原完備1,日備1,日備2
View Code這次成功了,數據都沒有丟失,那麽說明我丟失了差異備份、完整備份2也沒有關系
如果我丟失了日備1、差備、完備2,只有完備1和日備2,那麽這個時候你只能祈禱了,你只能還原完備1
差備、日備1、完備2、日備2的數據都已經丟失
BAK文件中日誌數量的多少
我剛才說
完備:復制數據和少量的log到bak
差備:復制有差異的數據和少量的log到bak
日備:不復制數據,如果是第一次日備,會把所有的log復制到bak,如果是第二次日備,會把自上一次日備到這次日備的log復制到bak
我怎麼看出來的?
測試:
我們看一下每次備份完畢後,bak文件裏面的日誌數量
1 USE master 2 GO 3 SELECT * 4 FROM fn_dump_dblog(NULL, NULL, N‘DISK‘, 1, 5 N‘c:\LogChainTest_full1_20131207102535.bak‘, DEFAULT, 6 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 7 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 8 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 9 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 10 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 11 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 12 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 13 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 14 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 15 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 16 DEFAULT, DEFAULT)
完備1
差備
日備1
完備2
日備2
在完備2的時候bak中的日誌只有44行,說明完整備份只存儲一些必要的日誌,不是所有日誌都存儲
完備存儲這些日誌的作用是在還原的時候根據這些log去redo/undo 保證事務一致性,所以只會寫入少量日誌
因為完備和差備都是復制數據,所以就沒有必要像日備那樣全部事務日誌都復制到bak裏面
而日備2為什麼只有73行記錄,因為在日備1的時候SQLSERVER已經截斷了事務日誌,日備2的日誌就像我前面說的
如果是第二次日備,會把自上一次日備到這次日備的log復制到bak
如果我們不想在backup log 的時候截斷事務日誌,可以使用NO_TRUNCATE和COPY_ONLY這兩個backup option
備份腳本 NO_TRUNCATE
View Code我們看一下第一個日誌備份和第二個日誌備份之後,數據庫事務日誌和bak文件裏面的日誌數量
日備1 數據庫日誌
日備1 bak文件日誌
日備2 數據庫日誌
日備2 bak文件日誌
備份腳本 COPY_ONLY
View Code我們看一下第一個日誌備份和第二個日誌備份之後,數據庫事務日誌和bak文件裏面的日誌數量
日備1 數據庫日誌
日備1 bak文件日誌
日備2 數據庫日誌
日備2 bak文件日誌
大家可以看一下這篇帖子
完整備份能截斷日誌嗎?
差異備份的作用
既然SQLSERVER靠bak文件裏的日誌來進行redo/undo,就像上面說的那樣,靠完備1,日備1,日備2就可以恢復所有數據
那麽差異備份有什麽用呢??
為什麼要有差異備份呢?
差異備份是為了RTO(Recovery Time Objective)
詳見:http://blog.sina.com.cn/s/blog_59388e440100oq52.html
如果只做日誌備份RTO有可能保證不了
之前說過:差備:復制有差異的數據和少量的log到bak
差異備份:靠DCM頁面復制粘貼把bak文件裏的數據復制粘貼到mdf文件的數據頁
日誌備份:redo/undo log
這兩個選項肯定是復制粘貼在速度上占優勢
當還原了差異備份之後,SQLSERVER根據差異備份時候的log使數據庫保存了事務一致性,然後還原日備1
還原日備1的時候,SQLSERVER根據差備的last lsn,只需要redo/undo 差備-》日備1這段時間的log就可以了
這樣節省了時間,不用redo/undo 完備1-》日備1這段時間的log,從而保證了RTO
而日誌備份,本人覺得是為了保證RPO(Recovery Point Objective)
被神化的日誌鏈
實際上日誌鏈就是我上面說的數據庫事務日誌,只是備份的時候,SQLSERVER把事務日誌放進去bak文件裏
我畫了幾張圖
上面那個實驗的理解圖
-------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------
大家可以使用下面兩個SQL語句
1 SELECT * FROM [sys].[fn_dblog]() 2 SELECT * FROM [sys].[fn_dump_dblog]()
在完整備份、差異備份、日誌備份測試一下在哪種備份類型下日誌會被截斷,截斷的意思(數據庫事務日誌的記錄數比bak文件裏的日誌記錄數少)
就像我在開頭做的那個實驗一樣
GUI界面下,默認就是截斷事務日誌,很多人都以為截斷事務日誌要加XX backup option才可以截斷
如何查看last_log_backup_lsn這個值
select last_log_backup_lsn from sys.database_recovery_status WHERE [database_id]=DB_ID(‘test‘)
last_log_backup_lsn這個值在boot page的last_log_backup_lsn項裏保存,表示對數據庫執行最後一次事務日誌備份中的最大LSN號,也可以說是下一次事務日誌備份的開始LSN
實驗
USE [test] select last_log_backup_lsn from sys.database_recovery_status WHERE [database_id]=DB_ID(‘test‘) BACKUP DATABASE [test] TO DISK =‘D:\DBBackup\testfull.bak‘ USE [test] select last_log_backup_lsn from sys.database_recovery_status WHERE [database_id]=DB_ID(‘test‘) --34000000031500001 BACKUP LOG [test] TO DISK =‘D:\DBBackup\testlog1.bak‘ USE [test] select last_log_backup_lsn from sys.database_recovery_status WHERE [database_id]=DB_ID(‘test‘) --34000000032300001 BACKUP LOG [test] TO DISK =‘D:\DBBackup\testlog2.bak‘ USE [test] select last_log_backup_lsn from sys.database_recovery_status WHERE [database_id]=DB_ID(‘test‘) --34000000032800001 USE [master] RESTORE DATABASE [test] FROM DISK = N‘D:\DBBackup\testfull.bak‘ WITH FILE = 1, MOVE N‘test‘ TO N‘D:\MSSQL\test.mdf‘, MOVE N‘test_log‘ TO N‘D:\MSSQL\test_log.ldf‘, NOUNLOAD,NORECOVERY , STATS = 5 GO USE [master] RESTORE DATABASE [test] FROM DISK = N‘D:\DBBackup\testlog2.bak‘ WITH FILE = 1, NOUNLOAD,NORECOVERY , STATS = 5 GO 消息 4305,級別 16,狀態 1,第 2 行 此備份集中的日誌開始於 LSN 34000000032300001,該 LSN 太晚,無法應用到數據庫。可以還原包含 LSN 34000000031500001 的較早的日誌備份。 消息 3013,級別 16,狀態 1,第 2 行 RESTORE DATABASE 正在異常終止。
可以看到,還原日誌備份的時候是讀取boot page的 last_log_backup_lsn的值來判斷日誌序列,此處應該先還原LSN 34000000032300001的日誌備份
日誌鏈斷裂的情況
paul的文章說了 SQL Server誤區30日談-Day20-破壞日誌備份鏈之後,需要一個完整備份來重新開始日誌鏈
下面這幾種操作都有可能引起日誌鏈斷裂
(1)由完整恢復模式或大容量事務日誌恢復模式轉為簡單恢復模式
(2)從數據庫鏡像進行恢復
(3)備份日誌時指定了NO_LOG 或 WITH TRUNCATE_ONLY(還好在SQL Server 2008中這個選項被取消了)
本人覺得日誌鏈斷裂是一個非常專業的名稱
很多人以為,我做了下面的備份策略:完備1-》差備-》日備1-》完備2-》日備2
如果差備丟失了就認為是日誌鏈斷裂了,數據庫不能還原到日備1
其實日誌鏈斷裂通俗的理解就是:沒有將日誌放進去bak文件裏
怎樣的情況才叫 沒有將日誌放進去bak文件裏呢??
我們知道當我們進行完備、差備、日備的時候都會把日誌放進去bak文件裏
情況一:
當你將數據庫恢復模式由完整恢復模式或大容量事務日誌恢復模式轉為簡單恢復模式
大家還是先看一下這篇文章吧:SQL Server日誌在簡單恢復模式下的角色
簡單恢復模式的機制是:文章中有這樣一句話
“在簡單恢復模式下,每一次CheckPoint,都會去檢查是否有日誌可以截斷,如果有inactive的VLF時,
CheckPoint都會將可截斷部分進行截斷,並將MinLSN向後推”
簡單來講就是簡單恢復模式不是在backup log DB 的情況下截斷日誌
而是在checkpoint的時候截斷日誌,那麽既然在checkpoint的時候已經截斷了日誌,在備份的時候數據庫的事務日誌
就沒有不活動日誌用於歸檔(把日誌放進去bak文件)
我們使用下面的腳本進行日誌備份就會報錯
View Code1 消息 4208,級別 16,狀態 1,第 6 行 2 當恢復模式為 SIMPLE 時,不允許使用 BACKUP LOG 語句。請使用 BACKUP DATABASE 或用 ALTER DATABASE 更改恢復模式。 3 消息 3013,級別 16,狀態 1,第 6 行 4 BACKUP LOG 正在異常終止。
但是完整備份和差異備份則不受影響
備份腳本
View Code完整備份和差異備份可以用下圖來理解,少量活動日誌放到bak文件裏用於保證事務一致性
完整備份差異備份時依然會將last lsn寫入bak文件裏
還原腳本
View Code先還原差備2再還原差備1就報錯
1 消息 4305,級別 16,狀態 1,第 1 行 2 此備份集中的日誌開始於 LSN 35000000028200004,該 LSN 太晚,無法應用到數據庫。可以還原包含 LSN 35000000024100001 的較早的日誌備份。 3 消息 3013,級別 16,狀態 1,第 1 行 4 RESTORE LOG 正在異常終止。
實際上完整和差備都是復制數據和少量活動日誌到bak裏面,所以還原是沒有問題的
但是日備不同,日備需要將完備到第一個日備的log,或者自上一次日備到這次日備的log全部放進去bak文件
因為簡單恢復模式是一checkpoint就截斷日誌,根本無辦法保存完整的log,所以是不允許日備的
情況二:
備份日誌時指定了NO_LOG 或 WITH TRUNCATE_ONLY(還好在SQL Server 2008中這個選項被取消了)
TRUNCATE_ONLY的意思是只截斷日誌不備份日誌到bak文件裏(只能用在backup log語句)
NO_LOG的意思是不備份日誌到bak文件裏(不備份日誌到bak文件裏意味著不能backup log,當然也意味著不能截斷日誌)
我們轉到SQLSERVER2005
備份腳本
NO_LOG
View Code備份策略:完備-》差備-》日備
大家可以看到執行日備的時候沒有產生bak文件
查看bak文件裏的日誌
View Code完備0行
差備0行
其實可以用下圖來理解
bak文件裏只有數據沒有日誌,連保證事務一致性的少量的活動日誌都沒有
備份腳本
TRUNCATE_ONLY
View Code備份策略:日備
大家可以看到執行日備的時候沒有產生bak文件
查看日誌備份前數據庫事務日誌
查看日誌備份前數據庫事務日誌
其實可以用下圖來理解
truncate_only只是截斷了日誌,沒有產生bak文件,更不用說備份日誌到bak文件裏面了
我們再做一個實驗
備份腳本
View Code當我進行到第三個日誌備份的時候就報錯了
1 (1 行受影響) 2 消息 4214,級別 16,狀態 1,第 8 行 3 無法執行 BACKUP LOG,因為當前沒有數據庫備份。 4 消息 3013,級別 16,狀態 1,第 8 行 5 BACKUP LOG 正在異常終止。
可以用下圖來理解
(2)從數據庫鏡像進行恢復這種情況由於沒有研究過就不說了
小結:
截斷日誌跟日誌鏈斷裂不是同一樣東西!!
截斷日誌:針對數據庫事務日誌
日誌鏈斷裂:針對bak裏的日誌
大家不要混淆了
不神秘的事務日誌尾部
當你的數據庫損壞或置疑,你可以嘗試進行尾日誌備份
尾日誌指的是哪個地方? 為什麼要進行尾日誌備份?
假如有下面的腳本
View Code在第二個日誌備份之後還插入了一條記錄到tt表
如果這時候數據庫損壞,那麽你可以備份事務日誌尾部,把最後的事務日誌記錄(INSERT INTO tt
SELECT 3)放進去bak文件裏,然後進行還原數據庫
使用下面腳本,備份日誌尾部
註意:數據庫離線的狀態下是不能備份日誌尾部的!!
網上很多文章都誤導人
由於數據庫 ‘LogChainTest‘ 離線,無法打開該數據庫
1 --備份日誌尾部 2 USE master 3 GO 4 DECLARE @strbackup NVARCHAR(100) 5 --改為日期加時間的 6 SET @strbackup = ‘C:\LogChainTest_log_tail_‘ 7 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 8 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 9 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT,NORECOVERY; 10 GO
這時候數據庫顯示正在還原
還原腳本
1 ------------------------------------------------------------- 2 --還原 3 USE master 4 GO 5 --還原第一個完整備份 6 RESTORE DATABASE LogChainTest FROM DISK=‘C:\LogChainTest_full1_20131207145154.bak‘ 7 WITH REPLACE ,CHECKSUM, NORECOVERY 8 GO 9 10 --還原第一個日誌備份 11 RESTORE LOG LogChainTest FROM DISK=‘c:\LogChainTest_log1_20131207145157.bak‘ 12 WITH NORECOVERY 13 GO 14 15 --還原第二個日誌備份 16 RESTORE LOG LogChainTest FROM DISK=‘c:\LogChainTest_log2_20131207145158.bak‘ 17 WITH NORECOVERY 18 GO 19 20 --還原日誌尾部 21 RESTORE DATABASE LogChainTest FROM DISK=‘c:\LogChainTest_log_tail_20131207145333.bak‘ 22 WITH RECOVERY 23 GO
數據沒有丟失,可以查出最後一條插入到tt表的記錄3
回答開頭的問題:尾日誌指的是哪個地方? 為什麼要進行尾日誌備份?
其實備份日誌尾部,大家可以把他作為普通的事務日誌備份
如果遇到錯誤還可以加上CONTINUE_AFTER_ERROR 的backup option
1 --備份日誌尾部 2 USE master 3 GO 4 DECLARE @strbackup NVARCHAR(100) 5 --改為日期加時間的 6 SET @strbackup = ‘C:\LogChainTest_log_tail_‘ 7 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 8 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 9 BACKUP LOG LogChainTest TO DISK = @strbackup WITH CONTINUE_AFTER_ERROR,NORECOVERY; 10 GO
備份記錄
實際上這個[msdb].[dbo].[backupset]表的作用只是給你看做了哪些備份
1 SELECT * FROM [msdb].[dbo].[backupset]
使用GUI的時候,我發現了一個問題
當我用上面的備份策略 完備1-》差備-》日備1-》完備2-》日備2
當我完成日備1的時候,還原界面和backupset表的界面如下
當我再進行完備2和日備2的時候,還原界面變成了下面的樣子
backupset表依然能顯示出備份記錄
很多人就認為備份鏈斷裂了,日誌鏈斷裂,備份日誌鏈斷裂,日誌備份鏈斷裂
這個表的記錄是刪除不了的
1 USE [msdb] 2 GO 3 DELETE FROM [msdb].[dbo].[backupset] 4 TRUNCATE TABLE [msdb].[dbo].[backupset]
1 消息 547,級別 16,狀態 0,第 1 行 2 DELETE 語句與 REFERENCE 約束"FK__backupfil__backu__473C8FC7"沖突。該沖突發生於數據庫"msdb",表"dbo.backupfilegroup", column ‘backup_set_id‘。 3 語句已終止。 4 消息 4712,級別 16,狀態 1,第 2 行 5 無法截斷表 ‘msdb.dbo.backupset‘,因為該表正由 FOREIGN KEY 約束引用。
這個表記錄了在備份的時候的lsn號
可以根據paul的文章做一些實驗
Debunking a couple of myths around full database backups
我們做一個實驗,先做一個完整備份
View Codebackupset表就會產生一條記錄
我們將bak文件刪除
用GUI來還原數據庫
結果:
他們的關系
1 USE [msdb] 2 GO 3 SELECT * FROM [dbo].[backupfile] 4 SELECT * FROM [dbo].[backupfilegroup] 5 SELECT * FROM [dbo].[backupset] 6 SELECT * FROM [sys].[backup_devices] 7 SELECT * FROM [dbo].[backupmediafamily] 8 SELECT * FROM [dbo].[backupmediaset]
每次備份的記錄都記錄在這些表裏面,還原的時候SSMS讀取這些表的記錄,讓你勾上幾個選項就可以還原數據庫了(非常傻瓜)
大家不要以為SQLSERVER在還原數據庫的時候依靠[msdb].[dbo].[backupset]表的lsn去對比備份順序
大家可以試想一下:
你的數據庫備份了3次,有3個備份記錄保存在backupset表
那麽當你把數據庫分離附加到別的sql實例的時候,你也可以還原你之前的備份
為什麼呢??
因為還原的時候只去數據庫的事務日誌去對比last lsn,是不依靠外部的其他的數據的而且也不需要依靠
如果還不明白的話,大家再看一下我上面貼出來的圖片吧o(∩_∩)o
總結
一直以來本人對SQLSERVER的備份還原機制都不是很熟悉,通過跟宋沄劍的討論讓本人重新認識SQLSERVER的備份、還原
失眠了兩晚,今晚可以吃一個好的水餃了
相關內容:
http://social.technet.microsoft.com/Forums/zh-CN/7e531652-1f00-441b-ae20-871b3e9573c8/sql-server-2005?forum=sqlserverzhchs
http://www.sqlskills.com/blogs/paul/misconceptions-around-the-log-and-log-backups-how-to-convince-yourself/
http://www.sqlskills.com/blogs/paul/more-on-how-much-transaction-log-a-full-backup-includes/
http://www.sqlskills.com/blogs/paul/debunking-a-couple-of-myths-around-full-database-backups/
淺談SQL Server中的事務日誌(一)----事務日誌的物理和邏輯構架
淺談SQL Server中的事務日誌(五)----日誌在高可用和災難恢復中的作用
上面的結論都經過我測試,希望大家可以指出本人的錯處o(∩_∩)o
您們也可以動手測試一下我說的是不是真的o(∩_∩)o
如有不對的地方,歡迎大家拍磚o(∩_∩)o
2013-12-7 補充:
大家不要誤解了,數據庫事務日誌截斷的意思不是說把不活動日誌部分刪除了,而是把這些日誌清空了
等待重用,除非你收縮事務日誌,不然這些日誌空間(VLF)只會等待重用
2013-12-8 補充:
還原日誌備份的時候使用restore log 或restore database都是一樣的
而還原差異備份的時候使用restore log就會報錯
1 USE master 2 GO 3 --創建數據庫 4 CREATE DATABASE LogChainTest; 5 GO 6 --改為完整恢復模式 7 ALTER DATABASE LogChainTest SET RECOVERY FULL; 8 GO 9 10 11 -------------------------------------------------------------------- 12 --備份 13 --第一個完整備份 14 USE master 15 GO 16 DECLARE @strbackup NVARCHAR(100) 17 --改為日期加時間的 18 SET @strbackup = ‘C:\LogChainTest_full1_‘ 19 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 20 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 21 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT; 22 GO 23 24 25 26 --第一個日誌備份 27 USE LogChainTest 28 GO 29 CREATE TABLE tt(id INT) 30 INSERT INTO tt 31 SELECT 1 32 DECLARE @strbackup NVARCHAR(100) 33 --改為日期加時間的 34 SET @strbackup = ‘C:\LogChainTest_log1_‘ 35 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 36 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 37 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT; 38 GO 39 40 41 42 --第一個差異備份 43 USE LogChainTest 44 GO 45 INSERT INTO tt 46 SELECT 2 47 DECLARE @strbackup NVARCHAR(100) 48 --改為日期加時間的 49 SET @strbackup = ‘C:\LogChainTest_diff1_‘ 50 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), ‘-‘, ‘‘), ‘ ‘, 51 ‘‘), ‘:‘, ‘‘) + ‘.bak‘ 52 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT, DIFFERENTIAL; 53 GO 54 ------------------------------------------------------------------------ 55 56 57 -------------------------------------------------------------------------- 58 --還原 59 60 61 62 USE master 63 GO 64 --只有完備備份還原才可以移動數據庫文件 65 RESTORE DATABASE LogChainTest FROM DISK=‘C:\LogChainTest_full1_20131208100145.bak‘ 66 WITH MOVE ‘LogChainTest‘ TO ‘E:\LogChainTest.mdf‘, 67 MOVE ‘LogChainTest_log‘ TO ‘E:\LogChainTest_log.ldf‘, 68 NORECOVERY ,REPLACE 69 GO 70 71 72 RESTORE LOG LogChainTest FROM DISK=‘c:\LogChainTest_log1_20131208100151.bak‘ 73 WITH MOVE ‘LogChainTest‘ TO ‘E:\LogChainTest.mdf‘, 74 MOVE ‘LogChainTest_log‘ TO ‘E:\LogChainTest_log.ldf‘, 75 NORECOVERY 76 GO 77 ------------------------------------------------- 78 RESTORE DATABASE LogChainTest FROM DISK=‘c:\LogChainTest_log1_20131208100151.bak‘ 79 WITH MOVE ‘LogChainTest‘ TO ‘E:\LogChainTest.mdf‘, 80 MOVE ‘LogChainTest_log‘ TO ‘E:\LogChainTest_log.ldf‘, 81 NORECOVERY 82 GO 83 84 85 RESTORE LOG LogChainTest FROM DISK=‘c:\LogChainTest_diff1_20131208100251.bak‘ 86 WITH MOVE ‘LogChainTest‘ TO ‘E:\LogChainTest.mdf‘, 87 MOVE ‘LogChainTest_log‘ TO ‘E:\LogChainTest_log.ldf‘, 88 RECOVERY 89 GO 90 ---------------------------------------------------------- 91 RESTORE DATABASE LogChainTest FROM DISK=‘c:\LogChainTest_diff1_20131208100251.bak‘ 92 WITH MOVE ‘LogChainTest‘ TO ‘E:\LogChainTest.mdf‘, 93 MOVE ‘LogChainTest_log‘ TO ‘E:\LogChainTest_log.ldf‘, 94 RECOVERY 95 GO 96 97 USE [LogChainTest] 98 GO 99 SELECT * FROM [dbo].[tt]
2016-8-2 補充:
MinLSN是當前所有活動事務的開始LSN和checkpoint的開始LSN中的較小者
MinLSN的作用是記錄當前數據庫需要恢復時,可能回滾的上限
實例恢復和介質恢復
實例恢復和fn_dblog從minlsn開始顯示
bootpage-》數據庫最後一個checkpoint的lsn-》ldf裏面定位到數據庫最後一個checkpoint開始的那條日誌記錄-》讀取minlsn
頁頭最後一次修改LSN(m_lsn)和dbi_checkptLSN進行對比
《SQL Server2008數據庫技術內幕》
(4.19)深入理解SQLSERVER的日誌鏈