先感謝宋沄劍給本人指點迷津,還有郭忠輝童鞋今天在QQ羣裏拋出的問題html
這個問題跟宋沄劍討論了三天,再次感謝宋沄劍sql
一直以來,SQLSERVER提供了一個很是好的管理工具:SSMS數據庫
又由於這個管理工具太好了,全部操做的簡單化,以致於使咱們中毒太深,服務器
對於SQLSERVER內部的一些概念搞得不清不楚ide
好比這些概念:日誌備份鏈,備份日誌鏈,日誌鏈,備份鏈,備份集工具
大部分都是因爲SSMS的界面所致使,有時候有些問題作一下實驗就能夠驗證了,恰恰咱們信賴了GUIsqlserver
閱讀下文以前你們能夠先看一下宋沄劍的文章測試
SQL Server誤區30日談-Day20-破壞日誌備份鏈以後,須要一個完整備份來從新開始日誌鏈
先說清楚這些概念吧
SQLSERVER只有日誌鏈,備份記錄(有些人也叫備份鏈)本人以爲叫備份記錄更合適
下面三個東西說的都是同同樣東西
備份集=備份記錄=備份鏈
備份集:好比備份的集合,好比有對一個數據庫的完備一、差備、日備一、完備二、日備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.
文章地址:
問題:爲什麼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的文章裏有解釋:
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看有沒有問題
備份腳本
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 LogChainTest 62 GO 63 INSERT INTO tt 64 SELECT 3 UNION ALL 65 SELECT 4 66 DECLARE @strbackup NVARCHAR(100) 67 --改成日期加時間的 68 SET @strbackup = 'C:\LogChainTest_full2_' 69 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 70 ''), ':', '') + '.bak' 71 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT; 72 GO 73 74 75 --第二個日誌備份 76 USE LogChainTest 77 GO 78 INSERT INTO tt 79 SELECT 5 80 DECLARE @strbackup NVARCHAR(100) 81 --改成日期加時間的 82 SET @strbackup = 'C:\LogChainTest_log2_' 83 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 84 ''), ':', '') + '.bak' 85 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT; 86 GO
還原腳本
1 USE master 2 GO 3 --還原第一個完整備份 4 RESTORE DATABASE LogChainTest FROM DISK='C:\LogChainTest_full1_20131207102535.bak' 5 WITH REPLACE ,NORECOVERY 6 GO 7 8 9 --還原第二個日誌備份 10 RESTORE LOG LogChainTest FROM DISK='C:\LogChainTest_log2_20131207102602.bak' 11 WITH RECOVERY 12 GO
插入的數據太少,日誌太少,搞得文件的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
1 USE master 2 GO 3 --還原第一個完整備份 4 RESTORE DATABASE LogChainTest FROM DISK='C:\LogChainTest_full1_20131207102535.bak' 5 WITH REPLACE ,NORECOVERY 6 GO 7 8 --還原第一個日誌備份 9 RESTORE LOG LogChainTest FROM DISK='C:\LogChainTest_log1_20131207102542.bak' 10 WITH NORECOVERY 11 GO 12 13 --還原第二個日誌備份 14 RESTORE LOG LogChainTest FROM DISK='C:\LogChainTest_log2_20131207102602.bak' 15 WITH RECOVERY 16 GO 17 18 USE [LogChainTest] 19 GO 20 SELECT * FROM tt
此次成功了,數據都沒有丟失,那麼說明我丟失了差別備份、完整備份2也沒有關係
若是我丟失了日備一、差備、完備2,只有完備1和日備2,那麼這個時候你只能祈禱了,你只能還原完備1
差備、日備一、完備二、日備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
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,NO_TRUNCATE; 55 GO 56 57 USE [LogChainTest] 58 GO 59 SELECT * FROM [sys].[fn_dblog](NULL,NULL) ORDER BY [Begin Time] ASC 60 61 62 63 --第二個完整備份 64 USE LogChainTest 65 GO 66 INSERT INTO tt 67 SELECT 3 UNION ALL 68 SELECT 4 69 DECLARE @strbackup NVARCHAR(100) 70 --改成日期加時間的 71 SET @strbackup = 'C:\LogChainTest_full2_' 72 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 73 ''), ':', '') + '.bak' 74 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT; 75 GO 76 77 78 --第二個日誌備份 79 USE LogChainTest 80 GO 81 INSERT INTO tt 82 SELECT 5 83 DECLARE @strbackup NVARCHAR(100) 84 --改成日期加時間的 85 SET @strbackup = 'C:\LogChainTest_log2_' 86 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 87 ''), ':', '') + '.bak' 88 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT,NO_TRUNCATE; 89 GO 90 91 92 93 94 95 96 USE master 97 GO 98 SELECT * 99 FROM fn_dump_dblog(NULL, NULL, N'DISK', 1, 100 N'c:\LogChainTest_full1_20131207102535.bak', DEFAULT, 101 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 102 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 103 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 104 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 105 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 106 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 107 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 108 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 109 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 110 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 111 DEFAULT, DEFAULT)
咱們看一下第一個日誌備份和第二個日誌備份以後,數據庫事務日誌和bak文件裏面的日誌數量
日備1 數據庫日誌
日備1 bak文件日誌
日備2 數據庫日誌
日備2 bak文件日誌
備份腳本 COPY_ONLY
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,COPY_ONLY; 55 GO 56 57 USE [LogChainTest] 58 GO 59 SELECT * FROM [sys].[fn_dblog](NULL,NULL) ORDER BY [Begin Time] ASC 60 61 62 63 --第二個完整備份 64 USE LogChainTest 65 GO 66 INSERT INTO tt 67 SELECT 3 UNION ALL 68 SELECT 4 69 DECLARE @strbackup NVARCHAR(100) 70 --改成日期加時間的 71 SET @strbackup = 'C:\LogChainTest_full2_' 72 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 73 ''), ':', '') + '.bak' 74 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT; 75 GO 76 77 78 --第二個日誌備份 79 USE LogChainTest 80 GO 81 INSERT INTO tt 82 SELECT 5 83 DECLARE @strbackup NVARCHAR(100) 84 --改成日期加時間的 85 SET @strbackup = 'C:\LogChainTest_log2_' 86 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 87 ''), ':', '') + '.bak' 88 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT,COPY_ONLY; 89 GO 90 91 USE [LogChainTest] 92 GO 93 SELECT * FROM [sys].[fn_dblog](NULL,NULL) ORDER BY [Begin Time] ASC
咱們看一下第一個日誌備份和第二個日誌備份以後,數據庫事務日誌和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文件)
咱們使用下面的腳本進行日誌備份就會報錯
1 USE master 2 GO 3 CREATE DATABASE LogChainTest; 4 GO 5 ALTER DATABASE LogChainTest SET RECOVERY SIMPLE; 6 GO 7 8 DECLARE @strbackup NVARCHAR(100) 9 --改成日期加時間的 10 SET @strbackup = 'C:\LogChainTest_log_' 11 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 12 ''), ':', '') + '.bak' 13 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT; 14 GO
1 消息 4208,級別 16,狀態 1,第 6 行 2 當恢復模式爲 SIMPLE 時,不容許使用 BACKUP LOG 語句。請使用 BACKUP DATABASE 或用 ALTER DATABASE 更改恢復模式。 3 消息 3013,級別 16,狀態 1,第 6 行 4 BACKUP LOG 正在異常終止。
可是完整備份和差別備份則不受影響
備份腳本
1 USE master 2 GO 3 CREATE DATABASE LogChainTest; 4 GO 5 ALTER DATABASE LogChainTest SET RECOVERY SIMPLE; 6 GO 7 8 --第一個完整備份 9 DECLARE @strbackup NVARCHAR(100) 10 --改成日期加時間的 11 SET @strbackup = 'C:\LogChainTest_full1_' 12 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 13 ''), ':', '') + '.bak' 14 BACKUP DATABASE LogChainTest TO DISK =@strbackup WITH INIT,CHECKSUM ; 15 GO 16 17 18 19 --第一個差別備份 20 USE LogChainTest 21 GO 22 CREATE TABLE tt(id int) 23 INSERT INTO tt 24 SELECT 1 25 DECLARE @strbackup NVARCHAR(100) 26 --改成日期加時間的 27 SET @strbackup = 'C:\LogChainTest_diff1_' 28 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 29 ''), ':', '') + '.bak' 30 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT, DIFFERENTIAL; 31 GO 32 33 --第二個差別備份 34 USE LogChainTest 35 GO 36 INSERT INTO tt 37 SELECT 9 38 DECLARE @strbackup NVARCHAR(100) 39 --改成日期加時間的 40 SET @strbackup = 'C:\LogChainTest_diff2_' 41 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 42 ''), ':', '') + '.bak' 43 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT, DIFFERENTIAL; 44 GO
完整備份和差別備份能夠用下圖來理解,少許活動日誌放到bak文件裏用於保證事務一致性
完整備份差別備份時依然會將last lsn寫入bak文件裏
還原腳本
1 --還原第一個完整備份 2 RESTORE DATABASE LogChainTest FROM DISK='C:\LogChainTest_full1_20131207120946.bak' 3 WITH REPLACE , NORECOVERY 4 GO 5 6 --還原第二個差別備份 7 RESTORE DATABASE LogChainTest FROM DISK='c:\LogChainTest_diff2_20131207121428.bak' 8 WITH NORECOVERY 9 GO 10 11 --還原第一個差別備份 12 RESTORE DATABASE LogChainTest FROM DISK='c:\LogChainTest_diff_20131207120957.bak' 13 WITH RECOVERY 14 GO
先還原差備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
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 DECLARE @strbackup NVARCHAR(100) 12 --改成日期加時間的 13 SET @strbackup = 'C:\LogChainTest_full1_' 14 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 15 ''), ':', '') + '.bak' 16 BACKUP DATABASE LogChainTest TO DISK =@strbackup WITH INIT,NO_LOG ; 17 GO 18 19 已爲數據庫 'LogChainTest',文件 'LogChainTest' (位於文件 1 上)處理了 176 頁。 20 BACKUP DATABASE...FILE=<name> 成功處理了 176 頁,花費 0.025 秒(57.671 MB/秒)。 21 22 23 24 --第一個差別備份 25 USE LogChainTest 26 GO 27 CREATE TABLE tt(id INT) 28 INSERT INTO tt 29 SELECT 1 30 DECLARE @strbackup NVARCHAR(100) 31 --改成日期加時間的 32 SET @strbackup = 'C:\LogChainTest_diff_' 33 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 34 ''), ':', '') + '.bak' 35 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT, DIFFERENTIAL,NO_LOG; 36 GO 37 38 39 (1 行受影響) 40 已爲數據庫 'LogChainTest',文件 'LogChainTest' (位於文件 1 上)處理了 96 頁。 41 BACKUP DATABASE...FILE=<name> WITH DIFFERENTIAL 成功處理了 96 頁,花費 0.016 秒(49.152 MB/秒)。 42 43 44 45 --第一個日誌備份 46 USE LogChainTest 47 GO 48 INSERT INTO tt 49 SELECT 2 50 DECLARE @strbackup NVARCHAR(100) 51 --改成日期加時間的 52 SET @strbackup = 'C:\LogChainTest_log1_' 53 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 54 ''), ':', '') + '.bak' 55 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT,NO_LOG; 56 GO
備份策略:完備-》差備-》日備
你們能夠看到執行日備的時候沒有產生bak文件
查看bak文件裏的日誌
1 SELECT * 2 FROM fn_dump_dblog(NULL, NULL, N'DISK', 1, 3 N'c:\LogChainTest_full1_20131207123314.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)
完備0行
差備0行
其實能夠用下圖來理解
bak文件裏只有數據沒有日誌,連保證事務一致性的少許的活動日誌都沒有
備份腳本
TRUNCATE_ONLY
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 USE [LogChainTest] 12 GO 13 SELECT * FROM [sys].[fn_dblog](NULL,NULL) ORDER BY [Begin Time] ASC 14 15 16 --日誌備份 17 USE LogChainTest 18 GO 19 CREATE TABLE tt(id INT) 20 INSERT INTO tt 21 SELECT 2 22 DECLARE @strbackup NVARCHAR(100) 23 --改成日期加時間的 24 SET @strbackup = 'C:\LogChainTest_log1_' 25 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 26 ''), ':', '') + '.bak' 27 BACKUP LOG LogChainTest TO DISK = @strbackup WITH TRUNCATE_ONLY; 28 GO 29 30 --日備後的事務日誌記錄 31 USE [LogChainTest] 32 GO 33 SELECT * FROM [sys].[fn_dblog](NULL,NULL) ORDER BY [Begin Time] ASC 34 35 36 37 SELECT * 38 FROM fn_dump_dblog(NULL, NULL, N'DISK', 1, 39 N'c:\LogChainTest_diff_20131207123347.bak', DEFAULT, 40 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 41 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 42 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 43 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 44 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 45 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 46 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 47 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 48 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 49 DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, 50 DEFAULT, DEFAULT)
備份策略:日備
你們能夠看到執行日備的時候沒有產生bak文件
查看日誌備份前數據庫事務日誌
查看日誌備份前數據庫事務日誌
其實能夠用下圖來理解
truncate_only只是截斷了日誌,沒有產生bak文件,更不用說備份日誌到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 USE master 14 GO 15 DECLARE @strbackup NVARCHAR(100) 16 --改成日期加時間的 17 SET @strbackup = 'C:\LogChainTest_full1_' 18 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 19 ''), ':', '') + '.bak' 20 BACKUP DATABASE LogChainTest TO DISK = @strbackup WITH INIT; 21 GO 22 23 24 25 --第一個日誌備份 26 USE LogChainTest 27 GO 28 CREATE TABLE tt(id INT) 29 INSERT INTO tt 30 SELECT 1 31 DECLARE @strbackup NVARCHAR(100) 32 --改成日期加時間的 33 SET @strbackup = 'C:\LogChainTest_log1_' 34 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 35 ''), ':', '') + '.bak' 36 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT; 37 GO 38 39 40 --第二個日誌備份WITH TRUNCATE_ONLY 41 USE LogChainTest 42 GO 43 INSERT INTO tt 44 SELECT 2 45 DECLARE @strbackup NVARCHAR(100) 46 --改成日期加時間的 47 SET @strbackup = 'C:\LogChainTest_log2_' 48 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 49 ''), ':', '') + '.bak' 50 BACKUP LOG LogChainTest TO DISK = @strbackup WITH TRUNCATE_ONLY; 51 GO 52 53 54 --第三個日誌備份 55 USE LogChainTest 56 GO 57 INSERT INTO tt 58 SELECT 3 59 DECLARE @strbackup NVARCHAR(100) 60 --改成日期加時間的 61 SET @strbackup = 'C:\LogChainTest_log3_' 62 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 63 ''), ':', '') + '.bak' 64 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT; 65 GO
當我進行到第三個日誌備份的時候就報錯了
1 (1 行受影響) 2 消息 4214,級別 16,狀態 1,第 8 行 3 沒法執行 BACKUP LOG,由於當前沒有數據庫備份。 4 消息 3013,級別 16,狀態 1,第 8 行 5 BACKUP LOG 正在異常終止。
能夠用下圖來理解
(2)從數據庫鏡像進行恢復這種狀況因爲沒有研究過就不說了
小結:
截斷日誌跟日誌鏈斷裂不是同同樣東西!!
截斷日誌:針對數據庫事務日誌
日誌鏈斷裂:針對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 DECLARE @strbackup NVARCHAR(100) 14 --改成日期加時間的 15 SET @strbackup = 'C:\LogChainTest_full1_' 16 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 17 ''), ':', '') + '.bak' 18 BACKUP DATABASE LogChainTest TO DISK =@strbackup WITH INIT,CHECKSUM ; 19 GO 20 21 22 23 --第一個日誌備份 24 USE LogChainTest 25 GO 26 CREATE TABLE tt(id INT) 27 INSERT INTO tt 28 SELECT 1 29 DECLARE @strbackup NVARCHAR(100) 30 --改成日期加時間的 31 SET @strbackup = 'C:\LogChainTest_log1_' 32 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 33 ''), ':', '') + '.bak' 34 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT,COPY_ONLY; 35 GO 36 37 38 39 40 41 --第二個日誌備份 42 USE LogChainTest 43 GO 44 INSERT INTO tt 45 SELECT 2 46 DECLARE @strbackup NVARCHAR(100) 47 --改成日期加時間的 48 SET @strbackup = 'C:\LogChainTest_log2_' 49 + REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR, GETDATE(), 120), '-', ''), ' ', 50 ''), ':', '') + '.bak' 51 BACKUP LOG LogChainTest TO DISK = @strbackup WITH INIT,COPY_ONLY; 52 GO 53 54 55 --在第二個日誌備份後插入記錄到tt表 56 INSERT INTO tt 57 SELECT 3
在第二個日誌備份以後還插入了一條記錄到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
咱們作一個實驗,先作一個完整備份
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
backupset表就會產生一條記錄
咱們將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://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數據庫技術內幕》