SQL Server transaction log with hundreds of small virtual log files compared to a healthy log with a few evenly sized VLFs

SQL Server VLF Count: Why Too Many VLFs Hurt and How to Fix Them

What virtual log files are, why too many VLFs slow down startup, restores and replication, how to measure your VLF count, and how to rebuild the log with a healthy layout.

July 14, 2021

Some SQL Server performance problems never show up clearly in a query plan or in wait statistics. A database takes minutes to come online, a restore takes far longer than expected, an Always On secondary keeps falling behind, and the cause is hard to find. Behind many of these problems is an invisible structure inside the transaction log: the VLF (virtual log file) count.

This article explains what VLFs are, how they’re created, which problems too many VLFs cause, how to measure your VLF count, and how to rebuild a log file with a healthy VLF layout, step by step.

What Is a VLF (Virtual Log File)?

SQL Server internally divides every transaction log file (.ldf) into pieces called virtual log files. The log works through these pieces in a circular way: new records are written to the end of the log, and VLFs that are no longer needed are truncated and reused. Log truncation and log shrink both happen at VLF boundaries.

Administrators can’t set the number or size of VLFs directly. SQL Server decides them itself when the log file is created and every time it grows, based on the size of the growth. In other words, your VLF count is a direct result of how your log file grew.

SQL Server Consulting

Need expert support for SQL Server?

Our senior database team supports SQL Server performance tuning, health checks, migrations, Remote DBA services and urgent operational issues.

Explore Services

How Are VLFs Created?

According to the rule Microsoft documents, the number of VLFs created each time the log grows depends on the size of the growth:

Growth size VLFs created
Less than 1/8 of the current log size (SQL Server 2014 and later) 1
Less than 64 MB 4 (SQL Server 2022 and later, up to 64 MB: 1)
64 MB – 1 GB 8
Larger than 1 GB 16

This is where the problem starts. In a database that has run for years with the old defaults (10% or 1 MB autogrowth for the log), the log may have grown in thousands of small steps. Each small growth adds a few small VLFs, until the log file is made of tens or even hundreds of thousands of them. Since SQL Server 2016, the default log autogrowth for new databases is 64 MB, but databases upgraded or migrated from older versions keep their old settings.

The opposite is a problem too: growing a log in one very large step creates a few very large VLFs. Log truncation then happens in huge chunks and log space isn’t used efficiently. The goal is neither thousands of tiny VLFs nor a handful of giant ones.

What Problems Do Too Many VLFs Cause?

In the first phase of database recovery, SQL Server scans every VLF in the log and builds a list of them. The more VLFs, the longer this takes. According to Microsoft, a large number of VLFs can cause:

  • Slow database startup: after a SQL Server restart or a failover, the database takes a very long time to finish recovery.
  • Slow restore and attach: restoring or attaching the database takes far longer than expected, which directly extends your RTO in a disaster.
  • Slow log backups.
  • Replication, CDC and Always On latency: the Log Reader Agent has to scan every VLF for transactional replication, and redo latency grows on Always On secondaries.
  • Timeouts when setting up mirroring (errors 1413, 1443 and 1479), and memory errors such as 701 during restore.

These effects become pronounced as the VLF count reaches the tens and hundreds of thousands. Microsoft recommends keeping the total VLF count to a few thousand at most.

Signs in the error log

From SQL Server 2012 onward, when a database with more than 10,000 VLFs starts up, is restored or is attached, SQL Server writes informational message 9017 to the error log: “Database … has more than … virtual log files which is excessive.” If the error log also shows minutes passing between a database’s “Starting up database” message and its recovery “Analysis … Phase 1 of 3” message, that’s another typical sign of a high VLF count. We explain how to read the error log in our SQL Server Error Log guide.

How to Measure Your VLF Count

On SQL Server 2016 SP2 and later, VLF information comes from two dynamic management functions. sys.dm_db_log_info replaces the old DBCC LOGINFO command, and sys.dm_db_log_stats returns summary information. For every database on the server:

SELECT d.name,
       d.recovery_model_desc,
       ls.total_vlf_count,
       ls.active_vlf_count,
       CAST(ls.total_log_size_mb AS decimal(18, 2)) AS total_log_size_mb,
       CAST(ls.total_log_size_mb / NULLIF(ls.total_vlf_count, 0) AS decimal(18, 2)) AS avg_vlf_size_mb,
       ls.log_truncation_holdup_reason
FROM sys.databases AS d
CROSS APPLY sys.dm_db_log_stats(d.database_id) AS ls
WHERE d.state_desc = N'ONLINE'
ORDER BY ls.total_vlf_count DESC;

When you read the results, look at the average VLF size, not just the count. 20,000 VLFs in a 50 GB log (2.5 MB on average) is clearly a problem; 200 VLFs in a 50 GB log (250 MB on average) is a reasonable layout. The log_truncation_holdup_reason column shows why the log can’t be truncated; before fixing VLFs, it should be NOTHING or a transient value.

How to Fix a High VLF Count

The idea is simple: shrink the log file as far as possible, grow it back to the size you need in a few controlled steps, and fix the autogrowth setting. Before you start, make sure you have a restorable backup.

1. Make sure the log is truncated

The log can only shrink if the VLFs at its end aren’t in use. Under the full recovery model, take a log backup first. If log_truncation_holdup_reason shows LOG_BACKUP or ACTIVE_TRANSACTION, deal with that cause first. For details, see SQL Server Transaction Log Too Big?

USE [SalesDB];

BACKUP LOG [SalesDB]
TO DISK = N'E:\Backup\SalesDB_LOG_before_vlf_fix.trn'
WITH COMPRESSION, CHECKSUM;

-- Where are the active VLFs? If the last VLFs are active, the shrink won't reach its target.
SELECT TOP (5) vlf_sequence_number, vlf_begin_offset, vlf_size_mb, vlf_active
FROM sys.dm_db_log_info(DB_ID())
ORDER BY vlf_begin_offset DESC;

2. Shrink the log

-- Logical name of the log file
SELECT name, size / 128 AS size_mb FROM sys.database_files WHERE type = 1;

-- Shrink as far as possible (target in MB)
DBCC SHRINKFILE (N'SalesDB_log', 0);

-- Result: how many VLFs are left?
SELECT COUNT(*) AS vlf_count FROM sys.dm_db_log_info(DB_ID());

If the active log sits in the VLFs at the end of the file, the first attempt may not shrink it far enough. Taking another log backup (in full recovery) and repeating the shrink usually solves this. For more on shrinking, see SQL Server Shrink Best Practices.

3. Grow the log to its target size in controlled steps

Size the log for its real requirement (its usage during the busiest period). Jumping straight to a large target creates very large VLFs; getting there in tiny steps creates too many. A practical approach is to move towards the target in 8 GB steps; since every growth over 1 GB creates 16 VLFs, each step adds VLFs of roughly 512 MB:

-- Target: a 32 GB log with VLFs of roughly 512 MB
ALTER DATABASE [SalesDB] MODIFY FILE (NAME = N'SalesDB_log', SIZE = 8192MB);
ALTER DATABASE [SalesDB] MODIFY FILE (NAME = N'SalesDB_log', SIZE = 16384MB);
ALTER DATABASE [SalesDB] MODIFY FILE (NAME = N'SalesDB_log', SIZE = 24576MB);
ALTER DATABASE [SalesDB] MODIFY FILE (NAME = N'SalesDB_log', SIZE = 32768MB);

-- Fixed autogrowth for future growth
ALTER DATABASE [SalesDB] MODIFY FILE (NAME = N'SalesDB_log', FILEGROWTH = 512MB);

For smaller logs, scale the step size down; for a 4 GB log, for example, 1 GB steps are enough. Microsoft also publishes a script that calculates the optimal VLF layout and growth steps for your current log sizes in the Tiger Toolbox repository on GitHub.

4. Fix the autogrowth setting for good

Warning

Skip this step and the problem comes back within months. Set log autogrowth as a fixed size in MB, not a percentage, and following Microsoft’s recommendation, don’t set it above 1,024 MB. From SQL Server 2022, log growths up to 64 MB benefit from instant file initialization and create a single VLF. Check the autogrowth settings of every log file on the server with:

SELECT DB_NAME(database_id) AS database_name,
       name AS logical_name,
       CAST(size AS bigint) * 8 / 1024 AS size_mb,
       CASE WHEN is_percent_growth = 1
            THEN CAST(growth AS varchar(10)) + ' %'
            ELSE CAST(CAST(growth AS bigint) * 8 / 1024 AS varchar(10)) + ' MB'
       END AS autogrowth
FROM sys.master_files
WHERE type = 1
ORDER BY is_percent_growth DESC, growth;

Every log file growing by a percentage or in very small steps such as 1–10 MB is a candidate for a high VLF count in the future. In the example above, a 512 MB growth on the 32 GB log is less than 1/8 of the current size, so it creates a single 512 MB VLF; the VLF layout stays healthy even as the log grows.

Watch Out in Always On and Log Shipping Environments

Note

In an availability group, log file operations affect every replica. Growing the log on the primary (ALTER DATABASE ... MODIFY FILE) is applied to the secondaries too, so make sure their log disks have enough space as well. A shrink only works if the log records the secondaries haven’t yet received or applied aren’t at the end of the log, so do this work at a quiet time when all replicas are synchronized. For more on Always On architecture, see our SQL Server Always On guide.

Frequently Asked Questions

There’s no hard limit. Microsoft recommends keeping the total to a few thousand at most. From SQL Server 2012, more than 10,000 VLFs triggers warning 9017 in the error log. Serious startup and restore slowdowns usually appear in the tens and hundreds of thousands. The average VLF size matters as much as the count.
It still works, but on SQL Server 2016 SP2 and later Microsoft recommends sys.dm_db_log_info and sys.dm_db_log_stats. They can be used for every database in a single query and return clearer columns.
The effects Microsoft documents are mainly on database startup and recovery, restore and attach, log backups, and replication, CDC and Always On redo. Day-to-day queries aren’t expected to slow down directly; the problem shows up at the worst possible moment, during a failover or a disaster-recovery restore.
No. Log backups, DBCC SHRINKFILE and ALTER DATABASE ... MODIFY FILE all work while the database is online. Still, pick a low-activity window, as growing the log can cause brief slowdowns.
A single very large growth creates 16 very large VLFs. Log truncation then happens in huge chunks and space is used inefficiently. Reaching the target in a few controlled steps gives you a reasonable number of reasonably sized VLFs.
Yes, if the autogrowth setting isn’t fixed. A log that grows by a percentage or in very small steps will collect thousands of VLFs again over time. A fixed, sensible autogrowth value, plus sizing the log for its busiest period in advance, keeps the problem from returning.

SQL Server Performance Consulting

Don’t Let Hidden Problems Stretch Your Restore and Failover Times

Aryasoft’s senior DBA team reviews and fixes the health of your SQL Server environment end to end, including VLF layout, log sizing, autogrowth settings and restore times.

4.7/5