一. 概述
在sql server 裡有資料檔案.mdf和日志檔案.ldf,日志檔案是sqlserver資料庫的另一個重要組成部分,日志檔案記錄了所有事務以及每個事務對資料庫所做的修改。為了提高資料庫的性能, sqlserver 資料是緩存在記憶體裡,并沒有實時寫入到磁盤,直到資料庫出現檢查點(checkpoint)或者記憶體不足必須(Lazy Write)将資料的修改寫入到磁盤。 sql server在開啟了事務并對記憶體中的資料進行修改時,會生成日志記錄。 sqlserver 對資料頁的插入修改删除都是在記憶體中完成後送出事務,但并不會同步到硬碟的資料頁上。 為了保證資料庫事務的一緻性 如(伺服器崩潰,斷電)等 記憶體中的修改沒有來得及寫入硬碟,下次重新開機時候要能夠恢複到一個事務一緻的時間點,就必須依賴于事務日志。
1.1 存儲結構
與資料檔案不同 日志檔案不是按頁/區來進行組織的。每個實體日志檔案是分成多個虛拟日志單元,虛拟日志單元沒有固定大小,且數量不固定, 管理者也不能配置大小和數量。 例如:日志檔案每自動增長一次(預設是按10%的空間擴充),會至少增加一個虛拟單元。
事務日志是一種回繞的檔案。例如一個資料庫裡的日志檔案包括5個虛拟日志單元,在建立資料庫時,邏輯日志檔案從實體檔案的始端開始,新的日志記錄被添加到邏輯日志未端,然後向實體日志未端擴張。
當邏輯日志的末端到達實體日志的末端時,新的日志記錄将回繞到實體日志檔案的始端繼續向後寫(這是因為日志備份會截斷使日志空間重用)。
下圖是日志檔案的流程圖,當日志備份後虛拟日志1和虛拟日志2會被截斷,虛拟日志3成為了邏輯日志的開頭,當虛拟日志3和虛拟日志4在使用後,再次備份時,由于日志檔案是一個回繞的檔案,此時又從虛拟日志1開始。
圖1 日志檔案的外觀

在一個虛拟日志單元裡,分成很多塊,塊内有具體的日志記錄,每條日志記錄有一個LSN(Log Sequence Number)編号,這個編号由三部分組成。第一部分是虛拟日志單元(Virtual Log File)序列号,第二部分是在虛拟日志單元中塊的編号,第三部分是在塊中日志記錄的編号。對于某個LSN,其編号為000001D:000000FD:0002。 這表明這個LSN是屬于虛拟日志000001D,該虛拟日志中屬于塊000000FD,在該塊中對應記錄2。
1.2 DBCC LOG
使用DBCC LOG來檢視日志檔案裡存放了些什麼資訊, dbcc log(dbname, formart_id),formart_id 使用"3" 參數輸出會比較詳細。
Create database TestLog
go
use TestLog
go
Create Table Test(ID int,name nvarchar(50))
GO
Insert into Test Values(1,'aaaa')
update Test set name='bbbb' where ID=1
Go
dbcc traceon (3604)
go
dbcc log (TestLog,3)
由于dbcc log是未公開的指令,是以未找到相關說明, 如下圖所示 包括了目前序号号,操作類型,事務号等相關資訊。
二. ApexSQL Log工具
由于dbcc log資料不太直覺,現通過第三方工具ApexSQL Log來檢視,該工具可以看到對上面表的建立,插入,更新,删除的操作記錄,在資料庫日志檔案裡還标注了起始時間表,以及操作由哪個使用者執行的,對于每一個操作,可以看到更具體的更新資訊。
這是剛剛操作的二條記錄如下圖所示
選中insert 該行可以找到該語句做undo (撤消復原 舊值覆寫)和redo(送出 新值覆寫)
-- Undo INSERT (0000001E:00000047:0013) done at 2018-07-29 09:49:55.570 by hsr-PC\hsr in transaction 0000:00000301 (Committed)
BEGIN TRANSACTION
DELETE FROM [dbo].[Test] WHERE /*** WARNING: WHERE CLAUSE FOR THIS STATEMENT WAS GENERATED FOR A TABLE WITH NO PRIMARY KEY AND NO CLUSTERED INDEX ***/[ID] = 1 AND [name] = N'aaaa' COLLATE Chinese_PRC_CI_AS
IF @@ROWCOUNT <= 1 COMMIT TRANSACTION ELSE BEGIN ROLLBACK TRANSACTION; PRINT 'ERROR: STATEMENT AFFECTED MORE THAN ONE ROW. ALL THE CHANGES WERE ROLLED BACK.' END
--Redo INSERT (0000001E:00000047:0013) done at 2018-07-29 09:49:55.570 by hsr-PC\hsr in transaction 0000:00000301 (Committed)
INSERT INTO [dbo].[Test] ([ID], [name]) VALUES (1, N'aaaa' COLLATE Chinese_PRC_CI_AS)
-- 下面ID=1的語句做四做操作
update Test set name='cccc' where ID=1
update Test set name='dddd' where ID=1
update Test set name='eeee' where ID=1
delete from Test where ID=1
下列記錄了相應的操作,trial restricted 可能是因為該軟體需要付費。
總結: 使用truncate table 來删除操作是不會記錄日志的,且無法做undo操作。日志記錄與實際修改的資料量有關,每一條記錄的修改都會儲存日志記錄。sql server日志裡面能讀到資料修改前的值和修改後的值。
參考文獻:
sq lserver2012實施與管理實戰指南