mssql中开启cdc导致事务日志空间被占满事件记录(代码片段)

author author     2023-01-12     260

关键词:

问题描述

SQLServer中开启CDC之后,在某些情况下会导致事务日志空间被占满的现象为: 在执行增删改语句(产生事务日志)的过程中提示,The transaction log for database *** is full due to REPLICATION(数据库“***”的事务日志已满,原因为“REPLICATION”).

CDC以及复制的基本原理粗略地讲,对于日志的使用步骤如下:   1、每当基础表(开启了CDC或者replication的表)产生事务性操作(增删改)之后,对应的事务日志写入日志文件;   2、此时的日志被状态被标记为Replication,也即处于待复制状态,这个活动状态跟数据库的还原模式无关,即便是简单还原模式;   3、然后有后台进程来读取这个日志,根据事务日志的内存写入目标表,这个目标对于cdc来说是记录数据变化的系统表,对于replication来说是写入distribution这个库;   4、步骤3完成之后,事务日志被标记为正常状态,如果是简单还原模式,被后台进程解析过的事务日志被截断,可以重用;

如果上述中间的第三个步骤出现问题,也即后台进程无法解析日志后释放可用的日志空间,再次往数据库中写入操作,就会出现:数据库“TestDB”的事务日志已满,原因为“REPLICATION”的情况

本文通过通过演示开启CDC的情况下日志空间被占满的现象,以及对应的处理办法

测试环境准备

首先建立一个测试数据库

USE master
GO
CREATE DATABASE TestLogFull ON PRIMARY 
( 
    NAME = NTestLogFull, 
    FILENAME = ND:\\DBFile\\TestLogFull\\TestLogFull.mdf , 
    SIZE = 500MB , 
    MAXSIZE = UNLIMITED, 
    FILEGROWTH = 100MB 
)
LOG ON 
(
    NAME = NTestLogFull_log, 
    FILENAME = ND:\\DBFile\\TestLogFull\\TestLogFull_Log.ldf , 
    SIZE = 1MB , 
    MAXSIZE = 512MB 
)

这里创建了一个名为TestLogFull的数据库指定日志文件的最大为512M,主要是为了演示日志空间被占满的现象; 接着开启新建一个表同时开启CDC来测试

USE TestLogFull
--启用CDC
EXECUTE sys.sp_cdc_enable_db;
GO

--创建一张测试表
create table test_cdc
(
    id int identity(1,1) primary key,
    name nvarchar(50),
    mail varchar(50),
    address nvarchar(50),
    lastupdatetime datetime
)

--对表启用CDC
EXEC sys.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           = DEFAULT

CDC开启成功,开始测试日志被占满的情况

SELECT NAME, CASE is_tracked_by_cdc WHEN 1 THEN 已开启CDC ELSE ‘未开启 CDC’ END AS result 
FROM sys.TABLES 
WHERE OBJECT_ID = OBJECT_ID(dbo.test_cdc)

问题分析

演示对某些表开启CDC的情况下日志文件文件被占满的三种情况:

1、代理服务器未启动导致日志空间被占满

文中一开始提到的步骤3,对于CDC,进程就是SQL Server Agent中的cdc.***_capture作业或者复制代理作业来读取日志,如果SQL Server Agent在开启了CDC或者复制之后被关闭,或者重启服务器之后SQL Server Agent没有随机自动启动 就有可能造成步骤2中的日志积压,也就是记录数据变化之后的事务日志处于replication状态,无法重用,导致没有可以使用的日志,致使发生操作数据库的时候提示The transaction log for database *** is full due to REPLICATION.

  这里暂时关闭代理服务(仅仅是为了测试演示这一现象)

增删改都可以产生事务日志,这里就演示insert数据的情况,做一个写数据的SQL,往开启了CDC的表中写数据库,在建库的时候日志文件有限制成了512M,因为这个表上开启了CDC,写数据这个过程会产生事务日志,日志有空空间限制,在写入数据的过程中,一开始是没有问题的,随着数据的不断写入(Replication状态的日志不断积压),当日志全部使用之后,下面的报错就会产生了

--模拟一个大批量的事务性操作
while 1=1
begin
    insert into test_cdc values (newid(),newid(),newid(),GETDATE())
end

此时观察事务日志的使用情况,发现已经是完全使用了

--查看日志使用率
DBCC SQLPERF(LOGSPACE);

