动态

详情 返回 返回

SQLServer事務日誌的VLF虛擬文件個數過多的處理 - 动态 详情

Review某個SQLServer的生產環境的errolog的時候,無意中發現這麼一條日誌,意思是過多的VLF文件,會影響數據庫的啓動速度和日誌備份效率。
Database *** has more than 10000 virtual log files which is excessive. Too many virtual log files can cause long startup and backup times.Consider shrinking the log and using a different growth increment to reduce the number of virtual log files.
由於這個庫已經存在很久了,猜測可能是當時設置的SQLServer的事務日誌文件增長設置不合理導致的,雖然問題不大,這個情況還是簡單總結一下處理方式。

 

VLF(虛擬日誌文件)的副作用

事務日誌中VLF會有一些負面影響,在數據庫恢復過程的初始階段,SQL Server 會遍歷所有事務日誌文件中的所有 VLF,並生成這些 VLF 的列表。 此過程可能需要很長時間,具體取決於特定數據庫中存在的 VLF 數量。 VLF 越多,過程越長。
如果遇到頻繁的事務日誌自動增長或小增量手動增長,數據庫最終可能會出現大量 VLF。 當 VLF 數量達到數十萬的範圍時,你可能會遇到以下部分或大部分症狀:
在 SQL Server 啓動期間,一個或多個數據庫需要很長時間才能完成恢復。
還原數據庫需要很長時間才能完成。
嘗試附加數據庫需要很長時間才能完成。
嘗試設置數據庫鏡像時,遇到錯誤消息 1413、1443 和 1479,表示超時。
嘗試還原數據庫時,遇到與內存相關的錯誤,如 701。
事務複製或變更數據捕獲可能會出現明顯延遲。

事務日誌和VLF(虛擬日誌文件)的關係

那麼,事務日誌和VLF文件的個數是怎麼樣的一種關係呢?或者説VLF(虛擬日誌文件的增長模式)是怎麼樣的?
1,在 SQL Server 2014 (12.x) 及更高版本中,如果下一次增長少於當前日誌物理大小的 1/8,則創建 1 個 VLF,補償此增長大小。
2,如果下一次增長超過當前日誌大小的 1/8,則使用 pre-2014 方法,即:
  2.1,如果增長少於 64 MB,則創建 4 個 VLF,補償此增長大小(例如,增長 1 MB,創建 4 個 256 KB 的 VLF)。
    在 Azure SQL 數據庫中,從 SQL Server 2022 (16.x)(所有版本)開始,邏輯略有不同。 如果增長小於或等於 64 MB,則數據庫引擎只創建一個 VLF 來補償此增長大小。
  2.2,如果增長來自 64 MB(至 1 GB),則創建 8 個 VLF,補償此增長大小(例如,增長 512 MB,創建 8 個 64 MB 的 VLF)。
  2.3,如果增長大於 1 GB,則創建 16 個 VLF,補償此增長大小(例如,增長 8 GB,創建 16 個 512 MB 的 VLF)。
 
從2.1中可以看到,如果設置的日誌增長值過小,會導致生成多個較小的VLF。那麼又如何修復這種情況呢?
 

VLF(虛擬日誌文件)過多的修復方案

如下是一個完整的測試腳本以及驗證方案。
--建庫腳本,刻意將日誌文件的增長設置為1MB(FILEGROWTH = 1024KB)
CREATE DATABASE [DB01]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'DB01', FILENAME = N'D:\MSSQL\DB01.mdf' , SIZE = 512MB , MAXSIZE = UNLIMITED, FILEGROWTH = 65536KB )
 LOG ON 
( NAME = N'DB01_log', FILENAME = N'D:\MSSQL\DB01_log.ldf' , SIZE = 64MB , MAXSIZE = 2048GB , FILEGROWTH = 1024KB )
 WITH CATALOG_COLLATION = DATABASE_DEFAULT, LEDGER = OFF
GO

--新建的數據庫默認是完整恢復模式,建庫之後做一次完整的備份,否則數據庫還是簡單恢復模式
backup database DB01 to disk = 'D:\MSSQL\DB01.bak';


-- 查看 DB 增長信息
use DB01;
go

