其实公司从很久之前就开始用CDC做数据同步,之前的博客也记录过使用CDC过程中遇到的问题。
无法更改列 ‘xxx’,因为它是 ‘REPLICATED’。
CDC的ct表出现违反唯一约束错误
今天打算把CDC相关知识点具体总结一下:
一.开启库级别CDC
--查询数据库是否启用CDC:1 成功; 0 失败
select name,is_cdc_enabled from sys.databases where name=db_name;
--开启库级别CDC:
EXEC sys.sp_cdc_enable_db
错误号 15517 :某个/些存储过程使用了具有WITHEXECUTE AS 的选项。使其在当前库具有了某个架构,但是当在别的地方执行时,由于没有这个架构,所以就报错
解决方法:
ALTER AUTHORIZATION ON DATABASE::[AdventureWorks] TO [sa]
--禁用数据库
EXEC sys.sp_cdc_disable_db
二.开启表级别CDC
exec sys.sp_cdc_enable_table
[ @source_schema = ] 'source_schema',--是源表所属的架构的名称
[ @source_name = ] 'source_name' ,--是对其启用变更数据捕获的源表的名称。source_name 必须存在于当前数据库中。不能对 cdc 架构中的表启用变更数据捕获。
[ @role_name = ] 'role_name'--是用于控制更改数据访问的数据库角色的名称。如果显式设置为 NULL,则没有控制角色用于限制对更改数据的访问。
[,[ @capture_instance = ] 'capture_instance' ]--是用于命名特定于实例的变更数据捕获对象的捕获实例的名称,源表最多可以有两个捕获实例
[,[ @supports_net_changes = ] 1 --将为捕获实例生成一个净更改函数。对于在调用中指定的时间间隔内发生更改的每个非重复行,此函数仅返回一项更改。若要支持净更改函数,原表必须具有主键或唯一索引。如果使用了唯一索引,则必须使用@index_name参数指定索引名称。在逐渐或唯一索引中定义的列必须包含在要捕获的源列列表中。
[,[ @index_name = ] 'index_name' ] --由于指定@supports_net_changes ,则需要指定唯一索引名称
[,[ @captured_column_list = ] 'captured_column_list' ]--标识将包括在更改表中的源表列。如果为 NULL,则所有列都将包括在更改表中。captured_column_list 是以逗号分隔的列名称列表。
[,[ @filegroup_name = ] 'filegroup_name' ]--是要用于为捕获实例创建的更改表的文件组。如果为 NULL,则使用默认文件组。
[,[ @partition_switch = ] 'partition_switch' ]--是否可以对启用了变更数据捕获的表执行 ALTER TABLE 的 SWITCH PARTITION 命令。
--为test表开启变更捕获
exec sys.sp_cdc_enable_table @source_schema='dbo',
@source_name='test',
@role_name=null,
@capture_instance=DEFAULT
--查询表是否启用成功:1 成功; 0 失败
SELECT name , is_tracked_by_cdc ,
CASE WHEN is_tracked_by_cdc = 0 THEN 'CDC功能禁用' ELSE 'CDC功能启用'
END as 描述
FROM sys.tables
WHERE OBJECT_ID IN( OBJECT_ID('dbo.test'))
表开启CDC后,数据库系统表下回自动创建名为 cdc.schemaname_tablename_CT,当向表中实际修改数据时,变更数据会写入该表。其中删除增加一行,更新增加两行,分别为更新前和更新后,由__$operation列进行标识:1 = 删除、2= 插入、3= 更新(旧值)、4= 更新(新值)
此时sqlserver代理作业下会多两个作业,一个是capture作业,用户变更捕获,一个是cleanup作业,用于清理过期数据。
所以CDC功能需要SQL代理可以正常工作
三.常用SQL
下面是一些常用查询及存储过程:
--查询已经开启的捕获实例
exec sys.sp_cdc_help_change_data_capture
--查看对某个实例(即表)的哪些列做了捕获监控:
EXEC sys.sp_cdc_get_captured_columns
@capture_instance = 'dbo_table_name' -- sysname
--查找配置信息
SELECT * FROM msdb.dbo.cdc_jobs
--查看当前配置
EXEC sp_cdc_help_jobs
--maxtrans:捕获作业每次循环时要处理的最大事务数
--maxscans:每次循环次数
--continuous:1:连续运行;0:间隔运行
--pollinginterval:日志扫描周期之间的秒数
--retention:变更数据保留的分钟数,默认保留3天
--更改数据保留时间为100分钟
EXECUTE sys.sp_cdc_change_job
@job_type = N'cleanup',
@retention=100
--停用作业,不会丢失变更数据捕获已捕获的数据
EXEC sys.sp_cdc_stop_job N'cleanup'
--启用作业
EXEC sys.sp_cdc_start_job N'cleanup'
--删除作业
EXEC sys.sp_cdc_drop_job @job_type = N'cleanup' -- nvarchar(20)
--创建作业
EXEC sys.sp_cdc_add_job
@job_type = N'cleanup',
@start_job = 0,
@retention = 5760
CDC也可用于DDL变更捕获
--查询DDL记录
SELECT * FROM cdc.ddl_history
对开启CDC的表进行DDL操作,除非是syssdmin,db_owner,ddladmin角色,其他成员身份更改表结构都会报权限错误,即使显式为该角色赋予该表的DDL权限
--使用CDC函数来获取更改
DECLARE @from_lsn binary(10), @to_lsn binary(10)
SET @from_lsn =
sys.fn_cdc_get_min_lsn('dbo_table_name')
SET @to_lsn = sys.fn_cdc_get_max_lsn()
SELECT * FROM cdc.fn_cdc_get_all_changes_HumanResources_Department
(@from_lsn, @to_lsn, N'all update old');
获取某个时间段的更改信息:
–先根据日志序列号(logsequence number ,LSN)来获取跟踪变更数据:
–Sys.fn_cdc_map_time_to_lsn获取变更范围内的最大、最小LSN值。可以使用:
–Smallest greater than;smallest greater than orequal;largest less than;largest less than or equal.
DECLARE @bglsn VARBINARY(10)=sys.fn_cdc_map_time_to_lsn('smallest greater than or equal','2019-03-05 12:00:00.997')
DECLARE @edlsn VARBINARY(10)=sys.fn_cdc_map_time_to_lsn('largest less than or equal',GETDATE())
SELECT id,name
FROM cdc.dbo_test_CT
WHERE [__$operation]=2 AND [__$start_lsn] BETWEEN @bglsn AND @edlsn
--sys.fn_cdc_map_lsn_to_time 查询变更时间:
SELECT [__$operation] ,
CASE [__$operation] WHEN 1 THEN '删除' WHEN 2 THEN '插入' WHEN 3 THEN '更新(捕获的列值是执行更新操作前的值)'
WHEN 4 THEN '更新(捕获的列值是执行更新操作后的值)' END [类型],
sys.fn_cdc_map_lsn_to_time([__$start_lsn]) [更改时间] ,
ID,
NAME
FROM cdc.dbo_test_CT
--获取LSN边界
SELECT sys.fn_cdc_get_max_lsn(),--[数据库级别的最大LSN]
sys.fn_cdc_get_min_lsn('cdc.dbo_test_CT')--[捕获实例的lsn]
四.CDC表中添加新列
原表开启CDC后,在表上新增列,是不会自动捕获到CDC中的。需要重新激活新添加的列的CDC。
有两种方法:1.若CT表中数据允许丢失,则可以直接关闭CDC后重开,此时CT表也会删除重建,表结构与更改后的表结构一致
2.若无法接受数据丢失,则可以采用下列步骤新建一个捕获实例,详细步骤如下:
1.原表添加新列
2.添加新的cdc-capture inctance
3.将数据从旧表移动到新表
4.禁用旧的cdc实例
5.此解决方案不会丢失数据,但CDC表名称会改变。如果可以接受,则步骤结束;如需保持原来的表名称,则可以进行第6,7步的反向操作。因为ct表无法进行sp_rename操作
6.添加新的实例,名称为旧的CT表名称,将数据反向迁移后禁用捕获实例
--添加新列
alter table test add loc varchar(20) null
--添加新的cdc-capture inctance
exec sp_cdc_enable_table
@source_schema = N'dbo',
@source_name = N'test',
@role_name = NULL,
@filegroup_name =null,
@capture_instance = 'loc'--新的系统表名称(cdc.loc_ct)
--将数据从旧表移动到新表
INSERT INTO cdc.loc_ct
(__$start_lsn, __$end_lsn,__$seqval,__$operation,__$update_mask,Id,Name)
SELECT
__$start_lsn,
__$end_lsn,
__$seqval,
__$operation,
__$update_mask,
Id,
Name
FROM cdc.dbo_test_CT
--禁用旧的cdc实例
EXEC sp_cdc_disable_table
@source_schema = N'dbo',
@source_name = N'test',
@capture_instance = 'dbo_test'
--此操作会更改捕获实例名称,禁用cdc实例后,原来的'cdc.dbo_test_ct'系统表会被删除,只有新建的'loc_ct'.
--若想保证cdc实例名称不变,可以进行反操作。新建cdc实例为'dbo_test',插入数据,并禁用'loc'.
exec sp_cdc_enable_table
@source_schema = N'dbo',
@source_name = N'test',
@role_name = NULL,
@filegroup_name =null,
@capture_instance = 'dbo_test'--新的系统表名称(cdc.loc_ct)
--将数据从旧表移动到新表
INSERT INTO cdc.dbo_test_CT
(__$start_lsn, __$end_lsn,__$seqval,__$operation,__$update_mask,Id,Name)
SELECT
__$start_lsn,
__$end_lsn,
__$seqval,
__$operation,
__$update_mask,
Id,
Name
FROM cdc.loc_CT
--禁用旧的cdc实例
EXEC sp_cdc_disable_table
@source_schema = N'dbo',
@source_name = N'test',
@capture_instance = 'loc'
--原表中数据不需要保留,则直接禁用cdc实例后,在启用cdc.此时系统表中的表也将删除并重建。
--禁用cdc实例
EXEC sp_cdc_disable_table
@source_schema = N'dbo',
@source_name = N'test',
@capture_instance = 'dbo_test'
--启用cdc实例
EXECUTE sys.sp_cdc_enable_table
@source_schema = N'dbo'
, @source_name = N'test'
, @capture_instance=DEFAULT
五.管理和监视变更捕获作业
捕获进程效率的另一个重要度量值是吞吐量。它是在每个会话期间每秒处理的平均命令数。若要确定会话的吞吐量,请将 command_count 列中的值除以持续时间列中的值。以下查询返回最近会话的平均吞吐量:
SELECT command_count/duration AS [Throughput] FROM sys.dm_cdc_log_scan_sessions WHERE session_id = 0
若要确定有空扫描的会话,请运行以下查询:
SELECT * from sys.dm_cdc_log_scan_sessions where empty_scan_count <> 0
六.还原或附加启用了变更数据捕获的数据库
SQL Server 使用以下逻辑确定还原或附加数据库后变更数据捕获是否继续保持启用状态:
如果数据库以同一数据库名称还原到同一服务器,变更数据捕获将保持启用状态。
如果数据库还原到其他服务器,默认情况下将禁用变更数据捕获,并删除所有相关的元数据。
若要保留变更数据捕获,还原数据库时请使用 KEEP_CDC 选项。有关此选项的详细信息,请参阅 RESTORE。
如果数据库在分离后附加到同一服务器或其他服务器,变更数据捕获将保持启用状态。
如果使用 KEEP_CDC 选项将数据库附加或还原到 Enterprise 以外的任何版本,操作将被阻止,因为变更数据捕获需要 SQL Server Enterprise。将显示错误消息 932