因为日志空间被完全使用了,那么观察一下日志的等待状态,是Replication状态

--日志状态模式查询
select [name],[database_id],[log_reuse_wait],[log_reuse_wait_desc] 
from [sys].[databases] where name = TestLogFull;

此时尝试收缩也是无效的,因为日志都是出于活动状态,活动状态的日志是无法收缩的

--尝试收缩日志文件
DBCC SHRINKFILE(NTestLogFull_Log ,100)

可见,因为代理被关闭,读取日志的作业无法执行,造成日志堵塞,那么开启代理来看看到底行不行? 开启代理,查看CDC作业的执行情况,会发现,此时代理作业也不好使了,作业执行的时候并没有成功,一样提示说事务日志已满

此时观察测试表的cdc目标表没有任何数据,说明此时即便开启了代理,cdc的作业依然没有成功执行

那么这里为什么CDC的代理作业也无法正常执行? 其实也不难理解,cdc的作业也是读取事务日志写数据的,这中间也相当于有事务性操作,必须要借助日志来实现,而此时又没有可用的日志空间,这个作业当然要失败了。

那么此时怎么办? 既然是日志堵塞了,就想办法清理到这部分活动日志,尝试将事务日志标记为已分发(虽然这里是CDC,但是对于日志的使用应该是跟复制一样的)

--日志中所有复制的事务将标记为已分发
EXEC sp_repldone @xactid=NULL,@xact_segno=NULL,@numtrans=0,@time=0,@reset=1;

根据本人的测试,在执行上面的语句,将复制的事物标记为已分发之后,再次查看日志使用率,发现还是100%,但是尝试写入数据的时候是成功的,再次写入数据(一条即可)之后,日志空间开始释放,应该是写入时候的时候触发被标记为已分发的日志截断,也就是将上面占用了100%的日志空间释放出来,然后再观察日志的使用率,发现如预期的,这部分日志已被截断,日志空间不再是被完全占用了,日志变成Nothing状态(可重用)

--查看日志使用率
DBCC SQLPERF(LOGSPACE);
--日志状态模式查询
select [name],[database_id],[log_reuse_wait],[log_reuse_wait_desc] 
from [sys].[databases] where name = TestLogFull;

这个测试说明,如果开启了CDC,SQL Server代理没有正常启动或者对应的作业没有正常启动,日志空间会随着不断产生的事物被占满,导致数据库无法进行写入性操作,这里是用过手动标记日志为已分发的方式来释放日志的,这种情况下会导致cdc日志断裂的情况,也就是手动释放的日志无法传递到下游(cdc日志表),毕竟不是一个太好的办法,下面会说明另外一种办法。

2、短时间内较大的事务性操作导致的日志空间被占满的情况

对上面所说的代理服务被关闭导致日志堵塞的情况不同,这里直接开启代理服务,依旧拿着下面的脚本往表中写数据(比如实际业务中批量导入数据之类的) 在写入一段时间之后,依然出现了事务日志被填满的情况,这又是为什么?

--模拟一个大批量的事务性操作
while 1=1
begin
    insert into test_cdc values (newid(),newid(),newid(),GETDATE())
end

还要从CDC的代理任务说起,这个代理的JOB虽然是连续执行的,但是因为上面写数据的时候也是连续写入的,也就是日志是连续产生的,因为限制了日志文件的大小(这里为了方便演示,限制为512M),日志文件有最大使用空间的限制。这里可以认为是一个Session消耗日志空间(Insert操作),一个进程解析日志之后释放日志空间(代理作业),但是消耗的速度要高于释放的速度,一旦日志空间被使用完,CDC的代理作业也无法完成,这样就又造成了上面的情况:日志空间被填满,数据库无法执行任何写入操作,CDC作业也无法执行从而释放可重用的日志空间,上面是通过手动标记事务日志的状态来解决日志文件被填满的,直接手动标记日志为已分发的做法是有点不合适的,一旦标记日志状态为已分发,接下来他就不会传递给CDC的系统表或者订阅端了,这里通过另外一种方法来解决此问题:既然当前日志占满了,就在添加一个日志,注意新加日志初始化的空间不要太小。(有兴趣测试的盆友,这里添加完日志文件后注意耐心等待一两分钟)然后随后的CDC作业会借助新加的这个日志空间会继续执行

如果是当前逻辑此磁盘空间充足,也可以通过扩大事务日志文件来实现

