change data capture是在每次對數據庫執行insert、update、delete操作的時候,捕獲變更數據的方法,這在數據倉庫中是常用到的技術手段。Oracle早在9i中就加入了這一特性,在多年後的今天,SQL Server總算是提供了同樣的功能。
CDC的原理是每次對源表(Source Tables)執行insert、update、delete時,數據庫事務日志會記錄DML造成的變更數據,然後捕獲處理過程將日志中源表的變更數據寫入變更捕獲表(Change Tables),最後ETL工具使用CDC查詢函數將變更數據抽取到數據倉庫。
相比起在源表建立促發器,CDC對源表事務性能影響小,而且可以獲取變更元數據。
這裡示范一個CDC過程
第一步 啟用數據庫的SQL Server Agent。
第二步 建立一個數據庫Database for CDC。
第三步 然後以服務角色sysadmin的用戶登錄,執行下面的命令:
USE[DatabaseforCDC];
GO
EXECUTE[sys].[sp_cdc_enable_db_change_data_capture];
GO
執行命令後即啟用了數據庫的CDC特性。啟用CDC特性後系統會自動建立名為CDC的構架和用戶,並建立了幾個用於CDC的數據表。
第四步 建立源表Source_Table,且將column_1列作為唯一索引IX_Source_Table
第五步 執行命令:
EXEC sys.sp_cdc_enable_table_change_data_capture
@source_schema = 'dbo'
, @source_name = 'Source_Table'
, @role_name = 'change_table_user1'
, @index_name = 'IX_Source_Table'
, @capture_instance = 'ST_Instance'
, @supports_net_changes = 1
, @captured_column_list = 'column_key, column_1, column_2'
, @filegroup_name = 'PRIMARY';
這個命令指定捕獲源表Source_Table的變化,參數說明如下:
參數 說明 source_schema 源表的構架名 source_name 源表名 role_name 獲取變化數據的數據庫角色,如果角色不存在,sp_cdc_enable_table_change_data_capture過程執行成功後會創建角色 index_name 唯一索引名,不是必須的,如果源表沒有主鍵可以指定唯一索引以確定哪一行數據是變更數據 capture_instance 捕獲實例,一個源表最多可以有兩個實例 supports_net_changes 是否捕捉淨變化數據,如果是的話,源表必須有主鍵或者指定唯一標識列 captured_column_list 需要在變更捕獲表中保存的列 filegroup_name 變更捕獲表使用的文件組
為源表Source_Table成功建立捕獲實例ST_Instance後,系統自動建立了變更捕獲表ST_Instance_CT,變更捕獲表的命名規則是實例名+CT。
對源表執行DML命令時,每行受到DML影響的數據都會在變更捕獲表中保存數據,不同的DML命令在變更捕獲表中保存的數據不同。
命令類型 每行數據在變更捕獲表的保存情況 insert 保存插入行的數據 delete 保存刪除行的數據 update 保存兩行數據,一行是更新前的,一行是更新後的
可以看到,變更捕獲表中除了我們需要保存源表的三個列外,還多出了5個以”__$”開頭的列,用於記錄元數據。
列名 說明 __$start_lsn 日志中序列號,在對源表執行DML時,每個事務有一個日志序列號 __$end_lsn 空,不使用 __$seqval 在一個事務中,DML影響行的序列號 __$Operation 識別執行的是何種DML,1=delete,2=insert,3=update(更新前),4=update(更新後) __$update_mask 用2進制標識哪一列發生變更
可以先試著執行下面的命令,以檢驗CDC的效果:
insertdbo.source_table
values('ck_1','c1_1','c2_1','c3_1');
insertdbo.source_table
values('ck_2','c1_2','c2_2','c3_2');
insertdbo.source_table
values('ck_3','c1_3','c2_3','c3_3');
updatedbo.source_table
setcolumn_2='c2_2_c'
wherecolumn_key='ck_2';
updatedbo.source_table
setcolumn_key='ck_3_c'
wherecolumn_key='ck_3';
deletedbo.source_table
wherecolumn_key='ck_3_c';
select*fromcdc.st_instance_ct;
將cdc.st_instance_ct表中數據列出來後如下:
__$start_lsn __$end_lsn __$seqval __$Operation __$update_mask column_key column_1 column_2 1 0x0000002D000001A1002C NULL 0x0000002D000001A10012 2 0x07 ck_1 c1_1 c2_1 2 0x0000002D000001A90004 NULL 0x0000002D000001A90002 2 0x07 ck_2 c1_2 c2_2 3 0x0000002D000001AB0004 NULL 0x0000002D000001AB0002 2 0x07 ck_3 c1_3 c2_3 4 0x0000002E0000007E0022 NULL 0x0000002E0000007E0020 3 0x04 ck_2 c1_2 c2_2 5 0x0000002E0000007E0022 NULL 0x0000002E0000007E0020 4 0x04 ck_2 c1_2 c2_2_c 6 0x0000002E000000830007 NULL 0x0000002E000000830002 3 0x01 ck_3 c1_3 c2_3 7 0x0000002E000000830007 NULL 0x0000002E000000830002 4 0x01 ck_3_c c1_3 c2_3 8 0x0000002E0000008A0006 NULL 0x0000002E0000008A0004 1 0x07 ck_3_c c1_3 c2_3
其中__$update_mask是受影響的列,對insert和delete來說,影響全部的列,所以值是111(十進制的7);對column_key更新時,值是001(十進制1);對column_2更新時,值是100(十進制4)。
變更數據查詢
變更捕獲表是那些CDC實例名+CT的表,表中記錄了源表的數據變更歷史。但通常要按照一定的要求查詢這些變更。
在數據庫的CDC構架中,除了變更捕獲表外,還可看到有5個在數據庫啟用CDC時建立的表:
表名 作用 captured_columns 記錄所有CDC實例要保存的列 change_tables 記錄所有的CDC實例 ddl_history 記錄所有源表由DDL產生的變更 index_columns 記錄CDC實例使用的唯一索引 lsn_time_mapping 記錄日志序列號的時間,每個DML事務都有一個日志序列號
除了關聯查詢這些CDC表外,SQL Server 2008裡還有多個CDC函數和儲存過程,用於查詢變更數據。
1. 日志序列號與事務時間的變換
在change tables中沒有記錄事務發生的時間,只記錄了事務的日志序列號(lsn),而日志序號號對應的時間記錄在lsn_time_mapping表中。sys.fn_cdc_map_lsn_to_time和sys.fn_cdc_map_time_to_lsn是兩個用於轉換日志序列號與事務時間的函數;sys.fn_cdc_map_time_to_lsn用於獲取某一時間段內的所有日志序列號。
2. 最小和最大日志序列號
sys.fn_cdc_get_min_lsn和 sys.fn_cdc_get_max_lsn函數獲得目前存在的最大和最小日志序列號。
3. 查詢變更數據
cdc.fn_cdc_get_all_changes_函數用於查詢實例中滿足要求的所有變更記錄。cdc.fn_cdc_get_net_changes_函數用於查詢實例中滿足要求的淨變更記錄,所謂的淨變更記錄既是最後一次DML操作後源表的記錄,比如在對一行數據進行了多次update後,使用cdc.fn_cdc_get_all_changes_會返回所有更新前和更新後的數據記錄,而淨變更只返回最後一次更新後的記錄。
4. 獲取變更列
在對源表進行update操作後,有時需要知道更新的是哪一列。在變更捕獲表中__$update_mask字段保存變更列的2進制編碼。sys.fn_cdc_is_bit_set用於返回列序的二進制值,比如要知道第3 列是否變更,使用sys.fn_cdc_is_bit_set( 3, __$update_mask ),若返回1,則表明第3列變更,返回0,則表明沒有變更。另外要知道實例中的列是第幾列,可使用sys.fn_cdc_get_column_ordinal函數。
5. 獲取源表DDL變更歷史
sys.sp_cdc_get_ddl_history函數用於查詢對源表使用數據定義語句的歷史,通常在用DDL改變源表時,也要使用同樣的DLL改變變更捕獲表。比如刪除源表中某一列,或者將某一列的值類型由int改成long,那麼變更捕獲表也要跟著變化。
變更數據的清理
變更捕獲表中數據要周期性的加載到數據倉庫中,被加載後的數據就要清理掉,否則用於cdc的數據會越來越多。使用sys.sp_cdc_cleanup_change_table存儲過程清除變更數據。此外,在啟用數據庫CDC時,系統自動在SQL Server Agent中加入每日清除變更數據的作業。
停用CDC
sys.sp_cdc_disable_table_change_data_capture存儲過程用於停用CDC實例。sys.sp_cdc_disable_db_change_data_capture存儲過程用於停用數據庫CDC功能。
小結
CDC是在SQL Server2008 CTP3中才加入特性,在前兩個CTP中還沒有出現過,可以看到還有些不盡人意,沒有Oracle那麼完善,希望在正式版中能進一步改進。