在Sqlserver2008上利用CDC实现了数据更新的跟踪,比以往的利用时间戳,触发器实现更加方便快捷.
参考资料:
实现步骤如下:
1.配置cdc
-- 开启cdc
USE db1 GOEXEC sys.sp_cdc_enable_db--验证--0 :未开启cdc 1:开启cdcSELECT is_cdc_enabled FROM sys.databases WHERE database_id=DB_ID()--表开启cdcUSE db1;GOEXEC sys.sp_cdc_enable_table@source_schema ='dbo',@source_name='t_cdc_ta',@role_name=null,@capture_instance=NULL ,@supports_net_changes=1,@index_name=null,@captured_column_list=null,@filegroup_name=default,@allow_partition_switch=1/*开启之后会生成cdc构架,并生成查询函数和变更数据表cdc.captured_columnscdc.change_tablescdc.ddl_historycdc.index_columnscdc.lsn_time_mappingdbo.systranschemascdc.dbo_t_cdc_ta_CT 以构架名和表名组合的变更数据表*/--表结构CREATE TABLE [t_cdc_ta]([id] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,[name] [varchar](20) NULL,[addr] [varchar](20) NULL,[ttime] [datetime] NULL)2.跟踪变更数据
当往源表t_cdc_ta中新增,插入,删除数据时,可以在变更数据表[cdc].[dbo_t_cdc_ta_CT]中看到如下数据
__$operation:
1-删除 ,2-新增,4-更新
3.根据变更数据,利用ETL可以实现数据的增量更新
脚本如下:
USE [db1]
GOCREATE TABLE [dbo].[cdc_capture_log]([cdc_capture_log_id] [int] IDENTITY(1,1) NOT NULL,[capture_instance] [nvarchar](50) NOT NULL,[start_time] [datetime] NOT NULL,[min_lsn] [binary](10) NOT NULL,[max_lsn] [binary](10) NOT NULL,[end_time] [datetime] NULL,[status_code] [int] NOT NULL DEFAULT(0))CREATE PROCEDURE [dbo].[usp_init_cdc_capture_log]@capture_instance NVARCHAR(50)AS BEGIN SET nocount ON ; DECLARE @start_lsn BINARY(10),@end_lsn BINARY(10),@prev_max_lsn BINARY(10)--get the max LSN for the capture instance from --the last extractSELECT @prev_max_lsn = MAX(max_lsn)FROM dbo.cdc_capture_logWHERE capture_instance = @capture_instance -- if no row found in cdc_capture_log get the min lsn -- for the capture instance IF @prev_max_lsn IS NULL SET @start_lsn = sys.fn_cdc_get_min_lsn(@capture_instance)ELSE SET @start_lsn = sys.fn_cdc_increment_lsn(@prev_max_lsn) -- get the max lsn SET @end_lsn = sys.fn_cdc_get_max_lsn()IF @start_lsn>=@end_lsn SET @start_lsn=@end_lsnINSERT INTO dbo.cdc_capture_log(capture_instance,start_time,min_lsn,max_lsn)VALUES (@capture_instance,GETDATE(),@start_lsn,@end_lsn)SELECT CAST(SCOPE_IDENTITY() AS INT) cdc_capture_log_idEND GOcreate procedure [dbo].[usp_extract_userm_capture_log] @cdc_capture_log_id INTASBEGINset nocount on;DECLARE @start_lsn binary(10),@end_lsn binary(10)-- get the lsn range to processSELECT @start_lsn = min_lsn,@end_lsn = max_lsn from dbo.cdc_capture_logwhere cdc_capture_log_id = @cdc_capture_log_id-- extract and return the changesselect m.tran_end_time modified_ts,x.*from cdc.fn_cdc_get_all_changes_dbo_t_cdc_ta(@start_lsn, @end_lsn, 'all') xjoin cdc.lsn_time_mapping m on m.start_lsn = x.__$start_lsn ;endGOCREATE PROCEDURE [dbo].[usp_end_cdc_capture_log]@cdc_capture_log_id INTAS BEGINSET nocount ON ;UPDATE dbo.cdc_capture_logSET end_time = GETDATE(),status_code = 1WHERE cdc_capture_log_id = @cdc_capture_log_idENDGO--在另一个库上建一个相同的结构的表作为同步数据测试用表USE montiorGOCREATE TABLE [dbo].[t_cdc_ta]([id] [int] PRIMARY KEY NOT NULL,[name] [varchar](20) NULL,[addr] [varchar](20) NULL,[ttime] [datetime] NULL)GOCREATE PROC [dbo].[p_merge]@oper INT,@id INT,@name VARCHAR(20),@addr VARCHAR(20),@ttime DATETIMEAS -- 删除IF @oper=1 BEGIN DELETE FROM dbo.t_cdc_taWHERE id=@idENDELSE IF @oper=2 -- 新增BEGIN INSERT INTO dbo.t_cdc_ta(id,NAME,addr,ttime)VALUES(@id,@name,@addr,@ttime)ENDELSE IF @oper=4 -- 更新BEGIN UPDATE dbo.t_cdc_taSET NAME=@name,addr=@addr,ttime=@ttimeWHERE id=@id ENDGO停用cdc
EXEC sp_cdc_disable_table
EXEC sp_cdc_disable_db这样能实现一个定时的同步更新,利用作业来不断的读取新增加的lsn来更新目的数据表,当然同步的时间一定要大于数据变更的清理作业的时间,
默认配置cdc的时候会配置两个job
cdc.db1_capture :捕获变更的作业
cdc.db1_cleanup : 数据清理作业 ,每天凌晨两天清理
之前看到一个哥们在同步数据的时候用的 SSIS的条件拆分组件,我测试了下这个数据变更是有先后顺序的,不能直接拆分数据集直接执行,
这里我时显得方式是利用循环组件一条一条数据处理,希望能有更好的办法。。。