ALTER DATABASE TestLogFull
MODIFY FILE (
                NAME = NTestLogFull_log, 
                FILENAME = ND:\\DBFile\\TestLogFull\\TestLogFull_log.ldf , 
                SIZE = 600MB , 
                MAXSIZE = UNLIMITED, 
                FILEGROWTH = 100MB 
            )

此种情况说明,如果限制了日志的大小(或者存储日志的磁盘空间不足),数据库中开启了CDC或者复制,   一旦数据出现大批量持续性写入操作(增删改),此时会出现SQL Server代理解析并释放日志的速度跟不上,也有可能造成日志被占满的情况

3、不增加日志文件空间或者添加日志文件情况下重启SQLServer服务

  这个办法也是本人在重现这一现象并尝试解决的时候试出来的,可行性不是太强,但还是说明一下,那就是重启大法,同时重启之后日志文件也发生了一些有意思的变化   建库的时候日志文件限制为最大512M,同时没有手动标记标记日志为已分发状态,但是重启SQLServer服务之后,如果存放日志的磁盘有空间,这个日志会自动扩充一部分   然后有了这部分扩充出来的日志,代理job就可以解析Replication状态的日志(之后)就可以释放日志空间了(需要一段时间来解析并释放日志,根据待复制的日志量有关)   下图可以明显看到,日志限制为512MB,但是初始化为556MB,明显大过最大日志大小,这个是归功于重启SQLServer服务的结果

  一下是在SQL Server 2014 SP2版本下测试的现象,

如果是SQL Server 2014(非SP2补丁版),开启CDC的方式占满日志则不会出现如下的情况,也就是说重启有日志并不会自动扩充一部分,我也是醉了,验证个东西真不容易,这些小细节跟补丁版本也有关系,不过这种偏门的方法不能作为经验!

总结

当开启了CDC之后,在相关表上的变化会写入事务日志(日志状态为Replication状态),代理任务会解析日志,解析完日之后标记日志为可重建状态(如果是简单还原模式,是可重用,如果是完整还原模式,日志备份也无法截断Replication状态的日志),这种状态下如果限制了日志的最大大小比较小,或者没有限制,存储日志的磁盘空间不足,在大批量写入数据(增删改)的时候,有可能产生的日志占满日志文件的情况,会导致释放日志的代理作业无法进行,代理作业无法进行又无法释放日志,仿佛是死循环。此时要么新增日志文件或者增加日志文件的最大大小,要么通过执行系统存储过程sp_repldone来标记事务为已分发(标记事务日志可重用)来解决这一问题。

数据库事务jeeps日志已满,原因为replication?

...长,并修改其中文件增长幅度即可。参考技术ASQLServer中开启CDC之后,在某些情况下会导致事务日志空间被占满的现象为:在执行增删改语句(产生事务日志)的过程中提示,Thetransactionlogfordatabase'***'isfulldueto'REPLICATION... 查看详情

45.jvm调优策略常见问题:内存泄漏(年老代堆空间被占满持久代被占满堆栈溢出线程堆栈满系统内存被占满)优化方法:优化目标优化gc步骤优化总结;案例分析(公司系统参数网上给的配置参数)(代码片段)

45.JVM调优策略45.1.常见问题45.1.1.内存泄漏45.1.1.1.年老代堆空间被占满45.1.1.2.持久代被占满45.1.1.3.堆栈溢出45.1.1.4.线程堆栈满45.1.1.5.系统内存被占满45.2.优化方法45.2.1.优化目标45.2.2.优化GC步骤45.2.3.优化总结45.3.案例分析45.3.1.案例1I... 查看详情

生产环境出现的几次线程池被占满的问题分析

...供的一个接口,php的这个服务由于一些未知原因特别慢,导致我们的http请求花了很长时间,这时候dubbo的线程一直得不到释放,由于A系统这时候频繁调用B系统,导致B系统的dubbo线程池线程很快被耗尽(dubbo服务提供方线程池默... 查看详情

db2数据库的事务日志已满怎么解决

参考技术A扩容,无论是循环日志还是归档日志,都要保证事务日志的目录空间足够即必须保证(primary+seconday)*logfilesize比文件系统小,才能保证数据库正常运行 查看详情

debezium的增量快照(代码片段)

...认情况下,事件流的捕获会在consistentsnapshot完成之后开启,不同数据量情况下,这个过程可能会耗费数小时乃至数天,并且一旦这个过程由于某些异常因素停止,那重新开启后,它将从头开始执行。为了解... 查看详情

腾讯云服务器被攻击,带宽被占满的抗战日记(代码片段)

...某人气不过,开始DDOS,占满服务器的带宽,导致网站访问非常缓慢。杂记根据腾讯云后台+宝塔后台,带宽被占满:(小垃圾水管没办法) 部分参考:记一次阿 查看详情

技术分享mysql中mgr中secondary节点磁盘满,导致mysqld进程被oomkilled

参考技术A在对MySQL8.0.26vsGreatSQL8.0.25的对比测试过程中,有一个环节是人为制造磁盘满的场景,看看MGR是否还能正常响应请求。在实测过程中,最后发现磁盘满的那个节点,持续时间足够久后,会因为内存消耗过大而最终被OS给OOM... 查看详情

mssql分析数据库日志文件无法收缩的问题(代码片段)

...erver2008R2数据库中,无法对数据库日志进行收缩,导致日志不断膨胀。二、问题分析由于是日志文件不断增大且无法收缩,所以初步判断为存在未提交的事务。检查可能阻止日志阶段的活动事务,执行:DBCCOPENT... 查看详情

磁盘inode节点被占满的解决方法

Linux服务器,查看日志发现程序无法继续写文件,但是用df-h查看磁盘容量还有剩余。排查思路:怀疑是机器的inode节点被占满,使用df-i查看磁盘inode节点使用情况,果然是inode节点满了。进行如下步骤进行排查:1,df-i查看磁盘节... 查看详情

linux/dev/mapper/centos-root被占满

...哪个目录占用最大  逐一排查下去,原来是项目中的Log日志,删掉就好了 查看详情

linux磁盘空间被占满?清空回收站试试!(代码片段)

今天本来想训练一个模型的,但是奈何数据量非常大,还没全下载到本地就把我磁盘撑满了,经过一系列,但是仔细一查好像不是那么回事,记录一下解决过程。首先看了下HDFS上的数据:hadoopfs-du-h第一列... 查看详情

什么是mysqlbinlog

...至解决此类问题。特性描述MySQL从8.0.20开始集成ZSTD算法,开启压缩功能后;以事务为单位进行压缩写入二进制日志文件,降低原文件占用的磁盘空间。压缩后的事务以压缩状态有效负载在复制流中发送到从库(MGR架构中为组member... 查看详情

mssql分析数据库日志文件无法收缩的问题(代码片段)

...erver2008R2数据库中,无法对数据库日志进行收缩,导致日志不断膨胀。二、问题分析由于是日志文件不断增大且无法收缩,所以初步判断为存在未提交的事务。检查可能阻止日志阶段的活动事务,执行:DBCCOPENT... 查看详情

mysql事务日志undolog详解

...比如服务器本身的错误,操作系统错误,甚至是突然断电导致的错误。情况二:程序员可以在事务执行过程中手动输入ROLLBACK语句结束当前事务的执行以上情况出现,我们需要把数据改回原先的样子,这个过程称之为回滚,这样... 查看详情

遇到的问题如何解释与解决

/dev/null垃圾箱,黑洞空间提示不足有可能是INODE号被占满涉及到.与..的文件时ls选项要加上-d选项为什么要用-d因为ls选项会自动列出目录下的文件,每一个目录下都存在(.也就是当前目录)与(..上一级目录)。不加d的话,*.会... 查看详情

记一次xstream引起的内存泄漏

...有80%的内存无法被回收,基本确认是系统出现内存泄漏,导致老年代空间被占满,频繁触发fullgc,fullgc触发stoptheword,导致业务接口超时。 二、承2.1、dump内存数据#netstat-tunlp|grep端口号#jmap-dump: 查看详情

控制kvm-qcow2增长空间-

...删除结合文件系统的写入机制,最终磁盘镜像空间被占满导致宿主机空间不足。 结束通过减少分区容量的方式,延缓磁盘消耗进展。本方式适合普通文件系统的修改,对lvm逻辑卷管理将独立说明。 准备工具1。gparted-live&n... 查看详情

一个开启多个事务导致optimisticlockexception异常的问题

...其他的事物中被修改,而造成这一个问题的原因是:同时开启了两个事务,修改了同一个对象。解决方式就是:让对象在同一个事务中修改。  我使用的是cuba框架,这个框架可以使用DataManager来操作数据,也可以使用JPA的Entity... 查看详情