本文转载自桦仔的博客http://www.cnblogs.com/lyhabc/archive/2013/06/10/3130856.html
对SQLSERVER数据库事务日志的疑问
摸不透SQLSERVER了
实验环境:SQLSERVER2005 SP4,Windows7
本来没什么心情写文章,反正没人看,关于我文章中提到的问题,有些可以从文章结尾的MSDN补充那里找到答案,而有些还没有答案
根据CSDN博客的这篇文章介绍,大家可以先看一下,然后再继续往下看,因为下面会引用到CSDN博客里的一些内容
第一个问题:为什麽我完整备份数据库的时候,为什麽事务日志记录数会增加的,再备份一次又减少,再备份一次又增加?
备份之后变成了100行记录
有时候不是固定会增加50行记录的,有时候当你再次备份的时候又会变回50行记录
而虚拟日志文件的数量会减少了,从原来的45个变成35个
备份之后
我是这样备份的
当我备份事务日志并截断日志之后,剩下10行记录,备份事务日志不会减少事务日志的物理大小,需要收缩事务日志才行
当我每次执行完整备份之后,log space used都会增大
1 DBCC SQLPERF(LOGSPACE)
还有根据我上面提供的文章《SQLSERVER事务日志介绍》
当SQL Server把虚拟日志文件1和2作为可重用区域时,事务日志也相应被截断(Truncate)
需要注意的是,物理日志大小也会随着变动。如果数据库运行在完整或是批量日志恢复模型下,那么从LSN45到49之间的区域将被删除(delete),
而且直到事务日志被备份后,这段区域的空间才会被重用。
但是刚才也备份过事务日志并截断日志,但是log space used跟日志文件物理大小没有改变
SQLSERVER应该会以虚拟日志文件作为单位,如果某个虚拟日志文件里没有任何LSN了,那么截断事务日志会把
没有任何LSN的虚拟日志文件那部分里面的LSN删除
这里说一下截断跟重用
截断:如果执行了备份事务日志并截断日志的操作,那么这部分虚拟日志文件里的LSN日志记录会被删除,但是整个事务日志文件的物理大小不会变小
重用:你需要执行备份事务日志并截断日志的操作,这部分虚拟日志文件才会重用,这样就不用增加事务日志文件的大小
就是说,你要备份事务日志并截断日志,然后那些被删除了LSN日志记录的虚拟日志文件才能重用,但是SQLSERVER会不会删除虚拟日志文件呢?
MSDN并没有说到,MSDN只是说SQLSERVER会创建并扩展虚拟日志文件,但是没有提到会自动删除虚拟日志文件
重用回以前的空间,反复循环使用。
问题二:但是每个虚拟日志文件装载多少个LSN,这里我也不知道,有可能是随机的?
而重用的时候SQLSERVER应该不会不连续的
比如 像下面的情况
LSN LSN LSN LSN LSN LSN LSN -》
1 2 3 10 5 6 7
LSN 1、2、3都归属于一个虚拟日志文件A
LSN 5、6、7都归属于另一个虚拟日志文件B
LSN 10 归属于一个虚拟日志文件C
由于虚拟日志文件C里的LSN没有被删除,那么重用日志的时候就会跳过LSN 10 然后继续重用日志,但是我觉得SQLSERVER不会是这样的
我觉得SQLSERVER重用日志应该是下面这样的
LSN LSN LSN LSN LSN LSN LSN -》
1 2 3 4 5 6 7
一直连续下去,不会出现中间有些虚拟日志文件没有删除的情况,确保LSN序列的顺序性
我备份了事务日志之后,并截断之后,物理大小跟使用空间都没有变化
----------------------------------------------------------------------------------------------------------
第一个LSN:倒数第二个checkpoint之后的第一个LSN
最后一个LSN:最后一个checkpoint之前的那个LSN
检查点LSN:跟第一个LSN是一样的
完整LSN:跟第一个LSN是一样的
上面这个图是我还原数据库的时候的界面,大家可以先备份一下数据库,然后还原数据库,看一下还原的备份集是不是你刚刚备份的时候的一样
很简单的画了一张图
----------------------------------------------------------------------------------------------------------------
1 DBCC LOG([DLGPOS])
在这里说一下事务日志里的 operation字段,operation字段说明这个LSN是做什么操作的
例如:LOP_BEGIN_CKPT,CKPT:checkpoint,如果看到operation是LOP_BEGIN_CKPT,表明这个LSN在做checkpoint操作
因为备份数据库的时候,SQLSERVER会做checkpoint,所以你会看到第一个LSN会跟LOP_BEGIN_CKPT操作的那个LSN联系起来
把16进制翻译成10进制跟还原数据库的时候的第一个LSN是一样的
而Current LSN只是显示当前每个日志记录的LSN号码,没有特别的意思,所以昨天我说的,currentLSN等于第一个LSN是错的
-------------------------------------------------------------------------------------------------------
还有一个“位置”的东东, POSITION字段跟还原数据库的时候的位置是一一对应的,当我每完整备份一次数据库就会增加一,或者备份事务日志一次就会增加一
但是完整备份跟事务日志备份是分开的,看下图
1 USE [msdb]
2 GO
3 SELECT [backup_set_id], [backup_finish_date], [database_name], [first_lsn],
4 last_lsn, [checkpoint_lsn], [is_copy_only],[position]
5 FROM [dbo].[backupset] WHERE [database_name]='dlgpos'
6 ORDER BY [backup_finish_date]
问题3:但是为什麽从64突然又回到9呢?为什麽会断裂呢?这个我还不搞不清楚,这个位置字段有什么用呢?
参考的文章:
http://msdn.microsoft.com/zh-cn/library/ms190925.aspx
http://msdn.microsoft.com/zh-cn/library/ms189085(v=SQL.90).aspx
http://msdn.microsoft.com/zh-cn/library/ms179355(v=SQL.105).aspx
---------------------------------------------------------------------------------------------------
从MSDN补充来的
在上面的一篇参考文章里
我摘抄重要部分吧
SQL Server 数据库引擎在内部将每一物理日志文件分成多个虚拟日志文件。虚拟日志文件没有固定大小,且物理日志文件所包含的虚拟日志文件数不固定。数据库引擎在创建或扩展虚拟日志文件时动态选择虚拟日志文件的大小。数据库引擎尝试维护少量的虚拟日志文件。在扩展虚拟日志文件后,虚拟日志文件的大小是现有日志大小和新文件增量大小之和。管理员不能配置或设置虚拟日志文件的大小或数量。
只有当日志文件使用较小的 size 和 growth_increment 值定义时,虚拟日志文件才会影响系统性能。如果这些日志文件由于许多微小增量而增长到很大,则它们将具有很多虚拟日志文件。这会降低数据库启动以及日志备份和还原操作的速度。建议您为日志文件分配一个接近于最终所需大小的 size 值,并且还要分配一个相对较大的 growth_increment 值
事务日志是一种回绕的文件
看一下这幅图
问题4:而minLSN有没有可能跟第一个LSN是一样的呢?而第一个LSN有没有可能跟minLSN处于相同的位置或者比第一个LSN排在后面?
我觉得有可能的,就像<SQLSERVER事务日志介绍>里面,假定LSN48是一个checkpoint,那么minLSN就排在第一个LSN后面
为什麽LSN50是一个minLSN呢?
根据msdn说的,minLSN是界定截断日志的边界点
minLSN前面的是日志记录是可以截断的,minLSN后面的日志记录是不可以截断的
-----------------------------------------------------------------------------------------------------------------
更正一下今天说的,无论你进行完整备份还是差异备份还是日志备份,事务日志文件里的所有日志都会保存到bak文件里
不管你进行多少次数据库备份、还原,这些事务日志都不会消失,收缩事务日志文件也不行的
除非你进行备份事务日志并截断日志
所以就不存在只保存到checkpoint的那部分,而是当你执行备份命令的时候,最后的那个LSN也会保存到bak文件里!!!!!!!
不信的话,你可以在完整备份数据库前执行下面的SQL语句,备份完毕之后再执行一下,看事务日志记录是不是一样的
1 USE [AdventureWorks]
2 GO
3 DBCC LOG([AdventureWorks])
还有截断事务日志,看一下前后是否一样
MSDN上说物理日志文件,逻辑日志文件,虚拟日志文件,LSN,checkpoint应该像下图这样
-----------------------------------------------------------------------------------------------------------------
很反感国内某些书本照抄MSDN的内容,当说到事务日志的时候就照抄MSDN,本来MSDN就说得很笼统
只能对它们说一句“他妈的”
由于文档以及相关文章实在太少,我也研究不下去了,连MSDN论坛里的rmaio大侠都说文档很少
关于fn_dblog的用法,没有什么人能够给出,只是说微软没有公开
-------------------------------------------------------------------------------------------------------
20130611补充
msdn文章:事务日志体系结构和管理
这个第一个日志记录的日志序列号 (LSN),称为最小恢复 LSN (MinLSN)
那么究竟是不是第一个LSN就是minLSN呢?我们做一个实验
先运行下面语句
1 USE [GPOSDB] --需要查看minLSN日志信息的数据库
2 GO
3 --DBCC LOG与[fn_dblog]的记录数是一样的,只是[fn_dblog]比DBCC LOG更加详细
4 SELECT * FROM [sys].[fn_dblog](NULL,NULL)
5 DBCC LOG([GPOSDB])
然后执行一次完整数据库备份
1 BACKUP DATABASE [GPOSDB] TO DISK='D:\GPOSDBFULLBACKUP20130611.BAK'
你会发现备份之前跟备份之后的minLSN不同了,而且看下图
就是说 minLSN = 第一个LSN
我把fn_dblog的结果做了一下调整,还有更多有意思的东西
1 SELECT * INTO ##TDBLOG FROM SYS.[fn_dblog](NULL,NULL)
2 SELECT * FROM [##TDBLOG]
3 SELECT [Current LSN], [Operation], [Checkpoint Begin], [Checkpoint End],
4 [Minimum LSN], [Dirty Pages], [SPID], [Transaction Name],
5 [Description]
6 FROM [##TDBLOG] ORDER BY [Current LSN] ASC
上图说明了,第一个LSN和最后一个LSN在两个checkpoint中间的,注意operation :LOP_BEGIN_CKPT和 LOP_END_CKPT
checkpoint的时候,有7个脏页写到磁盘里去
大家再执行一下下面这个语句
1 SELECT [Current LSN], [Operation] ,[Transaction ID]
2 [Minimum LSN], [Dirty Pages], [SPID], [Transaction Name],
3 [Description]
4 FROM [##TDBLOG] ORDER BY [Current LSN] ASC
大家注意看,我在SSMS里执行backup database的查询窗口的spid是59,SQLSERVER是先做checkpoint,然后再做backup database的操作的
我是怎麽知道,fn_dblog输出的结果对应于哪个数据库呢?
因为刚开始使用这个函数,不知道怎麽指定输出的某个数据库的日志信息,而这个函数有一个字段是[Database Name],但是这个字段输出都是NULL
然后我通过观察SPID这个字段,发现其中某一行有数据库启动的信息,因为我在查询窗口里指定了如下sql语句,use AdventureWorks,
再看一下SQL ERROR LOG,真的是SPID20这个进程启动的数据库,然后我use 其他数据库,发现也是一样的
所以你要看某一个数据库的日志信息的时候先要 use 你要查看日志的那个数据库
1 USE [AdventureWorks]
2 GO
----------------------------------------------------------------------------------------------------------------------------------
关于问题3,其实这个“位置”就是指的是“日志链”
日志链
连续的日志备份序列称为“日志链”。 日志链从数据库的完整备份开始。 通常,仅当第一次备份数据库时,或者将恢复模式从简单恢复模式切换到完整恢复模式或大容量日志恢复模式之后,才会开始一个新的日志链。 除非在创建完整数据库备份时选择覆盖现有备份集,否则现有的日志链将保持不变。 在该日志链保持不变的情况下,便可从介质集中的任何完整数据库备份还原数据库,然后再还原相应恢复点之前的所有后续日志备份。 恢复点可以是上次日志备份的结尾,也可以是任何日志备份中的特定恢复点。 有关详细信息,请参阅事务日志备份 (SQL Server)。
若要将数据库还原到故障点,必须保证日志链是完整的。 也就是说,事务日志备份的连续序列必须能够延续到故障点。 此日志序列的开始位置取决于您所还原的数据备份类型:数据库备份、部分备份或文件备份。 对于数据库备份或部分备份,日志备份序列必须从数据库备份或部分备份的结尾处开始延续。 对于一组文件备份,日志备份序列必须从整组文件备份的开头开始延续。 有关详细信息,请参阅应用事务日志备份 (SQL Server)。
因为我的电脑里安装了yoursqldba,他会自动每天自动执行事务日志备份跟完整备份的job,所以就形成了这个日志链了
-----------------------------------------------------------------------------------------------------------------
补充:其实checkpoint也属于一个日志记录,图片应该像下面这样
minLSN:可以看到,最新的LSN是148,147是CheckPoint,在这个CheckPoint之前事务1已经完成,而事务2还未完成,
所以对应的MinLSN应该是事务2的开始,也就是142.
而从MinLSN到日志的逻辑结尾处,则称为活动日志(Active Log)。
-----------------------------------------------------------------------------------------------
根据园子里的careyson大侠说的,第一个问题
为什麽完整备份之后日志记录会增加,有时候备份完之后会增加,有时候备份完之后会减少
要说这个问题,大家要先看一下这篇文章:SQL Server 2008 存储结构之DCM、BCM
里面说到数据库有一个系统页面DCM页,这个页面记录了一个文件中的哪一个区在最新一次完整数据库备份以后被修改过
我们做一个实验,先做一次完整数据库备份,然后再做一次完整数据库备份,两次完整数据库备份之间,你都不要对数据库做任何操作
然后在SSMS里输入下面SQL语句
1 DBCC TRACEON(3604,-1)
2 DBCC PAGE([DLGPOS],1,6,3) --实际上是第7页
你会看到就算你没有对数据库做任何操作,但是数据库里的某些数据页依然也会被修改
但是究竟修改了多少数据,没有人知道,但是可以肯定的是SQLSERVER修改了一些数据,以至于在完整备份之后事务日志有所增加或减少
而在日志记录里会看到
LOP_SET_BITS->LCX_DIFF_MAP 位图
LOP_FILE_HDR_MODIFY->LCX_FILE_HEADER 数据库文件头部
LOP_MODIFY_ROW->LCX_BOOT_PAGE_CKPT 数据库启动页
以上这些都是数据库系统页的修改
如果对DCM页不是很理解可以看一下careyson里的文章
差异备份依靠一个BitMap进行维护,一个Bit对应一个区,自上次完整备份后,被修改的区会被置为1,
而BitMap中被置为1对应的区会被差异备份所备份。而到下一次完整备份后,BitMap中所有的Bit都会被重置为0。
http://www.cnblogs.com/CareySon/archive/2012/02/17/2355200.html#2702773
还有事务日志里有很多 LOP_LOCK_XACT->LCX_NULL ,估计这些操作是用来锁住系统保留页面的吧
-------------------------------------------------------------------------------------------------