利用Sqlserver的CDC功能實現2張表的同步更新
Sqlserver利用CDC功能實時同步兩張表資料
一 適用環境
僅在SQLServer2008(含)以後的企業版、開發版和評估版中可用。
在開啟CDC功能前,記得要把sqlserver的代理伺服器開啟。、
二 CDC功能大概介紹
CDC(change data capture)功能主要捕獲SQLServer指定表的增刪改操作,由於任何操作都會寫日誌(哪怕truncate),所以CDC的捕獲來源於日誌檔案。日誌檔案會把更改應用到資料檔案中,同時也會標記符合要求的資料標記為需要新增跟蹤的項。然後通過一些配套函式,最後寫入到資料倉庫中。大概流程如圖:
三 具體同步步驟以及截圖
1準備工作
現在假設有2個數據庫和2張表,db1是我們的目標資料庫,裡面有一張目標表t_cdc_ta,對目標表的所有增刪改查操作,我們想要同步到結果庫monitor裡的結果表t_cdc_ta裡。
首先建立這樣2張表
create database db1;
create databasemonitor;
然後建立表結構,如下
CREATETABLE [t_cdc_ta]
(
[id] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
[name] [varchar](20) NULL,
[addr] [varchar](20) NULL,
[ttime] [datetime] NULL
)
2對資料庫開啟CDC功能
開啟cdc功能
USEdb1
GO
EXEC sys.sp_cdc_enable_db
-驗證
--檢查是否開啟成功
SELECT is_cdc_enabled ,
CASE WHEN is_cdc_enabled = 0 THEN 'CDC功能禁用'
ELSE 'CDC功能啟用'
END 描述
FROM sys.databases
WHERE NAME = 'db1'
--0 :未開啟cdc 1:開啟cdc
然後可以發現數據庫db1裡的一些變化
db1的安全性裡的使用者添加了cdc,架構也添加了cdc
在sqlserver代理裡,我們也會發現,增加了目標庫的capture和cleanup,一個負責捕獲變化,一個負責清除變化。
3對某張表具體開啟cdc功能
USE db1;
GO
EXECsys.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
這裡補充說明下,source_schema是表的擁有者,source_schema是表名。
role_name可以寫null,但是如果設定了比如設定成cdc_Admin,那麼可以在角色一欄自動建立的
具體語句
USE db1;
GO
EXECUTEsys.sp_cdc_enable_table
@source_schema = 'dbo'
, @source_name = 't_cdc_ta'
, @role_name = 'cdc_Admin'--可以自動建立
, @capture_instance=DEFAULT
GO
執行完後,我們會發現,在db1的系統表裡會生成這樣一張表
這張表就是針對目標表的增刪改操作會生成相關的監聽資料,寫進這張表裡,下面我們來事例下
4測試cdc功能,看看日誌表情況(記錄目標表的變化情況)
對目標表進行相關的增刪改操作
use db1;
insert intodbo.t_cdc_ta(id,name,addr,ttime) values
(1,'zjm','addr1','2017-10-1909:56:15.000'),
(2,'zjm2','addr2','2017-10-1909:56:15.000'),
(3,'zjm3','addr3','2017-10-1909:56:15.000'),
(4,'zjm4','addr4','2017-10-1909:56:15.000')
update dbo.t_cdc_taset name='xxq' where id=1
delete fromdbo.t_cdc_ta where id=3
此時我們看下日誌表的具體情況
select * from [db1].[cdc].[dbo_t_cdc_ta_CT]
右邊邊框是目標表裡的資料,對於[__$operation]列,相當於對於增刪改打了個標籤,1代表刪除,2代表插入,3代表update的舊資料,4代表update的新資料。
5如何針對日誌表對結果表同步
先針對結果表寫一個儲存過程,實現對結果表的增刪改操作
Use monitor
CREATE PROC [dbo].[p_merge]
@oper INT,
@id INT,
@name VARCHAR(20),
@addr VARCHAR(20),
@ttime DATETIME
AS
-- 刪除
IF @oper=1
BEGIN
DELETE FROM dbo.t_cdc_ta
WHERE [email protected]
END
ELSE IF @oper=2 -- 新增
BEGIN
INSERT INTO dbo.t_scdc_ta(id,NAME,addr,ttime)
VALUES(@id,@name,@addr,@ttime)
END
ELSE IF @oper=4 -- 更新
BEGIN
UPDATE dbo.t_cdc_ta
SET [email protected],[email protected],[email protected]
WHERE [email protected]
END
GO
此時,就可以看到寫好的儲存過程了
在寫一個遊標的程式碼,一條一條遍歷日誌表的資料,巢狀剛才儲存過程
use db1
declare @oper INT
declare @id INT
declare @name VARCHAR(20)
declare @addr VARCHAR(20)
declare @ttime DATETIME
--定義一個遊標
declare user_curcursor for select __$operation,id ,name,addr,ttime fromdb1.cdc.dbo_t_cdc_ta_CT
--開啟遊標
open user_cur
[email protected]@fetch_status=0
begin
--讀取遊標
fetch next from user_cur into @oper,@id,@name,@addr,@ttime
use monitor
set identity_insert t_cdc_ta on
exec dbo.p_merge @oper,@id,@name,@addr,@ttime
end
close user_cur
deallocate user_cur
上面的同步指令碼寫好之後,下面給一個企業級同步的方案
比如我想5分鐘執行一次同步資料
當第一次開啟捕獲,相關的儲存過程寫好之後,
可以寫一個指令碼,專門進行同步,然後同步完後刪除日誌表的資料。
把這樣的指令碼放進sqlserver自帶的作業計劃裡,設定執行間隔為5分鐘。
同步指令碼只需要在以上游標程式碼基礎上再加上一段刪除日誌表資料即可,具體如下:
--寫一個遊標,進行最終的同步操作
use db1
declare @oper INT
declare @id INT
declare @name VARCHAR(20)
declare @addr VARCHAR(20)
declare @ttime DATETIME
--定義一個遊標
declare user_curcursor for select __$operation,id ,name,addr,ttime fromdb1.cdc.dbo_t_cdc_ta_CT
--開啟遊標
open user_cur
[email protected]@fetch_status=0
begin
--讀取遊標
fetch next from user_cur into @oper,@id,@name,@addr,@ttime
use monitor
set identity_insert t_cdc_ta on
exec dbo.p_merge @oper,@id,@name,@addr,@ttime
end
close user_cur
deallocate user_cur
--刪除日誌表資料
use db1
delete from cdc.dbo_t_cdc_ta_CT
相關截圖如下
Sqlserver代理裡作業一欄右擊新建作業,然後設定相關的一些內容。
在步驟一欄新建,然後開啟sql指令碼檔案,會自動載入進去sql語句。
然後點選計劃,新建一個計劃,設定相關的時間間隔,執行頻率情況。比如設定5分鐘,那麼就會自動5分鐘執行一次指令碼,先同步資料,再刪除表資料。
備註補充:
一可能遇到的問題
1因為我設定sqlserver自帶的那個cleanup功能時間頻次啥的,5分鐘讓清空日誌表資料一次,可是執行了好幾遍,日誌表始終無法清空資料,所以才在同步資料腳本里在每次執行好同步後跟一句清空日誌表資料的sql語句,
實現人為的cleanup。
2因為在執行同步指令碼時也是需要執行時間的,在這個過程中,目標表的捕獲還在開啟中,萬一我同步的時候,目標表還在不斷增刪改,最後那個清空日誌表的sql會不會把沒有同步的資料記錄也刪除了,這裡存在一個紕漏地方,暫時沒想到更好的解決方法。
如果你有更好的想法解決這個問題,可以評論給我幫助。
二 CDC的一些儲存過程和函式連結,連結頁面裡往下翻有相關內容。