SQL Server 2008引入了CDC(Change Data Capture),它能记录: sql
1. 哪些数据行发生了改变 数据库
2. 数据行变动的历史记录,而不只仅是最终值。 app
跟CT(Change Tracking)相比,它经过做业实现异步变动跟踪(像事务复制),而CT是同步实现的。所以它对性能的影响较轻而且不会影响事务。 less
典型应用是在提取、传输和加载数据到其它数据源,就像图中的数据仓库。 异步
微软建议CDC结合快照快照隔离级别使用,能够避免读取变动数据与变动数据写入时的读写阻塞。 性能
须要注意:快照隔离级别会有额外的开销,特别是Tempdb(全部的数据更改都会被版本化存到tempdb)。 测试
use master go create database CDCTest go alter database CDCTest set allow_snapshot_isolation on go --enable CDC on database CDCTest use CDCTest go exec sys.sp_cdc_enable_db go
启用CDC以后会新增一个叫CDC的Schema和一系列的系统表、SP和View。官方建议不要直接查询系统表而是使用对应的系统SP/FN来获取CDC数据。 spa
系统对象3d |
说明日志 |
建议使用的对象 |
cdc.captured_columns |
为在捕获实例中跟踪的每一列返回一行 |
|
cdc.change_tables |
为数据库中的每一个更改表返回一行 |
|
cdc.ddl_history |
针对启用了变动数据捕获的表所作的每一数据定义语言 (DDL) 更改返回一行 |
|
cdc.lsn_time_mapping |
为每一个在更改表中存在行的事务返回一行 |
sys.fn_cdc_map_lsn_to_time (Transact-SQL) , sys.fn_cdc_map_time_to_lsn (Transact-SQL) |
cdc.index_column |
为与更改表关联的每一索引列返回一行 |
|
msdb.dbo.cdc_jobs |
存储用于捕获和清除做业的变动数据捕获配置参数 |
NA |
cdc.<capture_instance>_CT |
对源表启用变动数据捕获时建立的更改表。 该表为对源表执行的每一个插入和删除操做返回一行,为对源表执行的每一个更新操做返回两行.capture_instance格式=SchameName_TableName |
建立测试表并对期启用CDC。使用sys.sp_cdc_enable_table 对表启用CDC。
--Create a test table for CDC use CDCTest GO create table tb(ID int primary key ,name varchar(20),weight decimal(10,2)); go EXECUTE sys.sp_cdc_enable_table @source_schema = N'dbo' , @source_name = N'tb' , @role_name = null; GO
若是源表是数据库中第一个要启用变动数据捕获的表,而且数据库不存在事务发布,则 sys.sp_cdc_enable_table 还将为数据库建立捕获和清理做业。 它将 sys.tables 目录视图中的 is_tracked_by_cdc 列设置为 1。
对应的跟踪表cdc.dbo_tb_CT包含了源表全部的变动数据。它包含原来全部的列和5个新的列,结构如图:
当在源表中操行数据更改操做,表cdc.dbo_tb_CT会记录下来。试一下:
为何没有数据呢?由于以前介绍过了,CDC是靠做业来捕获变动数据的,个人Agent尚未运行。
手动启用后,就有数据了。
结果列的含义:
列名 |
数据类型 |
说明 |
__$start_lsn |
binary(10) |
更改提交的LSN。在同一事务中提交的更改将共享同一个提交 LSN 值。 |
__$seqval |
binary(10) |
一个事务内可能有多个更改发生,这个值用于对它们进行排序。 |
__$operation |
int |
更改操做的类型: 1 = 删除 2 = 插入 3 = 更新(捕获的列值是执行更新操做前的值)。 4 = 更新(捕获的列值是执行更新操做后的值)。 |
__$update_mask |
varbinary(128) |
位掩码,源表中被CDC跟踪的每一列对应一个位。若是 __$operation = 1 或 2,该值将全部已定义的位设置为 1。若是 __$operation = 3 或 4,则只有那些对应已更改列的位设置为 1。 |
如今再插入一行,并更新它,而后再删除ID=1的行。再查看结果:
简单说明一下跟踪的查询结果:总共5行,第一行和第二行是插入数据,第三行和第四行是更新先后的数据,第五行是删除数据。操做类型由_$operation值可得知。
前文中建立的tb表,记录了每一个人的姓名和体重变化信息。另外某一个数据库(表tb_rs),它是体重变化趋势报表的数据源。它天天同步一次数据,更新本身的数据。怎么用CDC来实现这个需求呢?
CDC中记录了start_lsn,若是能知道tb_rs上次同步完成时,tb中被同步的最大LSN。那下次同步时,只须要同步tb表中大于此LSN的变动记录便可。
问题就简单:获取上次同步完成tb的最大LSN,获取大于此LSN的全部变动记录,更新tb_rs。
insert into tb values(1,'Ken',70.2),(3,'Joe',66),(4,'Rose',50) update tb set weight=70 where ID=3; delete from tb where name='Rose'; go DECLARE @begin_time datetime, @end_time datetime, @begin_lsn binary(10), @end_lsn binary(10); --get the interval select @begin_time=GETDATE()-1,@end_time=GETDATE(); --map the time to LSN of the CDC table tb select @begin_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than or equal', @begin_time), @end_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', @end_time); --get the net changes within the specified LSNs SELECT * FROM cdc.fn_cdc_get_net_changes_dbo_tb(@begin_lsn, @end_lsn, 'all');
竟然没有Rose的记录?Joe的信息被更新过,怎么才一条记录?
这是由于这里获得是净变动行,也就是最终结果的意思。新增而后又删除,不影响最终结果,因此没有。屡次更新同一行的某一列数据,只返回最后更新的结果。
获得这个结果,咱们就能够根据__$operation和实际数据定义同步数据的逻辑了。好比:
--generate sync statements SELECT (case __$operation when 2 then 'insert into tb_rs values ('+cast(ID as varchar(2))+', '+Name+', '+cast(weight as varchar(10))+')' when 4 then 'update tb_rs set name='+name+',weight='+cast(weight as varchar(10))+' where ID='++cast(ID as varchar(2)) END) FROM cdc.fn_cdc_get_net_changes_dbo_tb(@begin_lsn, @end_lsn, 'all');
对于更新过的行,同步数据时,我想要先判断出列是否被更改过和被更改的时间。更改过的列才须要被同步,而不是全部列同步一次。以name为例:
DECLARE @begin_time datetime, @end_time datetime, @begin_lsn binary(10), @end_lsn binary(10); --get the interval select @begin_time=GETDATE()-1,@end_time=GETDATE(); --map the time to LSN of the CDC table tb select @begin_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than or equal', @begin_time), @end_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', @end_time); --get the all changes within the specified LSNs SELECT *, (Case sys.fn_cdc_has_column_changed('dbo_tb','name',__$update_mask) when 1 then 'Yes' when 0 then 'No' End) as isNameUpdated, sys.fn_cdc_map_lsn_to_time(__$start_lsn) as updateTime FROM cdc.fn_cdc_get_all_changes_dbo_tb(@begin_lsn, @end_lsn, 'all') where __$operation in(3,4); go
CDC不只能记录DML操做,还能记录DDL操做。查询cdc.ddl_history。
但有一点要格外注意:新增的列,能被CDC DDL跟踪到,可是新列的数据变动却不能被CDC跟踪到。若是须要跟踪它,先禁用表上的CDC,再启用便可。
在指定的数据库中首次启用CDC,而且不存在事务复制,则会建立capture和cleanup两个做业:
capture做业是用于扫描日志文件,把变动记录写到变动表中。调用sp_MScdc_capture_job来实现,能够根据当前库的实际事务吞吐量来设置扫描参数和扫描间隔,使得在性能开销和跟踪需求间达到合理平衡。
cleanup做业是清理变动变表中的数据,默认三天的数据。
因此合理设定cleanup的间隔是很是重要的。
这两个做业的相关的配置存储在msdb.dbo.cdc_jobs中。当前的默认配置如图:
1. CDC使用方便,易于配置,能与同步抽取等应用结合使用。
2. CDC能知足大多数对数据审计的要求,但不能告诉你“谁”更改了数据。
3. 虽然说CDC是异步的,对应性能影响小,但仍是会增长开销,特别是IO读写和容量方面的。开启CDC,每次更改,都至少会额外增长一次数据文件写和日志文件写操做。