CDC(数据变更捕获)最初是由ORACLE实现的,在数据库级别实现的增量抽取解决方案。
在CDC之前,在OLAP(联机分析处理)和ETL(数据仓库技术)开发中,对于增量抽取主要有如下几种方法:
① 时间截
在源表上增加一个时间戳字段,系统中更新修改表数据的时候,同时修改时间戳字段的值。当进行数据抽取时,通过比较上次抽取时间与时间戳字段的值来决定抽取哪些数据。
部分数据库不支持时间戳自动更新,即表中数据发生改变时,不会自动更新时间戳字段的值,这就要求业务系统在更新业务数据时,手工更新时间戳字段。
优点:时间戳性能较好,结构清晰,实现简单,可以实现数据的递增加载。
缺点:添加字段对数据表有较大侵入性,尤其是在已经完成并正在使用的功能上,突然添加一个字段,需要考虑业务系统是否受到影响;对不支持时间戳的自动更新的数据库,还要求业务系统进行额外的更新时间戳操作,增加处理逻辑;时间戳无法捕捉delete 操作,在数据准确性上受到了一定的限制。
上述缺点需要结合业务需要,分析能否采用时间戳方式实现增量抽取。
② 触发器
在要抽取的表上建立触发器,一般要建立插入、修改、删除三个触发器,同时根据需要决定是否借助于临时表。
每当源表中的数据发生变化,就被相应的触发器将变化的数据写入一个临时表,抽取线程从临时表中抽取数据,临时表中抽取过的数据被标记或删除。
优点:触发器性能高、实时性好,仓库处理规则简单,不需要修改业务系统表结构,可以实现数据的递增加载。
缺点:要求对监控表建立触发器,对业务系统有一定的影响,破坏数据库业务逻辑,容易对源数据库构成威胁,,同时增加了维护的复杂程度。
③ 全表对比方式
全表比对的方式是借助ETL 工具事先为要抽取的表建立一个结构类似的临时表,该临时表记录源表主键以及根据所有字段的数据计算出来,每次进行数据抽取前,对源表和临时表进行的比对,如有不同,进行增、删、改操作,例如目标表没有存在该主键值,表示该记录还没有,即进行Insert 操作。
优点:对已有系统表结构不产生影响,不需要修改业务操作程序,所有抽取规则由ETL完成,管理维护统一,可以实现数据的递增加载,没有风险。
缺点:比对过程较复杂,设计较为复杂,速度较慢。与触发器和时间戳方式中的主动通知不同,全表比对方式是被动的进行全表数据的比对,性能较差。当表中没有主键或唯一列且含有重复记录时,全表比对方式的准确性需要单独处理。
④ 日志表方式
在业务系统中添加系统日志表,当业务数据发生变化时,更新维护日志表内容,当作ETL 加载时,通过读日志表数据决定加载那些数据及如何加载。
优点:不需要修改业务系统表结构,源数据抽取清楚,速度较快。可以实现数据的递增加载。
缺点:日志表维护需要由业务系统完成,需要对业务系统业务操作程序作修改,记录日志信息。日志表维护较为麻烦,对原有系统有较大影响。工作量较大,改动较大,有一定风险。
⑤ 全表读取数据
每次获取数据时时均全量获取。
优点:加载规则简单,维护简单。
缺点:重复读取数据,性能差,一般不用。
上述的各种捕获变更数据,都是采用在应用程序中实现自定义跟踪机制。 这些自定义机制通常要求对跟踪的表进行架构更改,或者使用触发器。为了满足数据迁移和数据抽取的业务需要,使得有机会在数据库层面上直接实现增量抽取功能,ORACLE综合性能和场景需要,在数据库引擎层面直接集成了CDC功能,由于提供了类似API的功能接口,变更数据捕获和更改跟踪均不要求在源中进行任何架构更改或使用触发器,所以比第三方工具具有一定的优势。利用CDC捕获变更有以下特点:
① 性能影响小。使用异步进程捕获,通过进程读取事务日志,对系统造成的影响很小,不对业务系统造成太大的压力,影响现有业务。
② 监控范围大。对该表的所有DML和DDL操作都会被记录,有助于跟踪表的变化,实现表操作的追根溯源。
③ 操作简单 。CDC是在数据库引擎中添加的功能,封装在数据库中,类似于API接口调用,不需要复杂的业务处理逻辑就可以实现DML和DDL的操作监控。
④ 有一定时延性。由于捕获进程从事务日志中提取更改数据,因此,向源表提交更改的时间与更改出现在其关联更改表中的时间之间存在内在的延迟。 虽然这种延迟通常很小,但务必记住,在捕获进程处理相关日志项之前无法使用更改数据。
下图说明了CDC实现的原理:
① 数据源:监控的源表所有操作都会进入日志。日志为变更表提供数据源,作为捕获进程的输入来源。
② 捕获进程:扫描日志并将列数据以及与事务有关的信息写入变更数据捕获更改表中。
③ 捕获进程将在每个扫描周期内打开并提交其自己的事务。它检测何时为表新启用了变更数据捕获,并自动将这些表加入到当前在日志中监视更改项的表集中。 同样,它还会检测禁用的变更数据捕获,进而从当前监视更改数据的表集中删除源表。 在处理完日志的某个部分后,捕获进程将通知服务器日志截断逻辑,后者使用此信息来确定适合截断的日志项,所以采用完整备份和简单备份日志方式对CDC捕获不会产生影响。
-------查看当前实例是否启用CDC----
SELECT is_cdc_enabled FROM sys.databases WHERE name = '数据库实例'
----启用当前实例数据库的CDC功能-----
EXEC sys.sp_cdc_enable_db
GO
--关闭当前实例的CDC功能---
EXEC sys.sp_cdc_disable_db
GO
----开启对特定表的监控-----
EXEC sys.sp_cdc_enable_table
@source_schema = 'lccs019999', ------数据表所属架构
@source_name = 'MyTable', ------ 被监控的数据表
@role_name = NULL,
@capture_instance = NULL,
@supports_net_changes = 1,
@index_name = NULL,
@captured_column_list = NULL, ----被监控的列,NULL时监控所有列
@filegroup_name = DEFAULT
----关闭对特定表的监控-----
EXEC sys.sp_cdc_disable_table
@source_schema = 'lccs019999',
@source_name = 'HRPMBase',
@capture_instance = 'lccs019999_HRPMBase'
DECLARE @start_time DATETIME = '2011-8-10 00:00:00'
DECLARE @end_time DATETIME ='2011-8-11 00:00:00'
SELECT sys.fn_cdc_map_time_to_lsn('smallest greater than or equal',@start_time)
SELECT sys.fn_cdc_map_time_to_lsn('largest less than or equal',@end_time)
------------改变监控作业参数----------------
execute sys.sp_cdc_change_job
@job_type='cleanup',
@threshold = 5
GO
------------------获取DDL操作记录:更改的时间以及更改的内容-----------------------
select * from cdc.ddl_history
------------------获取特定表的DDL操作记录----------------------
EXEC sys.sp_cdc_get_ddl_history @capture_instance='lccs019999_HROrgRelation'
-----时间转化为操作lsn------
DECLARE @from_lsn BINARY(10),@end_lsn BINARY(10)
------lsn转化为时间-------
SELECT sys.fn_cdc_map_lsn_to_map(0x0002CB8E00001088000C)
----返回指定捕获实例的有效性间隔的低端点(start_lsn)---
SELECT sys.fn_cdc_get_min_lsn
----返回cdc.lsn_time_mapping系统表的最大日志序列号(LSN)----
SELECT sys.fn_cdc_get_max_lsn
-----进程提交每批新的更改数据时,将在该表中为每个具有更改表项的事务添加新项--
select * from cdc.lsn_time_mapping
建议执行 sys.fn_cdc_map_lsn_to_time和sys.fn_cdc_map_time_to_lsn系统函数
结合 cdc.fn_cdc_get_all_changes_
DECLARE @begin_time datetime, @end_time datetime, @begin_lsn binary(10), @end_lsn binary(10);
SET @begin_time = '2015-04-07 18:00:00.000';
SET @end_time = '2015-04-08 18:00:00.000';
SELECT @begin_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than', @begin_time);
SELECT @end_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', @end_time);
SELECT * FROM cdc.fn_cdc_get_net_changes_HR_Department(@begin_lsn, @end_lsn, 'all ');
------------对作业的更改------------------
EXEC sys.sp_cdc_change_job
@job_type = 'capture'
,@maxtrans = 1000 --每个扫描循环可以处理的最多事务数
,@maxscans = 10 --为了从日志中提取所有行而要执行的最大扫描循环次数
,@continuous = 1 --连续运行最多处理(max_trans * max_scans)个事务
,@pollinginterval = 5
EXEC sys.sp_cdc_change_job
@job_type = 'cleanup'
,@retention = 4320 --更改行将在更改表中保留的分钟数
,@threshold = 5000 --清除时可以使用一条语句删除的删除项的最大数量
------上述对作业的更改,更改后需重启作业-----
EXEC sys.sp_cdc_stop_job @job_type = N'capture';
EXEC sys.sp_cdc_start_job @job_type = N'cleanup';
---根据指定的 low_water_mark值从当前数据库的更改表中删除行,重置更改表中的最小__$start_lsn,并删除小于该值的数据.-----
cdc.sys.sp_cdc_cleanup_change_table
将同时清除cdc.HR_table_CT,cdc.lsn_time_mapping表的记录
-------------------捕获监控实例表DML操作记录--------------------
Selec * from cdc.dbo_CDC_Table _CT
这是最重要一步操作,该表就是记录源表的所有DML操作记录。每个表对应一个实例表,命名方式为“架构名_表名_CT”
应用于源表的每个插入或删除操作在更改表中各占一行。插入操作生成的行的数据列包含插入后的列值。删除操作生成的行的数据列包含删除前的列值。更新操作需要两行数据:一行用于标识更新前的列值,另一行用于标识更新后的列值。
__$start_lsn :与相应更改的提交事务关联的日志序列号 (LSN),记录操作时间。
__$end_lsn : (在SQL Server 2008中,此列始终为 NULL)
__$seqval :对事务内的行更改顺序
__$operation :源表DML操作
1 = 删除
2 = 插入
3 = 更新(旧值)
4 = 更新(新值)
__$update_mask :基于更改表的列序号的位掩码,用于标识那些发生更改的列
数据表有时间戳字段,但是时间戳没有被有效使用。此时可以利用CDC监控,将修改时的时间值同步到原始表(MyTable,ModifyDate字段为最后修改时间 )中:
------------将__$start_lsn转为为时间,此时间就是最后修改时间--------
UPDATE MyTable
SET MyTable.ModifyDate = sys.fn_cdc_map_lsn_to_time(__$start_lsn)
FROM cdc.lccs019999_MyTable_CT cdctable
WHERE MyTable.NM = cdctable.NM
AND __$operation IN(2,4)
-------------------清除监控表,减少占据空间---------------------
DECLARE @maxlsn BINARY(10);
SELECT @maxlsn = SELECT sys.fn_cdc_get_max_lsn();
EXEC sys.sp_cdc_cleanup_change_table
@capture_instance =N'lccs019999_MyTable',
@low_water_mark=@maxlsn
@threshold=5000
2、案例2:源表无时间戳字段,需要创建一个中间表,利用中间表记录数据的增删改
数据表中此前版本中没有时间戳字段,而且由于数据表已经在使用,无法预测增加时间戳字段的风险性。
对源表建立一个备份表,每次当源表新增或者修改时,都会同步到备份表中,增量获取数据从备份表中读取。
------初始化数据,建立中间表---------------
select *into bakDepartment from Department
------从变更表中获取数据,更新到中间表中--------------
DECLARE @selectFieds VARCHAR(8000);
DECLARE @maxlsn BINARY(10);
DECLARE @ExecSql VARCHAR(4000);
SELECT
@selectFieds = coalesce(@selectFieds,'') + fields.column_name + ','
FROM cdc.captured_columns fields
INNER JOIN cdc.change_tables tb ON fields.object_id =tb.object_id AND tb.capture_instance = 'lccs019999_Department'
SET @ExecSql= N'
DELETE FROM bakDepartment WHERE NM IN ( SELECT NMFROM cdc.lccs019999_Department_CT);
INSERT INTO bakDepartment SELECT '+
@selectFieds +' sys.fn_cdc_map_lsn_to_time(__$start_lsn)'+ '
FROM cdc.lccs019999_Department_CT WHERE __$operation IN(2,4);'
CDC捕获通过作业完成。使用前开启SQLServer代理服务。如下:
代理作业与启用了变更数据捕获的数据库相关联:一个作业用于填充数据库更改表,另一个作业负责清除更改表。捕获和清除作业都是使用默认参数创建的。创建后捕获作业会立即启动, 它连续运行,每个扫描周期最多可处理 1000 个事务,并在两个周期之间停顿 5 秒钟。 清除作业默认在每天凌晨 2 点运行一次。 它将更改表项保留三天(4320 分钟),使用单个删除语句最多可删除 5000 项。
作业创建和删除也可以使用 Transact-SQL 命令: sys.sp_cdc_add_job 和 sys.sp_cdc_drop_job。
管理员对变更数据捕获代理作业的默认配置没有显式的控制权。 提供 sys.sp_cdc_change_job 的目的是让你可以修改默认配置参数。 此外, sys.sp_cdc_help_jobs 存储过程还允许查看当前的配置参数。 在启动时,捕获作业和清除作业均会从 msdb.dbo.cdc_jobs 表中提取配置参数。 在停止并重新启动作业后,使用 sys.sp_cdc_change_job 对这些值所做的任何更改才会生效。
此外,系统还另外提供了两个存储过程,让你能够启动和停止变更数据捕获代理作业: sys.sp_cdc_start_job 和 sys.sp_cdc_stop_job。
① 启动和停止捕获作业并不会造成更改数据丢失。 它仅防止捕获进程主动扫描日志,以将更改项存储在更改表中。 若要在高峰需求时段禁止扫描日志以免增加负载,一个合理的策略是停止捕获作业并在需求减少时重新启动。
② 在对数据库启用变更数据捕获时,即使恢复模式设置为简单恢复,日志截断点也不会向前推进,直到为捕获标记的所有更改都已由捕获进程收集为止。 如果捕获进程未运行且有要收集的更改,执行CHECKPOINT将不会截断日志。