78
votes

I've got a database, [My DB], that has the following info:
SQL Server 2008
MDF size: 30 GB
LDF size: 67 GB

I wanted to shrink the log file as much as possible and so I started my quest to figure out how to do this. Caveat: I am not a DBA (or even approaching a DBA) and have been progressing by feel through this quest.

First, I just went into SSMS, DB properties, Files, and edited the Initial Size (MB) value to 10. That reduced the log file to 62 GB (not exactly the 10 MB that I entered). So, I attached SQL Profiler, saw that DBCC SHRINKFILE was being called. I then entered that command into the query editor and here's the results.

DBCC SHRINKFILE (N'My DB_Log' , 10)

And the output was:

Cannot shrink log file 2 (My DB_Log) because the logical log file located at the end of the file is in use.
DbId   FileId      CurrentSize MinimumSize UsedPages   EstimatedPages
------ ----------- ----------- ----------- ----------- --------------
8      2           8044104     12800       8044104     12800

(1 row(s) affected)

DBCC execution completed. If DBCC printed error messages, contact your system administrator.

I then did some research on that and found this:

http://support.microsoft.com/kb/907511

Which says that I need to backup the log file before the shrinkfile so that the virtual log files will be released and the shrinkfile can do its job - I don't know what that means... I'm just paraphrasing here :)

So, I figured I'd try to backup the log file and then do a DBCC SHRINKFILE (and I changed the new log file size to 12800 since that was the MinimumSize identified in the output of the previous DBCC SHRINKFILE command)

BACKUP LOG [My DB] TO DISK = 'D:\SQLBackup\20110824-MyDB-Log.bak'
GO
DBCC SHRINKFILE (N'My DB_Log' , 12800)
GO

The result was the same as the first go around. I can only get the log file down to 62 GB.

I'm not sure what I'm doing wrong and what I should try next.

9
in order to prevent this from happening again you should run log backups or set the recovery mode to simple.DForck42
My problem was a PAUSED REPLICATION / MIRRORING setup -- it appears sql won't shrink tlogs if it thinks it'll need them for replication. This probably won't be very many people's problem, but it might help a few.pettys

9 Answers

138
votes

Okay, here is a solution to reduce the physical size of the transaction file, but without changing the recovery mode to simple.

Within your database, locate the file_id of the log file using the following query.

SELECT * FROM sys.database_files;

In my instance, the log file is file_id 2. Now we want to locate the virtual logs in use, and do this with the following command.

DBCC LOGINFO;

Here you can see if any virtual logs are in use by seeing if the status is 2 (in use), or 0 (free). When shrinking files, empty virtual logs are physically removed starting at the end of the file until it hits the first used status. This is why shrinking a transaction log file sometimes shrinks it part way but does not remove all free virtual logs.

If you notice a status 2's that occur after 0's, this is blocking the shrink from fully shrinking the file. To get around this do another transaction log backup, and immediately run these commands, supplying the file_id found above, and the size you would like your log file to be reduced to.

-- DBCC SHRINKFILE (file_id, LogSize_MB)
DBCC SHRINKFILE (2, 100);
DBCC LOGINFO;

This will then show the virtual log file allocation, and hopefully you'll notice that it's been reduced somewhat. Because virtual log files are not always allocated in order, you may have to backup the transaction log a couple of times and run this last query again; but I can normally shrink it down within a backup or two.

44
votes

In addition to the steps you have already taken, you will need to set the recovery mode to simple before you can shrink the log.

THIS IS NOT A RECOMMENDED PRACTICE for production systems... You will lose your ability to recover to a point in time from previous backups/log files.

See example B on this DBCC SHRINKFILE (Transact-SQL) msdn page for an example, and explanation.

12
votes

I use this script on sql server 2008 R2.

USE [db_name]

ALTER DATABASE [db_name] SET RECOVERY SIMPLE WITH NO_WAIT

DBCC SHRINKFILE([log_file_name]/log_file_number, wanted_size)

ALTER DATABASE [db_name] SET RECOVERY FULL WITH NO_WAIT
10
votes

Try this

ALTER DATABASE XXXX  SET RECOVERY SIMPLE

use XXXX

declare @log_File_Name varchar(200) 

select @log_File_Name  = name from sysfiles where filename like '%LDF'

declare @i int = FILE_IDEX ( @log_File_Name)

dbcc shrinkfile ( @i , 50) 
2
votes

I tried many ways but this works.

Sample code is availalbe in DBCC SHRINKFILE

USE DBName;  
GO  
-- Truncate the log by changing the database recovery model to SIMPLE.  
ALTER DATABASE DBName  
SET RECOVERY SIMPLE;  
GO  
-- Shrink the truncated log file to 1 MB.  
DBCC SHRINKFILE (DBName_log, 1);  --File name SELECT * FROM sys.database_files; query to get the file name
GO  
-- Reset the database recovery model.  
ALTER DATABASE DBName  
SET RECOVERY FULL;  
GO
1
votes

I resolved this problem by taking the full and transactional backup. Sometimes, the backup process is not completed and that's one of the reason the .ldf file is not getting shrink. Try this. It worked for me.

0
votes

One of our heavily transacted databases grows a few hundred thousand records in a log table every day. There are multiple log files that grow a few hundred GB every day.

We have a scheduled job that takes differential backup every half an hour. We have another scheduled job for housekeeping that runs early morning every day.

We do SHRINKFILE during the housekeeping after setting the RECOVERY to SIMPLE. We do take a full backup at the beginning and at the end of the process in order to overcome the issue of losing our ability to recover to a point in time from previous backups/log files. We use some flag in the database to make sure that the differential backup is not attempted until the housekeeping job is completed. The brief outline is as below - The House Keeping job:

  1. Set the status to 'Housekeeping in progress'
  2. Set the database to Single User mode
  3. Take a full backup of the database
  4. Delete records from various tables that are old
  5. Set the database RECOVERY mode to SIMPLE
  6. Iterate through the log files and shrink each of them
  7. Set the database RECOVERY mode to FULL
  8. Take a full backup of the database
  9. Set the database to Multi-User mode
  10. Set the status to 'Housekeeping completed'

The differential backup job:

  1. Proceed only if the status is 'Housekeeping completed'
  2. Take a differential backup

It takes a while to complete but it gets our database tidy and fresh before the regular business starts in the morning. It has been working fine for us.

-1
votes

Thanks to @user2630576 and @Ed.S.

the following worked a treat:

BACKUP LOG [database] TO DISK = 'D:\database.bak'
GO

ALTER DATABASE [database] SET RECOVERY SIMPLE

use [database]

declare @log_File_Name varchar(200)

select @log_File_Name = name from sysfiles where filename like '%LDF'

declare @i int = FILE_IDEX ( @log_File_Name)

dbcc shrinkfile ( @i , 50)

ALTER DATABASE [database] SET RECOVERY FULL