天天看點

DTS開發記錄(5)-- 挑戰增量導出

    增量導出恐怕是DTS系統中最艱難的部分了,我們曾考慮過很多方案,最後都因為需要表大綱做一定的假設而不具備通用性而放棄。有很多效率較高的方案,由于為了追求通用性而無法實作,因為現實的情況比我們理想的要複雜許多。

    下面将列舉我們曾考慮的幾種主要方案,這些方案乍一看似乎都很有希望,可通過層層分析,最後都因為複雜的現實情況而胎死腹中。

1.觸發器

    這是一個非常簡單直接的解決方案,我們隻需要将DTS引擎駐留在比如windows服務中,該引擎通過資料庫的觸發器事件擷取源表資料更新的所有情況,即增量,然後相應的更新目的表。然而,由誰來建立觸發器了?

    我們DTS系統的任務是将資料從一個表遷移到另外一個表,它所擁有的權限是:

(1)讀取源表

(2)讀寫目的表

    我們沒有建立觸發器的權限,更别說接收觸發器的事件了。

    觸發器方案馬上被否決!

2.查詢資料庫的增量記錄

    觸發器方案被否決後,我們退而求其次,我們希望所有的資料庫都提供了某種機制,能讓我們查詢指定表的增量記錄。比如,我們給出一個時間段和指定表的名稱,調用資料庫的某個函數或存儲過程或擴充SQL指令,資料庫傳回一個結果資料集,這個資料集中包含了指定的時間段内,指定表中有哪些資料是新添加的、哪些資料被删除了、哪些資料是更新過的。如果能進行這樣的資料庫增量查詢,那麼增量導出也會比較簡單。

    為此,我們尋找了相關的資料,主要針對Oracle和SqlServer,到現在為止,還沒有發現其中的一個資料庫内置了類似的機制。其實,我們可以想想,資料庫當然可以實作這樣的機制,但是代價可能非常高昂。比如,資料庫要記錄“删除增量”,如果對應的指定表沒有設定主鍵,資料庫該用什麼來唯一标志被删除的記錄了,可能的辦法是使用臨時表把被删除的記錄完整的記錄下來,如果要完整的記錄下被删除的記錄,那麼臨時表的大綱就需要與指定表的大綱完全相同。是以,資料庫需要為每個表都建一個大綱完全相同的表來存儲這些增量記錄。随時間流逝,無疑,這些臨時表中的資料會越來越多,那麼,誰來負責删除這些臨時表中的增量記錄資料了?該删除哪些增量記錄了?

    我們知道,大多資料庫都實作了增量備份的功能,如果增量備份不是采用主條記錄比對的話,可能就是使用了臨時表,這樣,增量備份的時刻就是資料庫清空臨時表的最佳時機。但是對于随機的、可重複性的增量導出來說,還存在清空臨時表的時機嗎?也許你剛剛清空了臨時表的部分記錄,而我再一次類似的增量導出可能需要用到你剛清除的那些增量記錄。

    看來,我們希望所有的資料庫(不僅僅是Oracle和SqlServer)提供一種機制、自動為我們記錄所有時間内每個表的增量是不太可能的。該方案被否決。

3.雙排序逐條記錄比對

    我們再一次退而求其次,采用最笨的逐條記錄比對的方法。最笨的方案中也可以用一些效率較高的技巧,我們首先考慮到的是雙排序逐條記錄比對。

    所謂雙排序,隻的是對存在于源表中的記錄和目的表中的記錄都采用主鍵排序的方式,這樣通過主鍵值相同來識别比對的記錄,然後再比對其餘的字段來判斷資料是否更新;如果源表中的某條記錄在目的表中沒有找到比對,表明這條記錄是新增加的。

   如何判斷那條記錄是删除的?通常的做法是采用反向周遊,即從目标表中選取一條記錄,然後周遊源表,如果沒有發現這條記錄,則表明發現了一個删除增量。

    采用雙排序的方法,可以很快的識别删除增量。我們分别為已排序的源記錄和已排序的目标記錄設定一個指針,這個指針隻能單步前進,然後将兩個指針指向的記錄拿出來進行主鍵值比較,如果發現相同,則說明發現了比對,處理該條記錄,然後兩個指針各前進一步;如果源主鍵值較小,則說明該條記錄是新增加的;如果源主鍵值較大,則說明目的指針指向的目前記錄已經在源表中被删除,這是一個删除增量。

    這種方案似乎是可行的,雖然要逐條比對,但是效率也不會太低。

    可是,如果所操作的表采用的是聯合主鍵了(即,聯合主鍵中的任何一個鍵的資料都可能是重複的,但是将它們聯合起來,在表中卻又是唯一的)?我們仍然可以進行實作排序,記錄比對也同樣可以進行,隻不多前面我們通過主鍵值相等來進行比對,這裡需要通過多個列的值全相等來進行比對。

    我們再考慮複雜一點的情況,如果主鍵進行了分裂,或者參與了合并的情況了?經過排序後,主鍵的值是有序的,可是主鍵分裂的值(或者合并後的目标值)卻不一定是有序的。這樣就沒有辦法進行雙排序操作了。因為當操作源資料中的任意一條記錄時,都可能需要周遊目的表中的所有記錄。

4.單排序逐條記錄比對

    我們現在采用的方法是單排序逐條記錄比對,即隻對源表記錄排序,然後逐條記錄處理。這樣效率要低得多,但是任務反而變得單純些。效率最低的地方展現在識别“删除增量”,因為要用到反向周遊操作。我們希望找到更好的方法來替代它。

     在實際的實作中,你可能還會遇到一些困難,比如,我們的源表/目的表中的記錄非常多,是以不可能一次将它們讀入到記憶體中,我們需要分頁。标準的sql不支援分頁,針對不同的資料庫有不同的實作方式,特别是當複雜的排序(如聯合主鍵排序)摻雜在其中時,分頁操作更為困難。再就是,對BLOB、CLOB、LOB資料的比對,是否要一個一個byte的進行比較來決定其是否為“更新增量”了?

    另外,我們需要對增量導出做更細粒度的控制,比如可以讓其選擇在增量導出時能分别控制開啟或關閉“Add增量”、“Update增量”、“Delete增量”的導出。如果我們确信不需要“Delete增量”導出,那麼就可以節省大量的導出執行時間。