SqlServer 变更数据捕获(CDC)
变更数据捕获(Change Data Capture ,简称?CDC)记录 SQL Server 表的插入、更新和删除活动。使用变更数据捕获可以更有效跟踪表对象DML历史操作,对 ETL 等数据转移也非常有用。 变更数据捕获适用版本: SQL Server 2008 以上的 Enterprise Edition、Developer Edition 和 Evaluation Edition? 变更数据捕获原理: 变更数据捕获的更改数据源为 SQL Server 事务日志。当对表启用变更数据捕获时,系统将生成一个与该表结构类似的副本。当对源表进行插入、更新和删除 时,在事务日志会记录相关操作信息。变更数据捕获代理使用异步进程读取事务日志,将相关操作结果应用到副本表(捕获实例表)中,这样就完成了对源表操作的记录跟踪。 变更数据捕获实例: 在数据库?MyDatabase 中创建测试表: -- 创建测试表 USE MyDatabase GO CREATE TABLE CDC_Test ( id int not null,name varchar(50) not null,insertDate datetime not null,value numeric(14,4) not null ) GO ALTER TABLE CDC_Test ADD CONSTRAINT PK_CDC_Test PRIMARY KEY CLUSTERED (id) GO CREATE NONCLUSTERED INDEX IX_CDC_Test_NAME ON CDC_Test(name) GO CREATE UNIQUE NONCLUSTERED INDEX IX_CDC_Test_insertDate ON CDC_Test(insertDate) GO 查看数据库或表是否启用了cdc: -- 查看数据库是否启用cdc SELECT name,is_cdc_enabled FROM sys.databases WHERE is_cdc_enabled = 1 -- 查看当前数据库表是否启用cdc SELECT name,is_tracked_by_cdc FROM sys.tables WHERE is_tracked_by_cdc = 1 对当前数据库启用cdc : -- 对当前数据库启用cdc USE MyDatabase GO EXECUTE sys.sp_cdc_enable_db; GO 可能出现以下错误及解决办法: /* 消息 22830,级别 16,状态 1,过程 sp_cdc_enable_db_internal,第 186 行 无法更新元数据来指示已对数据库 MyDatabase 启用了变更数据捕获。执行命令 'SetCDCTracked(Value = 1)' 时失败。 返回的错误为 15404: '无法获取有关 Windows NT 组/用户 'KKadministrator' 的信息,错误代码 0x54b。'。 请使用此操作和错误来确定失败的原因并重新提交请求。 消息 266,级别 16,状态 2,过程 sp_cdc_enable_db_internal,第 0 行 EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配。上一计数 = 0,当前计数 = 1。 消息 266,级别 16,状态 2,过程 sp_cdc_enable_db,第 0 行 EXECUTE 后的事务计数指示 BEGIN 和 COMMIT 语句的数目不匹配。上一计数 = 0,当前计数 = 1。 消息 3998,级别 16,状态 1,第 1 行 在批处理结束时检测到不可提交的事务。该事务将回滚。 */ -- 原因是数据库所有者为Windows用户,改为“sa” EXEC dbo.sp_changedbowner @loginame = N'sa',@map = false GO --依赖别名已删除 启动数据库cdc后,接着对指定源表启用 cdc : -- 接着对指定源表启用cdc EXEC sys.sp_cdc_enable_table @source_schema= 'dbo',--源表架构 @source_name = 'CDC_Test',--源表 @role_name = 'CDC_Role' --角色(将自动创建) GO --作业 'cdc.MyDatabase_capture' 已成功启动。 --作业 'cdc.MyDatabase_cleanup' 已成功启动。 创建完成后,将看到数据库中创建了以下对象: -- 也可以使用脚本查看跟踪表的信息 EXEC sys.sp_cdc_help_change_data_capture GO EXEC sys.sp_cdc_help_change_data_capture 'dbo','CDC_Test' GO 下面介绍系统表中,各表的主要信息: 【cdc.captured_columns】 select * from cdc.captured_columns 每个启用变更数据库捕获的表,其已跟踪的列都可在该系统表中查看(参考:?cdc.captured_columns?) EXEC sys.sp_cdc_get_captured_columns?N'dbo_CDC_Test'; 建议使用该存储过程返回列的元数据信息 (参考:sys.sp_cdc_get_captured_columns?) 【cdc.change_tables】 select *fromcdc.change_tables 每对一个源表启用变更数据捕获时,该表都会记录其实例表的详细信息(参考:cdc.change_tables) ? EXEC sys.sp_cdc_help_change_data_capture EXEC sys.sp_cdc_help_change_data_capture?@source_schema='dbo' ,@source_name='CDC_Test' 最多可为每个源表返回两行,为每个捕获实例返回一行 (参考:sys.sp_cdc_help_change_data_capture) 【cdc.index_columns】 select *fromcdc.index_columns 对源表中的主键列或者启用变更数据捕获时指定的唯一索引列(指定索引优于主键)记录一行,变更数据捕获使用这些索引列来唯一标识源表中的行。默认情况下,将包括源表的主键列。(参考:cdc.index_columns) EXEC?sys.sp_cdc_help_change_data_capture?@source_schema?=?'dbo'?,@source_name?=?'CDC_Test' 或者使用该存储过程查看(如上图) 【cdc.ddl_history】 select *fromcdc.ddl_history 针对启用了变更数据捕获的表所做的每一数据定义语言(DDL)更改返回一行。可以使用此表来确定源表发生DDL更改的时间以及更改的内容。(参考:cdc.ddl_history) EXEC sys.sp_cdc_get_ddl_history?@capture_instance='dbo_CDC_Test' --测试 DDL 操作后再查看 ALTER TABLE CDC_Test ADD info VARCHAR(20) ALTER TABLE CDC_Test DROP COLUMN info 【cdc.<capture_instance>_CT】
select *fromcdc.dbo_CDC_Test_CT 捕获实例表:这是最重要的表,该表就是记录源表的所有DML操作记录。每个表对应一个实例表,命名方式为“架构名_表名_CT” 应用于源表的每个插入或删除操作在更改表中各占一行。插入操作生成的行的数据列包含插入后的列值。删除操作生成的行的数据列包含删除前的列值。更新操作需要两行数据:一行用于标识更新前的列值,另一行用于标识更新后的列值。 现在对源表进行插入、更新、删除后,查看该跟踪实例表: -- 进行相关操作 insert into CDC_Test(id,name,insertDate,value) select 1,'kk',GETDATE(),55 go update CDC_Test set name = 'hh',value = 50 where name = 'kk' go delete from CDC_Test where id = 1 go select * from cdc.dbo_CDC_Test_CT __$start_lsn : 与相应更改的提交事务关联的日志序列号 (LSN) __$end_lsn : (在 SQL Server 2008中,此列始终为 NULL) __$seqval :对事务内的行更改顺序 __$operation :源表DML操作 1 = 删除 2 = 插入 3 = 更新(旧值) 4 = 更新(新值) __$update_mask :基于更改表的列序号的位掩码,用于标识那些发生更改的列 再测试 DDL 对更改表的影响: -- 进行相关操作 INSERT INTO CDC_Test(id,value)SELECT 2,'mm',0 ALTER TABLE CDC_Test ADD info VARCHAR(20)--添加1新列 INSERT INTO CDC_Test(id,value) SELECT 2,0 ALTER TABLE CDC_Test DROP COLUMN value--删除1列 INSERT INTO CDC_Test(id,insertDate) SELECT 3,'hh',GETDATE() SELECT * FROM cdc.dbo_CDC_Test_CT --查看更改表 结果总结: 1. 新添加的列(如 info),在更改表中不会添加。但仍可进行跟踪记录操作,只是不在表cdc.captured_columns 中的列则不跟踪记录。 2. 删除了列(如 value),更改表中则标识为null 。即时再添加创建原来的字段,也无效。 增加或者删除一列后,没有记录跟踪,这种情况增么办? 一种方法是: 1. 增删某字段 2. 再对同一个表启用另一个变更数据捕获(新的变更表为源表当前的结构) 3.再根据 ID/时间/唯一键 等从新的跟踪表取数据(非实时获取数据情况,如定期转移数据等) 4. 如觉得每次调用都更改表名,可以使用视图,调用视图查询更改表,视图只要更改对应的表就行。 捕获实例表中的一些约束: Timestamp/ rowversion 列的数据类型被定义为 binary(8) Identity 列的数据类型被定义为 int 或 bigint 对于 LOB 数据类型 varchar(max)、nvarchar(max)、varbinary(max)、image、text、ntext 和 xml,如果LOB列被更新,则在捕获表才记录更新前的值,否则(即时更新其他列)更新前的值为null。这样节省了空间。 Truncate table 将无法对启用跟踪的表使用 SWITCH PARTITION 部分行将不会被捕获 【cdc.lsn_time_mapping】
select * from cdc.lsn_time_mapping 当捕获进程提交每批新的更改数据时,将在该表中为每个具有更改表项的事务添加新的项(参考:cdc.lsn_time_mapping) 建议执行 sys.fn_cdc_map_lsn_to_time和sys.fn_cdc_map_time_to_lsn系统函数 结合?cdc.fn_cdc_get_all_changes_<capture_instance>?和cdc.fn_cdc_get_net_changes_<capture_instance> ,可获取一段范围内的数据变化情况。 DECLARE @begin_time datetime,@end_time datetime,@begin_lsn binary(10),@end_lsn binary(10); SET @begin_time = '2015-05-16 00:00:00.000'; SET @end_time = '2015-05-17 00:00:00.000'; SELECT @begin_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than or equal',@begin_time); SELECT @end_lsn = sys.fn_cdc_map_time_to_lsn('largest less than',@end_time); --SELECT @begin_lsn,@end_lsn SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_CDC_Test( @begin_lsn,@end_lsn,'all update old' ) SELECT * FROM cdc.fn_cdc_get_all_changes_dbo_CDC_Test( @begin_lsn,'all' ) SELECT * FROM cdc.fn_cdc_get_net_changes_dbo_CDC_Test( @begin_lsn,'all' ) --如果有连续的空结果集(例如,当捕获作业正在连续运行时),则最后一个现有行中的 empty_scan_count 将递增 -- 为变更数据捕获日志扫描会话中遇到的每个错误返回一行 select * from sys.dm_cdc_errors -- 针对当前数据库中的每个日志扫描会话返回一行。返回的最后一行表示当前会话。 select * from sys.dm_cdc_log_scan_sessions -- 空扫描的会话 SELECT * from sys.dm_cdc_log_scan_sessions where empty_scan_count <> 0 -- 返回最近进行的会话的平均滞后时间 SELECT latency FROM sys.dm_cdc_log_scan_sessions WHERE session_id = 0 -- 返回最近会话的平均吞吐量 SELECT command_count/duration AS [Throughput] FROM sys.dm_cdc_log_scan_sessions WHERE session_id = 0 -- 查看捕获作业或清除作业的信息 -- SELECT * FROM msdb.dbo.sysjobs SELECT * FROM msdb.dbo.cdc_jobs EXEC sys.sp_cdc_help_jobs 对作业的更改 (参考 sys.sp_cdc_change_job) --对作业的更改 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_stop_job @job_type = N'capture'; EXEC sys.sp_cdc_start_job @job_type = N'cleanup'; EXEC sys.sp_cdc_start_job @job_type = N'cleanup'; 在可更新订阅的复制中,跟踪表记录有些不同: 在发布启用cdc: 最后一项,禁用(删除)变更数据捕获: -- 对表禁用变更数据捕获 USE MyDatabase; GO EXEC sys.sp_cdc_disable_table @source_schema = N'dbo',@source_name = N'CDC_Test',@capture_instance = N'dbo_CDC_Test' GO -- 对数据库禁用变更数据捕获 USE MyDatabase; GO EXECUTE sys.sp_cdc_disable_db; GO --执行完成后,相关的表、函数、用户、角色、架构、作业都会完全删除! 参考:?变更数据捕获 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |