Pgsql數據庫跨網絡跨平臺數據增量同步方案 - kettle全表數據同步

需求背景:

  1. 需要跨網絡:從阿里雲服務器上的數據庫,通過網閘使用ftp傳文件的方式,將數據同步到業主的專網中;阿里雲跟業主專網不能直連;
  2. 定時數據增量同步,具體同步哪些表,需要可配置;
  3. 節約工作量,最大限度上不改變當前表結構、業務流程控制等等;
  4. 增量操作包含insert、update;delete使用邏輯刪除,等價於update
  5. 常見應用場景:業主的應用基本都部署在專網中,但是部分業務,需要使用微信小程序、微信公衆號等,需要部署應用到互聯網上,這個時候,專網跟互聯網進行數據同步就有需要了。常見的網閘,會支持mysql、Oracle直連,但是支持pgsql的就比較少了,小編就碰到了這樣BT的業務場景。

方案概要:

       使用kettle,定時將需要同步的數據,生成insert語句的sql文件,通過ftp方式傳入專網;專網使用kettle,定時執行sql文件,將數據寫入數據庫;

       使用觸發器,實現pgsql的replace into操作,處理update數據;

       具體的方案,往下看,當前僅介紹數據的來源跟去向流程,一些監控手段就不做介紹了。

解決的問題:

  1. 跨網絡進行增量同步數據,即隔着網閘,網絡不能直連,使用dblink、主從庫等方案解決不了;
  2. 源表跟目標表字段名稱可以不一致,但表名稱要一致(表名稱不一致也可以實現);
  3. 自定義增量的控制字段,可以是create_time、update_time也可以是id等
  4. 方案通用數據庫全表,定製化操作極少

備註:

  1. delete使用邏輯刪除,當前方案,不支持物理刪除操作;參考方案:可以創建刪除操作觸發器,記錄所有物理刪除數據的表名稱、主鍵,然後進行同步即可。
  2. 處理update數據,使用自定義觸發器,仿replace into操作,先刪除數據,然後新增;

具體數據增量同步方案實現:

       kettle怎麼安裝,怎麼用,請自行百度。

從數據庫到文件:

  1. 要同步數據的表,放到一張配置表裏面,在獲取表名稱的節點,查詢出來
  2. 將查詢出來的表名稱list,放到kettle的全局變量裏面
  3. 通過【檢驗字段的值】【js腳本控制循環變量】構建一個循環操作;遍歷list,查詢數據,成sql文件;生成sql文件前,先更新增量數據標誌位;
  4. 循環結束之後,將生成的sql文件,通過ftp協議上傳到指定位置;

從文件到數據庫:

  1. 先通過ftp協議,下載sql文件,保存到指定位置;
  2. 讀取sql文件,執行insert操作;在目標數據庫中,添加插入操作觸發器,每次插入前,將原有主鍵數據刪除,用於處理update數據;

關鍵步驟解析

       獲取表名稱

ctrl_column: 控制增量的字段,用於拼接查詢sql,where條件

ctrl_value: 控制增量字段的取值,用於拼接查詢sql,where條件取值

column_type: 控制增量字段的類型,用於拼接查詢sql,強轉where條件取值的類型

column_list:  表的字段list,用於拼接查詢sql,調整查詢數據格式、字段名稱等;非必須

例如拼接後查詢語句: select id, ower_id …… from t_com_dictionary where id > ‘0’::integer

sync_flag: 控制是否同步該表數據

-- pgsql獲取所有表名稱:
select tablename from pg_tables where schemaname='模式名稱' order by tablename
-- pgsql獲取指定表字段(也可以通過kettle獲取表字段):
SELECT col_description(a.attrelid,a.attnum) as comment,format_type(a.atttypid,a.atttypmod) as type,a.attname as name, a.attnotnull as notnull
FROM pg_class as c,pg_attribute as a 
where c.relname = '表名稱' and a.attrelid = c.oid and a.attnum>0

