SQL Server 2008中的CDC(Change Data Capture)功能使用及釋疑


SQL Server 2008中的CDC(Change Data Capture)功能使用及釋疑

關鍵詞:CDC,數據庫審計

詳細參考:微軟官網大神總結

原文:http://www.cnblogs.com/chenxizhang/archive/2011/08/10/2133408.html

SQL Server 2008中的CDC(Change Data Capture)功能使用及釋疑

CDC(Change Data Capture:變更數據捕獲)這個功能是SQL Server 2008企業版的功能,它提供了一種新的機制,對表格數據的更新進行跟蹤,在數據倉庫的建設過程中,通過這種技術,可以簡化從業務數據庫導入數據的復雜度。

 

之前我有過兩篇文章介紹,最近因為又在和有關客戶介紹這方面的應用。發現之前的例子不是那么完整和清楚,特此再整理一篇出來,給大家參考

 

 

一、什么是CDC?

變更數據捕獲(Change Data Capture ,簡稱 CDC)記錄 SQL Server 表的插入、更新和刪除活動。SQLServer的操作會寫日志,這也是CDC捕獲數據的來源。

開啟cdc的源表在插入、更新和刪除活動時會插入數據到日志表中。cdc通過捕獲進程將變更數據捕獲到變更表中,通過cdc提供的查詢函數,我們可以捕獲這部分數據。

二、開啟CDC

2.1、開啟CDC的必要條件

  • sqlserver 2008 以上版本

  • 需要開啟代理服務(作業)

  • 磁盤要有足夠的空間,保存日志文件

  • 表必須要有主鍵或者是唯一索引

2.2、開啟數據庫CDC

1、 在需要開啟cdc的數據庫上執行腳本如下:

if exists(select 1 from sys.databases where name='db_name' and is_cdc_enabled=0)
begin
    exec sys.sp_cdc_enable_db
end

2、查詢數據庫的cdc開啟狀態

select is_cdc_enabled from sys.databases where name='db_name'

查詢結果為“1”,表示開啟成功。

2.3、開啟表CDC

*注意:表中必須有主鍵或者唯一索引

1、添加次要數據文件組及文件

數據庫右鍵“屬性” >> “文件組”>> ”添加”

“文件” >> “添加”

2、執行以下腳本,開啟表cdc

復制代碼
--CDC是數據庫文件組的名稱
IF EXISTS(SELECT 1 FROM sys.tables WHERE name='table_name' AND is_tracked_by_cdc = 0)
BEGIN
    EXEC sys.sp_cdc_enable_table
        @source_schema = 'dbo', -- source_schema
        @source_name = 'table_name', -- table_name
        @capture_instance = NULL, -- capture_instance
        @supports_net_changes = 1, -- supports_net_changes
        @role_name = NULL, -- role_name
        @index_name = NULL, -- index_name
        @captured_column_list = NULL, -- captured_column_list
        @filegroup_name = 'CDC' -- filegroup_name
END
復制代碼

3、查看表cdc開啟狀態

SELECT is_tracked_by_cdc FROM sys.tables WHERE name='table_name'

查詢結果為“1”,表示開啟成功。

三、使用CDC

開啟cdc后會在數據庫中生成以下文件,開啟數據庫GY_DB,開啟表VW_GHZDK

下面我們會對部分表和函數進行說明

系統表:

cdc.change_tables:表開啟cdc后會插入一條數據到這張表中,記錄表一些基本信息

cdc.captured_columns:開啟cdc后的表,會記錄它們的字段信息到這張表中

cdc.VW_GHZDK_CT(cdc.表名_CT):記錄VW_GHZDK表中所有變更的數據

字段“__$operation”為“1”代表刪除,“2”代表插入,“3”執行更新操作前的值,“4”執行更新操作后的值。字段“__$start_lsn”由於更改是來源於數據庫的事務日志,所以這里會保存其事務日志的開始序列號(LSN)

函數:

cdc.fn_cdc_get_all_changes_dbo_VW_GHZDK:針對在指定日志序列號 (LSN) 范圍內應用到源表的每項更改均返回一行。如果源行在該間隔內有多項更改,則每項更改都會表示在返回的結果集中

cdc.fn_cdc_get_net_changes_dbo_VW_GHZDK:針對指定 LSN 范圍內每個已更改的源行返回一個凈更改行。也就是說,如果在 LSN 范圍內源行具有多項更改,則該函數將返回反映該行最終內容的單一行

sys.fn_cdc_map_time_to_lsn:為指定的時間返回 cdc.lsn_time_mapping 系統表中 start_lsn 列中的日志序列號 (LSN) 值。可以使用此函數系統地將日期時間范圍映射到基於 LSN 的范圍,以供變更數據捕獲枚舉函數 cdc.fn_cdc_get_all_changes_<capture_instance> 和 cdc.fn_cdc_get_net_changes_<capture_instance> 返回此范圍內的數據更改。

 

 

 

四、最佳實踐:案例演示

-----------------------------------

1. 准備一個數據庫,里面准備一個表,Orders

  image

2. 啟用數據庫級別的CDC選項

--在數據庫級別啟用CDC功能
EXEC sys.sp_cdc_enable_db 

這個命令執行完之后,會在系統表里面添加6個表格

    image

 

3.在需要做數據捕獲的表上面啟用CDC選項

EXEC sys.sp_cdc_enable_table 
  @source_schema='dbo',
  @source_name='Orders',
  @capture_instance='Orders',
  @supports_net_changes=0,
  @role_name=null

 

【備注】關於這個存儲過程的具體用法和有關參數的含義,請參考

http://msdn.microsoft.com/en-us/library/bb522475.aspx

 

執行之后,會有如下的輸出消息

image

這個提示的意思是說,要啟動SQL Server Agent。因為CDC功能是要通過一個兩個作業來自動化完成的

  image

 

與此同時,執行上面的命令還將在系統表中添加一個表格

  image

 

還會添加一個函數

  image

 

4.插入或者更新數據測試CDC功能

--插入或者更新數據測試CDC功能
INSERT Orders(CustomerID) VALUES('Microsoft');
INSERT Orders(CustomerID) VALUES('Google');

UPDATE Orders SET CustomerID='Yahoo' WHERE OrderID=1
DELETE FROM Orders WHERE OrderID=2

 

這個范例插入兩行數據,緊接着又對第一行更新,然后還刪除了第二行,所以最終只有一行數據

  image

那么,我們來看看CDC做了什么事情呢?

SELECT * FROM cdc.Orders_CT

  image

我們可以來解釋一下上面結果的含義

__$operation=2的情況,表示新增

__$operation=3或者4,表示更新,3表示舊值,4表示新值

__$operation=1的情況,表示刪除

 

很好理解,不是嗎?

但是,我們一般都是需要按照時間范圍進行檢索,對吧,所以,需要使用下面的語法進行查詢

--按照時間范圍查詢CDC結果
DECLARE @from_lsn BINARY(10),@end_lsn BINARY(10)
DECLARE @start_time DATETIME = '2011-8-10 00:00:00'
DECLARE @end_time DATETIME ='2011-8-11 00:00:00'
SELECT @from_lsn=sys.fn_cdc_map_time_to_lsn('smallest greater than or equal',@start_time)
SELECT @end_lsn=sys.fn_cdc_map_time_to_lsn(' largest less than or equal',@end_time)
SELECT * FROM cdc.fn_cdc_get_all_changes_Orders(@from_lsn,@end_lsn,'all')

 

 

關於sys.fn_cdc_map_time_to_lsn這個函數,請參考

http://msdn.microsoft.com/en-us/library/bb500137.aspx

查詢的結果如下

image

 

如果需要包含更新操作的舊值,則可以以下的語法

DECLARE @from_lsn BINARY(10),@end_lsn BINARY(10)
DECLARE @start_time DATETIME = '2011-8-10 00:00:00'
DECLARE @end_time DATETIME ='2011-8-11 00:00:00'
SELECT @from_lsn=sys.fn_cdc_map_time_to_lsn('smallest greater than or equal',@start_time)
SELECT @end_lsn=sys.fn_cdc_map_time_to_lsn(' largest less than or equal',@end_time)
SELECT * FROM cdc.fn_cdc_get_all_changes_Orders(@from_lsn,@end_lsn,'all update old')

 

  image

 

通常,為了方便起見,我們會將這個查詢定義為一個存儲過程,如下

--定義存儲過程來進行查詢
CREATE PROC GetOrdersCDCResult(@start_time DATETIME,@end_time DATETIME)
AS
BEGIN
    DECLARE @from_lsn BINARY(10),@end_lsn BINARY(10)
    SELECT @from_lsn=sys.fn_cdc_map_time_to_lsn('smallest greater than or equal',@start_time)
    SELECT @end_lsn=sys.fn_cdc_map_time_to_lsn(' largest less than or equal',@end_time)
    SELECT * FROM cdc.fn_cdc_get_all_changes_Orders(@from_lsn,@end_lsn,'all')
END
 

 

然后,每次需要用的時候,就直接調用即可

--執行存儲過程
EXEC GetOrdersCDCResult '2011-8-10','2011-8-11'

 

5.結合SSIS實現事實表的增量更新

下面展示了一個SSIS 包的設計,這里面讀取CDC的數據,先進行一些查找,然后按照__$operation的值拆分成為三個操作,分別進行插入,更新和刪除,這樣就可以實現對事實表的增量更新

  image

 

 

【5】代碼匯總

USE SampleDatabase
GO

--在數據庫級別啟用CDC功能
EXEC sys.sp_cdc_enable_db 

--在需要做數據捕獲的表格上面啟用CDC功能
EXEC sys.sp_cdc_enable_table 
@source_schema='dbo',
@source_name='Orders',
@capture_instance='Orders',
@supports_net_changes=0,
@role_name=null --插入或者更新數據測試CDC功能 INSERT Orders(CustomerID) VALUES('Microsoft'); INSERT Orders(CustomerID) VALUES('Google'); UPDATE Orders SET CustomerID='Yahoo' WHERE OrderID=1 DELETE FROM Orders WHERE OrderID=2 --查詢CDC的結果 SELECT * FROM cdc.Orders_CT --按照時間范圍查詢CDC結果 DECLARE @from_lsn BINARY(10),@end_lsn BINARY(10) DECLARE @start_time DATETIME = '2011-8-10 00:00:00' DECLARE @end_time DATETIME ='2011-8-11 00:00:00' SELECT @from_lsn=sys.fn_cdc_map_time_to_lsn('smallest greater than or equal',@start_time) SELECT @end_lsn=sys.fn_cdc_map_time_to_lsn(' largest less than or equal',@end_time) SELECT * FROM cdc.fn_cdc_get_all_changes_Orders(@from_lsn,@end_lsn,'all') --定義存儲過程來進行查詢 CREATE PROC GetOrdersCDCResult(@start_time DATETIME,@end_time DATETIME) AS BEGIN DECLARE @from_lsn BINARY(10),@end_lsn BINARY(10) SELECT @from_lsn=sys.fn_cdc_map_time_to_lsn('smallest greater than or equal',@start_time) SELECT @end_lsn=sys.fn_cdc_map_time_to_lsn(' largest less than or equal',@end_time) SELECT * FROM cdc.fn_cdc_get_all_changes_Orders(@from_lsn,@end_lsn,'all') END --執行存儲過程 EXEC GetOrdersCDCResult '2011-8-10','2011-8-11'

 

【6】CDC的注意事項

【6.1】不能truncate

  truncate 了~會提示不能使用的喲~

【6.2】修改表結構的坑

測試數據

EXEC sys.sp_cdc_enable_db
CREATE TABLE AAA2
(
    ID INT PRIMARY KEY,
    Col1 NVARCHAR(50),
    Col2 INT
)

EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', 
    @source_name = 'AAA2'

INSERT INTO dbo.AAA2( ID, Col1,Col2 ) VALUES  ( 1, 'pp',34 ),( 2, 'bb',234 ),( 3, 'cc',12 )

UPDATE dbo.AAA2 SET Col1 = 'dd'   WHERE ID = 3

DELETE FROM dbo.AAA2 WHERE ID = 2

SELECT * FROM cdc.dbo_AAA2_CT

__$start_lsn __$end_lsn __$seqval __$operation __$update_mask ID Col1 Col2 ---------------------- ------------- ---------------------- ------------ -------------------- ---- ------ ----------- 0x0000015B0001378F0019 NULL 0x0000015B0001378F0016 2 0x07 1 pp 34 0x0000015B0001378F0019 NULL 0x0000015B0001378F0017 2 0x07 2 bb 234 0x0000015B0001378F0019 NULL 0x0000015B0001378F0018 2 0x07 3 cc 12 0x0000015B000137A50003 NULL 0x0000015B000137A50002 3 0x02 3 cc 12 0x0000015B000137A50003 NULL 0x0000015B000137A50002 4 0x02 3 dd 12 0x0000015B000137AB0005 NULL 0x0000015B000137AB0002 1 0x07 2 bb 234

    栗子1 ,刪除了一列Col2 然后再插入2條數據,然后還是會存在Col2的列(因為不需要改結構嘛╮(╯_╰)╭),然后跟蹤的時候把值設置成空

ALTER TABLE dbo.AAA2 DROP COLUMN Col2

INSERT INTO dbo.AAA2
        ( ID, Col1 )
VALUES  ( 4, N'DD'),( 5, N'EE')

__$start_lsn    __$seqval    __$operation    __$update_mask    ID    Col1    Col2
0x0000015B000138F40004    0x0000015B000138F40002    2    0x07    4    DD    NULL
0x0000015B000138F40004    0x0000015B000138F40003    2    0x07    5    EE    NULL

 

 栗子2 ,然后我腦抽的重新把Col2 加進去~然而我改成了字符串類型,然后從新插入數據 ,Col2 沒值啊!!!那也正常,因為這是就結構,雖然名字一樣,但是ColumnID已經不一樣了啊!所以追蹤不到是很正常的。

ALTER TABLE dbo.AAA2 ADD Col2 NVARCHAR(50)

INSERT INTO dbo.AAA2
        ( ID, Col1, Col2 )
VALUES  ( 6, -- ID - int
          N'jj', -- Col1 - nvarchar(50)
          'jjj'  -- Col2 - int
          )

__$start_lsn    __$seqval    __$operation    __$update_mask    ID    Col1    Col2
0x0000015B000138F40004    0x0000015B000138F40002    2    0x07    4    DD    NULL
0x0000015B000138F40004    0x0000015B000138F40003    2    0x07    5    EE    NULL
0x0000015B000139640003    0x0000015B000139640002    2    0x07    6    jj    NULL

 

栗子3 ,那我更新總可以了吧!當更新的是元結構有的列,是可以更新成功的,但是如果是新列做了改動,則捕獲不了,正常嘛(想想既然可以給你配置可跟蹤的列,那么不存在這個列表里面的列發生變化不捕捉,就是這個道理羅~)

UPDATE dbo.AAA2 SET Col2 = 'ee' WHERE ID = 4
UPDATE dbo.AAA2 SET Col1 = 'III' WHERE ID = 6


__$start_lsn    __$seqval    __$operation    __$update_mask    ID    Col1    Col2
0x0000015B000139C00003    0x0000015B000139C00002    4    0x02    6    III    NULL

 

那該如何處理這個問題呢~

從心做一個捕獲實例~從心做一個捕獲實例~從心做一個捕獲實例 重要的事情說3便 ╮(╯_╰)╭。也只有這樣羅

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM