背景
数据库开启CDC功能后,每天会生成大量的历史记录,即使达到参数“每个作业的最大历史记录“的阈值后也不会被删除,导致其它作业的历史记录被删除,无法查看以前的执行情况,非常不方便。
现象
数据库开启CDC后会创建“capture”和“cleanup”两个作业,capture作业会生成大量的历史记录,而且参数“每个作业的最大历史记录”不起作用,一直不删除。
分析
首先通过跟踪定位到SQL Agent服务在执行作业时调用存储过程msdb.dbo. sp_sqlagent_log_jobhistory处理作业历史记录。查看该存储过程的定义,首先把当前运行数据插入到历史记录表中。
然后通过@step_id判断是整个作业完成的记录还是执行过程中步骤的记录,如果是作业已经完成的记录,执行存储过程msdb.dbo.sp_jobhistory_row_limiter对历史记录进行清理。
查看存储过程msdb.dbo.sp_jobhistory_row_limiter的定义,首先从注册表中获取参数“所有作业最大行数”和“每个作业最大行数”的值。
先根据参数“每个作业最大行数”判断当前作业是否超过限制,并删除旧的记录。
然后根据参数“所有作业最大行数”判断所有作业历史记录是否超过限制,并删除旧的记录。
接下来分析为什么CDC的历史记录无法删除,通过跟踪定位到捕获作业调用存储过程[sys].[sp_cdc_scan]在一个没有退出方式的循环中扫描事务日志,并调用存储过程msdb.dbo. sp_sqlagent_log_jobhistory 记录历史记录,因为传递的@step_id的值代表这是执行步骤的记录,不是作业完成的记录,因此不会触发清理日志的存储过程,导致历史记录越积累越多。
总结:
- capture作业一直是运行的,所以记录的都是作业步骤的日志,不会执行到清理历史记录的步骤,导致历史记录越积累越多;
- 其它作业执行完成清理历史记录时,因为capture作业的历史记录越积累越多,达到“所有作业最大行数”的阈值,删除所有作业旧的历史记录。capture作业运行频繁,所以保留的大都是capture作业的历史记录,其它运行不频繁的作业的历史记录被删除。
解决
方法一:创建一个新的作业,定期删除CDC的作业历史记录。
CREATE PROC [dbo].[usp_gt_delete_cdc_job_history]ASBEGIN DECLARE @job_id UNIQUEIDENTIFIER;
DECLARE cdc_job_cursor CURSOR FOR SELECT job_id FROM msdb.dbo.cdc_jobs WHERE job_type = 'capture';
OPEN cdc_job_cursor; FETCH NEXT FROM cdc_job_cursor INTO @job_id; WHILE @@FETCH_STATUS = 0 BEGIN EXECUTE msdb.dbo.sp_jobhistory_row_limiter @job_id;
FETCH NEXT FROM cdc_job_cursor INTO @job_id; END
CLOSE cdc_job_cursor; DEALLOCATE cdc_job_cursor;END
方法二:到msdb中修改存储过程的定义,增加对CDC作业处理的逻辑。该方式已经反馈给微软,在SQL Server下一个版本会采用,也算对SQL Server做一点小贡献。
以下是具体的代码。
ELSE
BEGIN
IF EXISTS(SELECT 1 FROM msdb.sys.tables WHERE schema_id = SCHEMA_ID('dbo') AND name = 'cdc_jobs')
BEGIN
IF EXISTS(SELECT 1 FROM msdb.dbo.cdc_jobs WHERE job_id = @job_id AND job_type = 'capture')
BEGIN
EXECUTE msdb.dbo.sp_jobhistory_row_limiter @job_id
END
END
END
产品&服务
SQL专家云数据库智能运维平台
Moebius数据库多活集群
数据库协作运维服务