表名稱放入變量

       通過js腳本,獲取前一個節點查詢到的表名稱list,將數據放到kettle內存變量裏面

var tableRows=previous_result.getRows();
if (tableRows == null && (tableRows.size()==0)){
    false; //提示參數異常
} else {
    parent_job.setVariable("tables", tableRows);//ArrayList存儲表名變量
    parent_job.setVariable("size", tableRows.size());//存儲執行表的總數量
    parent_job.setVariable("i", 0);//循環控制變量
	
	var tableRow = tableRows.get(0);	//將第一行數據放到內存變量
     parent_job.setVariable("table_name", tableRow.getString("table_name",""));
	parent_job.setVariable("ctrl_column", tableRow.getString("ctrl_column",""));
	parent_job.setVariable("ctrl_value", tableRow.getString("ctrl_value",""));
	parent_job.setVariable("column_type", tableRow.getString("column_type",""));
	parent_job.setVariable("column_list", tableRow.getString("column_list",""));
	true;
}

檢驗字段值

更新標誌位

生成sql文件

控制循環

var tableRows=previous_result.getRows();
var size = new Number(parent_job.getVariable("size"));
var i = new Number(parent_job.getVariable("i"))+1;
if(i < size){
	var tableRow = tableRows.get(i);	//循環將每一行數據放到內存變量
    parent_job.setVariable("table_name", tableRow.getString("table_name",""));
	parent_job.setVariable("ctrl_column", tableRow.getString("ctrl_column",""));
	parent_job.setVariable("ctrl_value", tableRow.getString("ctrl_value",""));
	parent_job.setVariable("column_type", tableRow.getString("column_type",""));
	parent_job.setVariable("column_list", tableRow.getString("column_list",""));
	true;
}
parent_job.setVariable("i",i);	//重置i的值,用於校驗字段值節點,控制循環
true;

sql文件寫入數據庫

replace into 觸發器

CREATE OR REPLACE FUNCTION fn_replace_into() RETURNS TRIGGER AS $BODY$
		DECLARE
			strSQL VARCHAR;
	  BEGIN
			strSQL  = 'DELETE FROM '||TG_TABLE_NAME||' WHERE ID = ' || NEW.ID;
		  EXECUTE strSQL;
	    RETURN NEW;
	  END;
	$BODY$ LANGUAGE plpgsql;
	
	CREATE TRIGGER trg_t_system_user BEFORE INSERT  ON t_system_user FOR EACH ROW EXECUTE PROCEDURE fn_replace_into();

下載地址:

https://download.csdn.net/download/weixin_42686388/11224766

遇到的問題:

  1. 生成sql文件的時候,特殊類型字段數據,如timestamp、geometry類型等,會導致insert語句不能直接運行,在pgsql中可以將數據轉成varchar類型,然後插入;
  2. 使用copy命令生成csv文本文件,該方案也是可行的,需要將文件放到數據庫所在服務器;使用copy命令方式,性能更佳,而且不需要考慮數據類型導致insert語句異常問題;
  3. Linux定時任務,建議開啓隊列運行,防止同步時間間隔較短,單任務執行時間較長,導致並行任務過多,服務器內存爆掉問題;
  4. 編碼問題,生成sql文件,建議使用gbk編碼方式,因爲執行sql的時候,沒地方設置編碼(至少我沒找到),默認gbk;
  5. 理論上,當前方案也能實現跨數據庫類型的數據同步,時間關係未測試,有興趣的同學可以實踐一下;

 

部署Linux需要自行修改一些配置、去掉文件中文名稱等;可能這並不是最好的方案,但是我諮詢過專業的DBA,網上了查了很多資料,都沒找到更適合這種需求的方案,所以整理發出來大家一些交流學習下吧。這個方案目前已經在生產環境運行了將近半年多,還算穩定

覺得寫的好的給個贊;覺得有待改進的,請留言一起學習,非喜勿噴。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章