目录
什么是事务日志
事务日志的组成
事务日志大小维护方法
Truncate
Shrink
索引碎片
总结
SQL Server 数据库引擎在内部将每个物理文件分为多个虚拟日志文件。虚拟日志文件没有固定大小和固定数量,这两个值是由数据库引擎动态决定的。
事务日志是一个循环文件。当数据库创建以后,逻辑日志从物理日志文件的起始位置开始。新的日志记录被添加到逻辑日志的后面直到物理日志文件的末尾。日志截取会把LSN(Log Sequential Number)比最小恢复日志序列号(MinLSN)早的虚拟日志记录给释放掉。MinLSN 是要实现一次成功的数据库范围回滚所需要的最小日志记录。
事务日志的结构如下:
每个虚拟日志有多条记录组成,每条记录都有一个LSN(一条SQL 原子语句)。分析上面的图我们可以看到虚拟日志1和2被截取了,这说明虚拟日志1和2中的事务已经成功提交(并不代表数据修改已经物理上被更新到数据库文件中,因为有一个涉及磁盘IO效率问题)。
当逻辑日志到达物理日志文件的末尾以后,新的日志记录会循环到物理日志的开头位置,如下图所示:
只要逻辑日志尾不超过逻辑日志头,这种循环就永远不会结束。如果旧的日志记录能够被周期性/频繁的截取,那么就会有足够的可用空间留给新添加的日志记录,这样整个物理日志文件大小就会保持在一个相对稳定的范围,反之则会出现以下两种情况之一:
注意:如果一个数据库有多个事务日志文件(LDF), 那么除非第一个事务日志文件没有可用空间了,否则不会使用其他的事务日志文件。
由上面的描述我们可以大概知道Truncate 就是将事务日志中的可以回收的逻辑日志文件标识为可以再次使用,具体的触发条件分为两种情况:
打开查询分析器,执行以下查询:
现在对数据库执行一次日志备份, 然后执行LOGINFO命令:
可以看到逻辑日志1,2的状态被标识为0,这意味着逻辑日志1和2都可以被重用了。但是我们再观察下逻辑日志4的偏移和大小,这两个值并没有变,也就意味着整个事务日志大小仍然为:
1384448+712704=2,097,152 字节(纯逻辑日志文件大小 + 8192字节头)
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 消耗,所以平时好好维护索引才是我们应该做的。
总之,使用周期性的日志备份来维护我们的事务日志文件大小是非常明智的。