SQL Server Transaction Log Truncate && Shrink


目錄

什么是事務日志

事務日志的組成

事務日志大小維護方法

Truncate

Shrink

索引碎片

總結

什么是事務日志

  Transaction log   是對數據庫管理系統執行的一系列動作的記錄,並利用這些記錄來保證在遭遇硬件故障,災難情況下ACID的可用性。從物理上來說,事務日志就是一個記錄對數據庫更新操作的文件。

事務日志的組成

SQL Server 數據庫引擎在內部將每個物理文件分為多個虛擬日志文件。虛擬日志文件沒有固定大小和固定數量,這兩個值是由數據庫引擎動態決定的。

  事務日志是一個循環文件。當數據庫創建以后,邏輯日志從物理日志文件的起始位置開始。新的日志記錄被添加到邏輯日志的后面直到物理日志文件的末尾。日志截取會把LSN(Log Sequential Number)比最小恢復日志序列號(MinLSN)早的虛擬日志記錄給釋放掉。MinLSN 是要實現一次成功的數據庫范圍回滾所需要的最小日志記錄。

事務日志的結構如下:

  每個虛擬日志有多條記錄組成,每條記錄都有一個LSN(一條SQL 原子語句)。分析上面的圖我們可以看到虛擬日志12被截取了,這說明虛擬日志12中的事務已經成功提交(並不代表數據修改已經物理上被更新到數據庫文件中,因為有一個涉及磁盤IO效率問題)

  當邏輯日志到達物理日志文件的末尾以后,新的日志記錄會循環到物理日志的開頭位置,如下圖所示:

  只要邏輯日志尾不超過邏輯日志頭,這種循環就永遠不會結束。如果舊的日志記錄能夠被周期性/頻繁的截取,那么就會有足夠的可用空間留給新添加的日志記錄,這樣整個物理日志文件大小就會保持在一個相對穩定的范圍,反之則會出現以下兩種情況之一:

  1. 如果設置了自動增長,則會按照增長百分比/值來增加物理日志文件大小,這就是為什么我們的事務日志文件經常會變得很大。
  2. 如果沒有設置自動增長或者存儲事務日志文件的磁盤沒有可用空間了,那么SQL Server 會拋出9002 錯誤。

注意:如果一個數據庫有多個事務日志文件(LDF), 那么除非第一個事務日志文件沒有可用空間了,否則不會使用其他的事務日志文件。

事務日志大小維護方法

Truncate

  由上面的描述我們可以大概知道Truncate 就是將事務日志中的可以回收的邏輯日志文件標識為可以再次使用,具體的觸發條件分為兩種情況:

  1. 對於Recovery Mode Simple 的數據庫來說,每次事務之后會自動執行CheckPoint 操作,將提交完的邏輯日志空間清空,以保證事務日志文件大小最小。這等同於事務日志中沒有日志記錄,那么可以理解成沒有事務日志,一旦發生災難,可能會導致數據丟失。
  2. 對於Recovery Mode Full/Bulked-Log 來說,每次進行Backup Log 操作都會自動執行CheckPoint 操作,將提交完的邏輯日志空間清空。換句話說我們能通過周期性的日志備份來維護事務日志文件大小的可控。

打開查詢分析器,執行以下查詢:

  

現在對數據庫執行一次日志備份, 然后執行LOGINFO命令:

  可以看到邏輯日志1,2的狀態被標識為0,這意味着邏輯日志12都可以被重用了。但是我們再觀察下邏輯日志4的偏移和大小,這兩個值並沒有變,也就意味着整個事務日志大小仍然為:

1384448+712704=2,097,152 字節(純邏輯日志文件大小 + 8192字節頭)

Shrink

  Shrink 即是收縮日志,Truncate 操作並不會改變整個事務日志文件大小,只會將原本活躍的邏輯日志標記為不活躍以供下次使用;

Shrink 操作會完全破壞索引的物理結構,導致產生索引碎片,使索引失效。

為什么會這樣呢?因為數據文件收縮操作每次執行都會使用GAM 位圖算法來找到文件中最大文件,然后將它盡可能地移動到文件頭,如此反復(類似冒泡排序)。這樣就會完全打亂聚簇索引的順序,導致它由一個秩序進展的索引變成雜亂無章的索引。

對於DBCC SHRINKFILE, DBCC SHRINKDATABASE, 以及auto-shrink 它們都會產生一樣的后果,引入索引碎片, 導致大量I/O操作,CPU 消耗以及事務日志的過載。

來看一個例子,先創建一個數據庫:

USE MASTER;
GO 
IF DATABASEPROPERTYEX ('DBMaint2008', 'Version') > 0
DROP DATABASE DBMaint2008; 
CREATE DATABASE DBMaint2008;
GO
USE DBMaint2008;
GO 
SET NOCOUNT ON;
GO 
-- Create the 10MB filler table at the 'front' of the data file
CREATE TABLE FillerTable (c1 INT IDENTITY, c2 CHAR (8000) DEFAULT 'filler');
GO 
-- Fill up the filler table
INSERT INTO FillerTable DEFAULT VALUES;
GO 1280 
-- Create the production table, which will be 'after' the filler table in the data file
CREATE TABLE ProdTable (c1 INT IDENTITY, c2 CHAR (8000) DEFAULT 'production');
CREATE CLUSTERED INDEX prod_cl ON ProdTable (c1);
GO 
INSERT INTO ProdTable DEFAULT VALUES;
GO 1280 

然后查詢索引碎片百分比:
-- check the fragmentation of the production table
SELECT [avg_fragmentation_in_percent] FROM sys.dm_db_index_physical_stats (
DB_ID ('DBMaint2008'), OBJECT_ID ('ProdTable'), 1, NULL, 'LIMITED');
GO 

通過上面截圖可以發現初始情況下索引的碎片百分比僅0.5%, 這種情況已經非常好了。

我們把剛才創建的表刪掉並通過DBCC Shrink 操作回收空間:

可以看到索引碎片百分比已經接近100%,這樣情況下索引不但不會為我們查詢數據提高效率,反而會加重系統的負擔。

索引碎片

SQL Server 提供了兩種命令來處理上述情況:

a. Rebuild 索引

USE DBMaint2008;
GO
ALTER INDEX ALL ON ProdTable REBUILD
GO

b. Reorganize索引

USE DBMaint2008;
GO
ALTER INDEX ALL ON ProdTable REORGANIZE
GO 

我們來看一下對上面索引碎片接近100%的表進行索引重組后的效果:

可以看到數據庫表的索引恢復了正常,但是這種方案也不完全推薦,對於數據量很大的聚簇索引來說,重建/重組織索引會產生大量I/O CPU 消耗,所以平時好好維護索引才是我們應該做的。

總結

  1. Truncate 只會將虛擬日志的活躍部分變成非活躍部分,這樣就可以重用這些空間,它並不會影響整體事務日志大小;對於Simple 數據庫來說每次事務結束后都會執行CheckPoint 檢查,對於Full/Bulked-Log 數據庫來說每次日志備份操作以后都會執行CheckPoint 檢查。
  2. Shrink 操作可以減小日志文件的物理文件大小,同時也導致日志文件被重新組織,聚簇索引和非聚簇索引的原有結構都會被打亂,導致產生索引碎片,繼而帶來的影響是索引失效,磁盤I/O CPU 的資源消耗加大,對於Shrink 我們盡量避免使用它,一旦迫不得已使用Shrink 操作,我們也要按照實際情況決定是否需要重建/重組織索引。

  總之,使用周期性的日志備份來維護我們的事務日志文件大小是非常明智的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM