Virtual Log Files (VLFs) trong sql server

Virtual Log Files (VLFs) là gì ?

Trong mỗi database sẽ có một log file, dùng để lưu trữ những hoạt động logging trong sql server, và để tiện cho quá trình quản lý thì một log file sẽ được chia ra thành nhiều Virtual Log Files (VLF) về mặc logic.

Trong bài viết này mình sẽ viết tắt VLFs thay cho Virtual Log Files cho thuận tiện, để hiểu rõ hơn về VLF trong sql server chúng ta cùng tìm hiểu cấu trúc một database trong sql server

Một database trong sql server sẽ gồm 2 loại file : 

  • File lưu data : gồm có một file có đuôi .mdf và có thể có hoặc không có file có đuôi .ndf (không bắt buộc), những file này dùng để lưu data của database
  • File lưu log (còn được gọi là transaction log file): gồm một hoặc nhiều file .ldf, những file này chứa log records được lưu lại từ các hoạt động logging trong sql server, những log records này phục vụ cho nhiều hoạt động như recovery, replication, log shipping,… 

Để xem các file của một database, ở SSMS, ta chọn database →  properties → Files

Hình 1 

Hình 1 ta thấy database AZSqlServer gồm có 2 file : 

  • File số (1) có file type là ROWS DATA, file này chứa data và file này có đuôi .mdf (AZSqlServerData.mdf)
  • File số (2) có file type là LOG, file này chứa log records và file này có đuôi .ldf (AZSqlServerLog.Ldf) 

Log file được tạo ra từ một chuỗi nhiều Log records, mỗi log record được nhận dạng bằng một Log sequence number (LSN), để thuận tiện cho quá trình quản lý nhiều Log records thì Sql server quản lý thông qua hai khái niệm là VLF và log blocks, quan hệ giữa log file, VLFs, log blocks, log record như sau :  

  • Một log file chứa nhiều VLFs, một VLF chứa nhiều Log Blocks, một Log block chứa nhiều log records

Dưới đây là hình minh họa mối quan hệ trên 

Hình 2

hình 2 ta thấy 1 transaction log file gồm có 4 VLFs, trong đó VLF số 3 gồm có 3 Log blocks (LB1, LB2, LB3), và Lock block số 3 chứa 3 Log records

trong thực tế để biết được log record thuộc VLF và log block nào thì ta cần nhìn vào Log sequence number (LSN) của log record đó

  • Ví dụ LSN của log record là 0000002b:00000948:0003

Hình 3

LSN đang lưu ở dạng mã hex nên ta cần chuyển qua dec để biết cụ thể giá trị của từng cột

→ VLF = 2 * 16 + 11 = 43, Log Block = 9 * 256 + 4 * 16 + 8 = 2376, log record = 3

nên LSN đang nằm trong VLF số 43, Log Block số 2376, log record số 3

VLF không có kích thước cố định và cũng không có số lượng VLF cố định cho một log file cụ thể, sql server tạo ra VLF trong lúc tạo hoặc mở rộng log file.

Số lượng và kích thước VLF được tạo ra trong mỗi lần được xác định bằng công thức sau : 

  • Trong sql server 2014(12.x) và những version sau đó, nếu kích thước trong một lần tăng của log file nhỏ hơn ⅛ kích thước vật lý của log file hiện tại thì tạo 1 VLF trong lần tăng đó
  • Nếu kích thước trong một lần tăng lớn hơn hoặc bằng ⅛ kích thước log file hiện tại, sẽ sử dụng phương thức trước version 2014 như sau :
    • Nếu kích thước trong một lần tăng nhỏ hơn 64MB, tạo 4 VLFs cho lần tăng đó (ví dụ một lần tăng có kích thước là 1MB thì sẽ tạo ra 4 VLFs và mỗi VLF có kích thước là 256 KB)
    • Nếu kích thước trong một lần tăng từ 64MB đến 1GB, tạo 8 VLFs cho lần tăng đó (ví dụ mỗi lần tăng có kích thước là 512 MB thì sẽ tạo 8 VLFs tương ứng và mỗi VLF có kích thước là 64 MB)
    • Nếu kích thước trong một lần tăng lớn hơn 1GB, tạo 16 VLFs cho lần tăng đó, ví dụ mỗi lần tăng 8GB sẽ tạo 16 VLFs có kích thước 512 MB cho mỗi VLF.

Cụ thể kích thước của log file có thể tăng trong các trường hợp : 

  • Lúc tạo database, kích thước tăng của log file được xác định trong câu lệnh tạo database hoặc có giá trị mặc định nếu không xác định kích thước log file cụ thể
  • Lúc database thực hiện những hoạt động Logging vào log file hiện tại không đủ dung lượng để chứa những log records này, buộc log file phải tăng kích thước tự động và kích thước mỗi lần tăng được xác định trong cấu hình database
    • Có 2 hình thức tăng kích thước của log file là tăng theo phần trăm của dung lượng log file hiện tại hoặc tăng theo giá trị cố định tùy thuộc vào cấu hình, để xem được cấu hình bạn ấn vào ô ba chấm ở mục autogrowth/maxsize như ở  hình 1 sẽ hiện ra cấu hình như sau : 
  • Thay đổi kích thước log file bằng câu lệnh :
    • Ví dụ câu lệnh sau đây thay đổi kích thước log file lên 80MB:
ALTER DATABASE AZSqlServer MODIFY FILE 
( 
    NAME = N'AZSqlServer_Log'
    ,SIZE = 80MB
    ,FILEGROWTH = 5MB
)

Để dễ hình dung chúng ta cùng đi qua ví dụ cụ thể sau đây :

USE master
GO
/*Kiểm tra nếu AZSqlServer database đã tồn tại thì cần drop database này*/

IF DB_ID('AZSqlServer') IS NOT NULL
BEGIN
	ALTER DATABASE AZSqlServer SET SINGLE_USER
		WITH ROLLBACK IMMEDIATE
	DROP DATABASE AZSqlServer
END

/*Tạo AZSqlServer database với kích thước ban đầu của log file là 5MB, và mỗi lần log file tự động tăng 5MB nếu cần thiết */

CREATE DATABASE AZSqlServer
ON
(
	NAME = AZSqlServer_data
	,FILENAME = 'D:\DB\AZSqlServerData.mdf'
	,SIZE = 10MB
	,FILEGROWTH = 5MB
)
LOG ON
(
	NAME = AZSqlServer_Log
	,FILENAME = 'D:\DB\AZSqlServerLog.ldf'
	,SIZE = 5MB
	,FILEGROWTH = 5MB
)
GO

Tiếp theo chúng ta cùng khảo sát VLF của log file khi database vừa tạo ra, chúng ta có thể khảo sát VLF bằng view sys.dm_db_log_info hoặc câu lệnh DBCC DBCC LOGINFO, ở đây mình sẽ khảo sát bằng câu lệnh DBCC LOGINFO vì kích thước của mỗi VLF được hiển thị ở đơn vị byte, nó giúp ta dễ quan sát hơn

USE AZSqlServer
GO

DBCC LOGINFO

Kết quả

Hình 4

Một số cột chúng ta cần chú ý trong hình 4:  

RecoveryUnitId : luôn bằng 0 cho tất cả các version

FileId : FileId của transaction log mà ta đang khảo sát, bạn có thể tìm fileId của một file trong database bằng view sys.sysfiles 

Thực hiện câu lệnh sau đây để xác định fileId của log file 

USE AZSqlServer
GO

SELECT *
FROM sys.sysfiles

Kết quả : 

Hình 5

Từ kết quả trên ta thấy fileId = 2 cho log file của AZSqlServer database 

FileSize : Kích thước của Virtual log file (VLF) tính theo đơn vị byte, Số lượng VLF và Kích thước của VLF được tính như sau :

  • Từ câu lệnh tạo database cho ta biết kích thước log file sẽ được tạo là SIZE = 5MB, trước đó database chưa được tạo nên ta xem kích thước log file ở thời điểm trước khi tạo database là 0MB, do đó số VLF được tạo ra sẽ ứng với trường hợp sau :
    • Nếu kích thước tăng lớn hơn 1/8 kích thước log file hiện tại (kích thước tăng của log file là 5MB lớn hơn kích thước log file hiện tại là 0MB), sẽ sử dụng phương thức trước version 2014 như sau :
      • Nếu kích thước mỗi lần tăng nhỏ hơn 64MB (5MB < 64MB), tạo 4 VLFs cho mỗi lần tăng  
      • Nên sẽ có 4 VLF được tạo ra và mỗi VLF sẽ có kích thước là 5MB/ 4 = 1310720 byte

Nhìn vào hình 1 ta thấy, có 4 VLF được tạo ra và có 3 VLF có kích thước 1245184 byte và 1 VLF có kích thước là 1499136 byte, số lượng VLF là 4 giống như công thức ở trên, còn về kích thước thì chỉ gần bằng với tính toán bên trên là 1310720 byte, còn về phần tổng kích thước của 4 VLF ta thấy (1245184  * 3 + 1499136 )/1024/1024 = 4.99218750000 (MB) ~ 5 (MB)

StartOffset : Vị trí bắt đầu của Virtual log file trong transaction log file, một điểm lưu ý ở đây tuy là log file vừa mới được tạo nhưng VLF1 lại bắt đầu được tạo từ vị trí 8192 Byte chứ không phải 0Kb, lý do cho điều này là vì mỗi file trong sql server đều có page đầu tiên có kích thước 8kb dùng làm page header để lưu trữ một số thông tin của file, để xem rõ hơn page này chúng ta dùng lệnh sau : 

DBCC FILEHEADER (databaseId, FileId)

để sử dụng câu lệnh này ta cần dùng 2 tham số là databaseId và fileid, fileid  cho log file này bằng 2 như thông tin ở trên, còn database_id bạn có thể xác định bằng câu lệnh sau 

SELECT DB_ID('AZSqlServer') -- kết quả là 136

/*Kiểm tra thông tin page header*/

DBCC FILEHEADER (136, 2) 

Hình 6 – Thông tin file header

StartOffSet của VLF2 = StartOffSet của VLF1 + kích thước của VLF1 = 8192 + 1245184 = 1253376

Tương tự cho VLF thứ 3 và 4

FseqNo : số thứ tự của VLF, số này được tăng dần và không bị trùng giữa các VLF, ở VLF1 tuy là VLF đầu tiên của transaction log file nhưng có số thứ tự là 39, điều này xảy ra là do trong quá trình tạo database thì database được tạo bằng cách copy model database, nên hầu hết cấu hình của model database được giữ nguyên, VLF1 có số thứ tự là 39 vì thực chất trước đó đã có nhiều VLF đã được tạo ra ở model database và số thứ tự của VLF1 ở AZSqlServer database thực chất được lấy từ số thứ tự của VLF lớn nhất ở model database, để rõ hơn chúng ta cùng xem VLF ở model database bằng câu lệnh sau : 

USE model
GO

DBCC LOGINFO

Hình 7

Nhìn vào cột FseqNo ta thấy VLF lớn nhất của model database là 39 đúng bằng số thứ tự của VLF1 trong AZSqlServer database 

Status : Status của VLF, gồm các giá trị sau đây : 

  • 0 – VLF đang inactive
  • 1 – VLF đã được khởi tạo nhưng chưa được sử dụng
  • 2 – VLF đang active

ở đây ta thấy chỉ VLF1 có status là 2 tức VLF này đang active, còn 3 VLF còn lại status đang bằng 0, cho ta biết 3 VLF còn lại hiện tại chưa được sử dụng, cùng kiểm tra transaction log để ta rõ hơn

SELECT *
FROM fn_dblog(NULL, NULL)
GO

Hình 8

ở đây có tổng số 65 log records và current LSN luôn bắt đầu bằng 00000027, đây là số HEX của VLF, đổi ra số thập phân ta được 2 * 16 + 7 = 39 đúng bằng FseqNo của VLF1

Parity : biến này được dùng để biết điểm kết thúc trong một transaction log, gồm có 3 giá trị

  • 0 : Khi VLF chưa được sử dụng
  • 64 : Khi VLF được active lần đầu
  • 128 : Khi VLF đang inactive (thông qua quá trình log truncation) và được sử dụng qua trở lại
    • Cứ mỗi lần VLF được active trở lại thì giá trị 64 và 128 sẽ được chuyển đổi qua lại, giá trị VLF là một chuỗi như sau 0 → 64 → 128 → 64 → 128 →….
    • Vì database mới được tạo ra và chỉ có VLF1 đang active nên giá trị Parity của VLF1 là 64, còn giá trị paritty của 3 VLF còn lại là 0

CreateLSN : Log sequence number (LSN) của log record đã tạo VLF

  • VLF được tạo ra lúc tạo log file nên CreateLSN sẽ có giá trị là 0

Như vậy chúng ta đã có thông tin của VLF trong lần tạo log file, tiếp theo ta sẽ tăng log file để xem điều gì xảy ra

để khảo sát những thay đổi của VLF mình sẽ dùng extended Event sau :

database_file_size_change : cho ta biết kích thước log file hiện tại, và kích thước thay đổi của log file trong mỗi lần tăng

đây là scripst tạo session để quan sát event database_file_size_change

