在SQLServer数据库中打开CDCcausesthetransactionlogspacetofullduetoREPLICATION过程中提示,Thetransactionlogfordatabase'***'isfulldueto'REPLICATION'(thetransactionlogofthedatabase"***“已满,原因是“REPLICATION”)。CDC和replication的基本原理大致是换句话说,使用日志的步骤如下:1.每当基础表(启用了CDC或replication的表)产生事务性操作(增、删、改)时,相应的事务日志被写入日志文件。2、此时的日志被删除。状态标记为Replication,即处于待复制状态。这个active状态和数据库的restoremode没有关系,即使是简单的restoremode,3,然后有后台进程读取这个log,根据事务的内存写入目标表log,这个target是为CDC记录数据变化的系统表,写入分发库4进行复制。第3步完成后,事务日志被标记为正常。如果是简单恢复模式,会被后台进程解析过去的事务日志截断,可以重复使用。如果上面第三步出现问题,即后台进程无法解析日志并释放可用的日志空间,再次写入数据库,会出现:数据库“TestDB”事务日志已满,并且原因是“复制”。本文演示了启用CDC时日志空间已满的现象,以及相应的处理方法。测试环境搭建首先创建一个测试数据库,USEmasterGOCREATEDATABASETestLogFullONPRIMARY(NAME=N'TestLogFull',FILENAME=N'D:\DBFile\TestLogFull\TestLogFull.mdf',SIZE=500MB,MAXSIZE=UNLIMITED,FILEGROWTH=100MB)LOGON(NAME=N'TestLogFull_log',FILENAME=N'D:\DBFile\TestLogFull\TestLogFull_Log.ldf',SIZE=1MB,MAXSIZE=512MB)这里指定的日志文件最大大小为512M,主要是为了演示日志空间是full现象然后新建一个表,打开CDC测试USETestLogFull--enableCDCEXECUTEsys.sp_cdc_enable_db;GO--创建测试表createtabletest_cdc(idintidentity(1,1)primarykey,namenvarchar(50),mailvarchar(50),addressnvarchar(50),lastupdatetimedatetime)--启用CDCEXECsys.sp_cdc_enable_table@source_schema='dbo',@source_name='test_cdc',@role_name='cdc_admin',@capture_instance=DEFAULT,@supports_net_changes=1,@index_name=NULL,@filegroup_name=DEFAULTCDC成功启用,测试日志已满。这是当为某些表启用CDC时日志文件已满的演示。1、代理服务器未启动导致日志空间满。第3步,对于CDC来说,进程就是SQLServerAgent中的cdc.***_capturejob或者replicationagentjob读取日志。如果启用CDC或复制后SQLServerAgent关闭,或者服务器重启后SQLServerAgent没有重启随机自动启动可能会导致第2步的日志积压,即记录数据变化后的事务日志在复制状态无法复用,导致没有可用的日志,导致操作数据库时提示Thetransactionlogfordatabase'***'isfulldueto'REPLICATION'。此处暂时关闭代理服务(只是为了测试和演示此现象)可以通过增删改查生成事务日志。这里演示一下插入数据的情况。写一条SQL写入数据,将数据库写入到启用了CDC的表中。建库时,日志文件被限制为512M,因为网上启用了这张表CDC,写入数据的过程会产生事务日志。日志的空闲空间仅限于写入数据的进程。一开始,没有问题。随着数据的不断写入(Replication状态的日志不断积累),当日志被完全使用时,会产生如下错误。这时候观察事务日志的使用情况,发现已经满了,因为日志空间已经用满了。然后观察日志的waiting状态,此时是Replication状态尝试shrink也是无效的,因为日志都是active状态,active日志是无法shrink的。因为关闭了agent,无法执行读取日志的作业,导致日志被阻塞。然后打开代理看看是怎么回事。不?开启代理,查看CDC作业的执行状态。你会发现代理作业此时运行不正常。作业执行不成功。它还提示事务日志已满。此时观察测试表的cdctarget表不可用。有数据显示,此时即使开启了agent,cdcjob依然没有执行成功,那么为什么CDCagentjob不能正常执行呢?其实不难理解,cdc作业也是读取事务日志,写入数据。也相当于一个事务性的操作,必须借助日志来实现,此时没有可用的日志空间,这个作业当然会失败那么这个时候怎么办呢?由于日志被阻塞,所以尝试清理这部分活动日志,并尝试将事务日志标记为分布式(虽然这是CDC,但日志的使用应该与副本相同)根据我的测试,执行完上面的语句,将复制的东西标记为分布式后,再次查看日志使用率,发现还是100%,但是尝试写入数据时成功,再次写入数据(一条就够了)之后,日志空间开始释放。应该是当日志被标记为分布式时触发写入时被截断,即释放100%占用的日志空间,然后观察日志的使用情况。发现和预期的一样,这部分日志被截断了,日志空间不再被完全占用,日志变成了Nothing(可重用)。这个测试表明,如果启用了CDC,SQLServerAgent没有正常启动或者相应的job没有正常启动,日志空间会被不断产生的东西填满,导致数据库无法正常启动执行写操作。这里通过手动将日志标记为分布式来释放日志。在这种情况下,cdc日志会被打断,即手动释放的日志无法投递到下游(cdc日志表)。毕竟,这不是一个很好的方法。下面将解释另一种方式。2.短时间内大量事务操作导致日志空间满的情况和关闭代理服务导致日志被阻塞的情况不同。这里直接开启了代理服务,仍然使用下面的脚本去往表中写入数据(比如实际业务中批量导入数据),写入一段时间后,事务日志依旧满的。为什么?先从疾控中心的代理任务说起。这个agent的job虽然是连续执行的,但是因为上面的数据是连续写入的,也就是不断产生日志,因为日志文件的大小是有限制的(这里为了演示方便,限制为512M),日志文件空间使用有限。这里可以认为一个session消耗日志空间(Insert操作),一个进程解析完日志后释放日志空间(agentjob),但是消耗速度高于释放速度。一旦日志空间用完,CDC代理作业也无法完成,从而导致出现上述情况:日志空间已满,数据库无法进行任何写操作,无法执行CDC作业释放可复用日志空间。上面是通过手动标记事务日志的状态来解决的当日志文件已满时,手动标记日志为分布式是不合适的。一旦日志被标记为分布式,它就不会被传递到CDC系统表或订阅者。这里还有一个解决这个问题的方法:既然当前日志已经满了,就添加一个日志,注意新添加的日志的初始空间不能太小。(有兴趣测试的朋友请在此处添加日志文件后耐心等待一两分钟)然后后续的CDC作业将借助新添加的日志空间继续执行。如果日志的大小有限(或者存储日志的磁盘空间不足),并且数据库中开启了CDC或者replication。一旦数据发生大量连续写入操作(增删改查),此时SQLServer代理可能无法解析并释放日志。也可以3.不增加日志文件空间,不增加日志文件,重启SQLServer服务。本人重现此现象并尝试解决时也尝试了此方法。可行性不是太强,但还是要说明,那就是重启大法。同时,重启后的日志文件也发生了一些有趣的变化。建库时,日志文件被限制为***512M,并没有手动将日志标记为分布式,但重启SQLServer服务后,如果存放日志的磁盘有空间,日志会自动扩容一部分,有了这部分扩展日志,代理作业就可以解析Replication状态的日志(稍后),释放日志空间(解析和释放日志需要一段时间,根据要处理的日志量而定)copied)下图可以清楚的看到,日志限制是512MB,但是初始化为556MB,明显大于最大日志大小。这是由于重新启动SQLServer服务的结果。2014SP2版本下测试的现象,如果是SQLServer2014(非SP2补丁版),开启CDC,日志满了,不会出现下面的情况,也就是说,日志不会重启后就自动展开了,我也是醉了,验证一些东西确实不容易。这些小细节也和补丁版本有关,但是这部分方法不能作为经验!总结:启用CDC后,相关表的变化会写入事务日志(日志状态为Replication状态),代理任务会解析日志,解析后将日志标记为可重建状态(如果是简单恢复模式,可以复用,如果是完全恢复模式,日志备份不能截断Replication状态下的日志)在这种状态下,如果受限日志的最大尺寸比较小,或者没有限制,存储日志的磁盘空间不足,大量写入数据(增删改查)时,生成的日志可能会填满日志文件某些情况下,释放日志的代理作业log无法执行,proxyjob无法执行,log无法释放,就好像死循环一样。这时候要么增加一个新的日志文件或者增加日志文件的最大大小,要么执行系统存储过程sp_repldone将事务标记为分布式(将事务日志标记为可重用)来解决这个问题。