SELECT 
    name,
    type_desc ,
    size/128.0 AS current_size,
    -- growth: 8kb page number
    CASE is_percent_growth 
        WHEN 1 THEN CAST(growth AS VARCHAR) + '%' 
        ELSE CAST(growth*8/1024.0 AS VARCHAR) + ' MB' 
    END AS 'growth',
    physical_name
FROM sys.database_files;



--創建一個測試表
create table t1
(
	c1 int identity(1,1),
	c2 varchar(50),
	c3 varchar(50),
	c4 varchar(50),
	c5 varchar(50),
	c6 varchar(50),
	c7 varchar(50),
	c8 datetime2,
	constraint pk_t1_c1 primary key(c1)
);

--往測試表寫入數據
insert into t1 
select newid(),newid(),newid(),newid(),newid(),newid(),GETDATE() from sys.objects a , sys.objects b, sys.objects c;

--再多執行幾次這個語句,生成足夠多的事務日誌
insert into t1 
select  newid(),newid(),newid(),newid(),newid(),newid(),GETDATE() from t1;


-- sqlserver 事務日誌和包含的虛擬日誌文件個數
SELECT db.name, count(dbl.database_id) as Total_VLF_count, convert(decimal (10,2), avg(dbl.vlf_size_mb)) as Avg_VLF_Size_MB
FROM sys.databases db
 CROSS APPLY sys.dm_db_log_info(db.database_id) dbl
where name = 'DB01'
GROUP BY db.name
ORDER BY Total_VLF_count DESC;


--查看數據文件使用比例
SELECT file_id, name,type_desc,
       CAST(FILEPROPERTY(name, 'SpaceUsed') AS decimal(19,4)) * 8 / 1024. AS space_used_mb,
       CAST(size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS decimal(19,4)) AS space_unused_mb,
       CAST(size AS decimal(19,4)) * 8 / 1024. AS space_allocated_mb,
       CAST(max_size AS decimal(19,4)) * 8 / 1024. AS max_size_mb
FROM sys.database_files;


-- 事務日誌使用情況
DBCC SQLPERF(LOGSPACE);

-- 使用DBCC LOGINFO查看虛擬日誌文件(VLF)狀態,(0=空閒,2=活動)
DBCC LOGINFO;


-- 查看日誌截斷原因
SELECT 
    name AS '數據庫名稱',
    log_reuse_wait_desc AS '日誌重用等待原因'
FROM sys.databases;


-- DB Size 查看
EXEC sp_helpdb 'DB01';



--修復包含大量 VLF 的數據庫

solution1:
	1,備份事務日誌
	backup log DB01 to disk = 'D:\MSSQL\db01.trn2';
	2,手動收縮事務日誌文件,如下單位是MB
	DBCC SHRINKFILE (N'DB01_log' , 64);
	--這種方式收縮無效,需要收縮到指定大小才行
	DBCC SHRINKFILE (N'DB01_log' , 0, TRUNCATEONLY);
	******特別注意******
	1,手動收縮的目的是消除已經分配給事務日誌文件的那些小的VLF
	2,個人在SQLServer 2019標準版下實測,備份事務日誌後收縮,並不能一次就達到目的,需要執行2~3次備份+收縮操作才能將日誌收縮至最小,
	   按道理第一次備份玩日誌就可以截斷了(測試環境,備份後沒有繼續寫入),實際需要執行2~3次備份和收縮才行
       
	3,使用以下 T-SQL 腳本在一個步驟中手動將文件增長到所需的大小,避免頻繁增長導致過多的VLF:
	ALTER DATABASE DB01 MODIFY FILE (NAME='DB01_log', SIZE = 1024MB);
	請注意:上述直接修改DB01_log的大小的時候,生成的VLF的個數,不受限於上述默認自動增長生成VLF個數的規則


solution2:
	1,備份事務日誌
	2,手動收縮事務日誌文件。
	3,ALTER DATABASE DB01 MODIFY FILE (NAME = 'DB01_log', FILEGROWTH = 512MB);

refer:
https://learn.microsoft.com/zh-cn/sql/relational-databases/errors-events/mssqlserver-9017-database-engine-error?view=sql-server-ver16
https://learn.microsoft.com/zh-cn/sql/relational-databases/sql-server-transaction-log-architecture-and-management-guide?view=sql-server-ver16
 

Add a new 评论

Some HTML is okay.