CREATE EVENT SESSION [VLF] ON SERVER 
ADD EVENT sqlserver.database_file_size_change,
ADD EVENT sqlserver.databases_log_file_size_changed,
ADD EVENT sqlserver.databases_log_file_used_size_changed
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF)
GO

Để tăng kích thước log file ta tạo 1 bảng sau đó insert data vào bảng đó để tạo ra nhiều log records

USE AZSqlServer
GO

CREATE TABLE dbo.VLF
(
	Id INT IDENTITY (1, 1)
	,Value CHAR(8000)
)
GO

SET NOCOUNT ON
GO

DECLARE @Id INT = 0
WHILE @Id <= 500
BEGIN
	INSERT INTO dbo.VLF (Value)
	VALUES ('1')

	SET @Id += 1
END

Kết quả bắt được từ extended event

Hình 9

Nhìn vào kết quả lần chạy này ta thấy xuất hiện 1 dòng do hoạt động insert làm tăng kích thước log file với file name = AZSqlServer_Log, cột Is_Automatic = true vì log file đang tăng tự động theo settings (FILEGROWTH = 5MB), có 2 giá trị cần chú ý là Size_change_kb = 5120 kb (bằng 5MB) cho ta biết log file đã được tăng lên 5MB và kích thước sau khi tăng của log file là total_size_kb = 10240 KB (10MB), vì kích thước tăng là 5120 KB và kích thước log file trước khi tăng là 5120 KB, nên số lượng và kích thước VLF được tạo rơi vào trường hợp sau : 

  • Nếu kích thước tăng lớn hơn 1/8 kích thước log file hiện tại (5MB > ⅛ *5MB), sẽ sử dụng phương thức trước version 2014 như sau :
    • Nếu kích thước mỗi lần tăng nhỏ hơn 64MB (trong trường hợp của chúng ta là 5MB), tạo 4 VLFs cho mỗi lần tăng

Nên sẽ có 4 VLF được tạo ra và mỗi VLF sẽ có kích thước là 5MB/ 4 = 1310720 byte, ta cùng kiểm tra số lượng VLF 

USE AZSqlServer
GO

DBCC LOGINFO

Hình 10

ở đây ta thấy xuất hiện thêm 4 VLF nữa, kích thước mỗi VLF là 1310720 byte, lần này ta thấy kích thước của các VLF đã được phân bố đều

Tiếp theo ta thử tăng kích thước của VLF bằng scripts bằng cách chỉnh sửa kích thước của log file bằng câu lệnh sau : 

USE [master]
GO

ALTER DATABASE AZSqlServer MODIFY FILE 
( 
  NAME = N'AZSqlServer_Log'
  ,SIZE = 80MB
  ,FILEGROWTH = 5MB
)
GO

Hình 11

ở đây ta chú ý : Is_Automatic = False do log được tăng lên khi chạy script, Size_change_Kb = 71680 KB và total_size_kb = 81920 KB = 80MB

lần này kích thước thay đổi của log file là 71680 kb = 70 MB, kích thước của log file trước khi thay đổi là 10230 KB (10MB), nên số lượng và kích thước VLF được tạo rơi vào trường hợp sau : 

  • nếu kích thước tăng lớn hơn 1/8 kích thước log file hiện tại (70MB > (⅛) * 10MB), sẽ sử dụng phương thức trước version 2014 như sau :
    • nếu kích thước một lần tăng từ 64MB đến 1GB (70MB trong trường hợp của chúng ta), tạo 8 VLFs cho một lần tăng

nên sẽ có 8 VLF được tạo ra và kích thước mỗi VLF sẽ là 71680 /8 = 8960 KB = 9175040 (bytes)

cùng kiểm tra số VLF hiện tại

Hình 12

Từ hình 12 ta thấy có 8 VLF được tạo ra từ VLF số 9 đên VLF số 16, và mỗi VLF có kích thước là 9175040 (bytes)

Vậy qua ví dụ trên ta đã biết VLF là gì, số lượng cũng như kích thước của VLF trong mỗi lần thay đổi kích thước của log file

Kết luận : 

Một log file sẽ gồm nhiều VLFs, VLF trong log file không có số lượng cũng như kích thước cụ thể và cần quan sát để có thể điều chỉnh số lượng VLF trong log file một cách thích hợp để không gây ra các vấn đề về hiệu suất truy vấn trong sql server

Tài liệu tham khảo : 

SQL Server transaction log architecture and management guide

Related Post

Leave a Reply

Your email address will not be published. Required fields are marked *