SQL Server審計功能入門:CDC(Change Data Capture)

介紹

SQL Server 2008引入了CDC(Change Data Capture),它能記錄: sql

1. 哪些數據行發生了改變 數據庫

2. 數據行變動的歷史記錄,而不只僅是最終值。 app

跟CT(Change Tracking)相比,它經過做業實現異步變動跟蹤(像事務複製),而CT是同步實現的。所以它對性能的影響較輕而且不會影響事務。 less

典型應用是在提取、傳輸和加載數據到其它數據源,就像圖中的數據倉庫。 異步

clip_image001

實現

微軟建議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

clip_image002

系統對象3d

說明日誌

建議使用的對象

cdc.captured_columns

爲在捕獲實例中跟蹤的每一列返回一行

sys.sp_cdc_get_source_columns

cdc.change_tables

爲數據庫中的每一個更改表返回一行

sys.sp_cdc_help_change_data_capture

cdc.ddl_history

針對啓用了變動數據捕獲的表所作的每一數據定義語言 (DDL) 更改返回一行

sys.sp_cdc_get_ddl_history

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

爲與更改表關聯的每一索引列返回一行

sys.sp_cdc_help_change_data_capture

msdb.dbo.cdc_jobs

存儲用於捕獲和清除做業的變動數據捕獲配置參數

NA

cdc.<capture_instance>_CT

對源表啓用變動數據捕獲時建立的更改表。 該表爲對源表執行的每一個插入和刪除操做返回一行,爲對源表執行的每一個更新操做返回兩行.capture_instance格式=SchameName_TableName

cdc.fn_cdc_get_all_changes_<capture_instance> ,

cdc.fn_cdc_get_net_changes_<capture_instance>

建立測試表並對期啓用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個新的列,結構如圖:

clip_image003

驗證

當在源表中操行數據更改操做,表cdc.dbo_tb_CT會記錄下來。試一下:

clipboard[22]

爲何沒有數據呢?由於以前介紹過了,CDC是靠做業來捕獲變動數據的,個人Agent尚未運行。

手動啓用後,就有數據了。

clip_image005

結果列的含義:

列名

數據類型

說明

__$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的行。再查看結果:

clip_image006

簡單說明一下跟蹤的查詢結果:總共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');

clip_image007

竟然沒有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。

clip_image008

但有一點要格外注意:新增的列,能被CDC DDL跟蹤到,可是新列的數據變動卻不能被CDC跟蹤到。若是須要跟蹤它,先禁用表上的CDC,再啓用便可。

 

CDC Agent Job

在指定的數據庫中首次啓用CDC,而且不存在事務複製,則會建立capture和cleanup兩個做業:

clip_image009

     capture做業是用於掃描日誌文件,把變動記錄寫到變動表中。調用sp_MScdc_capture_job來實現,能夠根據當前庫的實際事務吞吐量來設置掃描參數和掃描間隔,使得在性能開銷和跟蹤需求間達到合理平衡。

     cleanup做業是清理變動變表中的數據,默認三天的數據。

因此合理設定cleanup的間隔是很是重要的。

這兩個做業的相關的配置存儲在msdb.dbo.cdc_jobs中。當前的默認配置如圖:

clip_image010

 

總結

    1. CDC使用方便,易於配置,能與同步抽取等應用結合使用。

    2. CDC能知足大多數對數據審計的要求,但不能告訴你「誰」更改了數據。

    3. 雖然說CDC是異步的,對應性能影響小,但仍是會增長開銷,特別是IO讀寫和容量方面的。開啓CDC,每次更改,都至少會額外增長一次數據文件寫和日誌文件寫操做。

相關文章
相關標籤/搜索