為了幫助設計和實現批量處理系統,基本的批量應用是通過塊和模式來構建的,同時也應該能夠為程序開發人員和設計人員提供結構的樣例和基礎的批量處理程序。

當你開始設計一個批量作業任務的時候,商業邏輯應該被拆分一系列的步驟,而這些步驟又是可以通過下面的標準構件塊來實現的:

  • 轉換應用程序(Conversion Applications):針對每一個從外部系統導出或者提供的各種類型的文件,我們都需要創建一個轉換應用程序來講這些類型的文件和數據轉換為處理所需要的標準格式。這個類型的批量應用程序可以是正規轉換工具模塊中的一部分,也可以是整個的轉換工具模塊(請查看:基本的批量服務(Basic Batch Services))。

  • 校驗應用程序(Validation Applications):校驗應用程序能夠保證所有的輸入和輸出記錄都是正確和一致的。校驗通常是基于頭和尾進行校驗的,校驗碼和校驗算法通常是針對記錄的交叉驗證。

  • 提取應用(Extract Applications)?這個應用程序通常被用來從數據庫或者文本文件中讀取一系列的記錄,并對記錄的選擇通常是基于預先確定的規則,然后將這些記錄輸出到輸出文件中。

  • 提取/更新應用(Extract/Update Applications):這個應用程序通常被用來從數據庫或者文本文件中讀取記錄,并將每一條讀取的輸入記錄更新到數據庫或者輸出數據庫中。

  • 處理和更新應用(Processing and Updating Applications):這種程序對從提取或驗證程序 傳過來的輸入事務記錄進行處理。這處理通常包括有讀取數據庫并且獲得需要處理的數據,為輸出處理更新數據庫或創建記錄。

  • 輸出和格式化應用(Output/Format Applications):一個應用通過讀取一個輸入文件,對輸入文件的結構重新格式化為需要的標準格式,然后創建一個打印的輸出文件,或將數據傳輸到其他的程序或者系統中。

更多的,一個基本的應用外殼應該也能夠被針對商業邏輯來提供,這個外殼通常不能通過上面介紹的這些標準模塊來完成。

另外的一個主要的構建塊,每一個引用通常可以使用下面的一個或者多個標準工具步驟,例如:

  • 分類(Sort)- 一個程序可以讀取輸入文件后生成一個輸出文件,在這個輸出文件中可以對記錄進行重新排序,重新排序的是根據給定記錄的關鍵字段進行重新排序的。分類通常使用標準的系統工具來執行。

  • 拆分(Split)- 一個程序可以讀取輸入文件后,根據需要的字段值,將輸入的文件拆分為多個文件進行輸出。拆分通常使用標準的系統工具來執行。

  • 合并(Merge)- 一個程序可以讀取多個輸入文件,然后將多個輸入文件進行合并處理后生成為一個單一的輸出文件。合并可以自定義或者由參數驅動的(parameter-driven)系統實用程序來執行.

批量處理應用程序可以通過下面的輸入數據類型來進行分類:

  • 數據庫驅動應用程序(Database-driven applications)可以通過從數據庫中獲得的行或值來進行驅動。

  • 文件驅動應用程序(File-driven applications)?可以通過從文件中獲得的數據來進行驅動。

  • 消息驅動應用程序(Message-driven applications)?可以通過從消息隊列中獲得的數據來進行驅動。

所有批量處理系統的處理基礎都是策略(strategy)。對處理策略進行選擇產生影響的因素包括有:預估批量處理需要處理的數據量,在線并發量,和另外一個批量處理系統的在線并發量,可用的批量處理時間窗口(很多企業都希望系統是能夠不間斷運行的,基本上來說批量處理可能沒有處理時間窗口)。

針對批量處理的標準處理選項包括有:

  • 在一個批處理窗口中執行常規離線批處理

  • 并發批量 / 在線處理

  • 并發處理很多不同的批量處理或者有很多批量作業在同一時間運行

  • 分區(Partitioning),就是在同一時間有很多示例在運行相同的批量作業

  • 混合上面的一些需求

上面列表中的順序代表了批處理實現復雜性的排序,在同一個批處理窗口的處理最簡單,而分區實現最復雜。

上面的一些選項或者所有選項能夠被商業的任務調度所支持。

在下面的部分,我們將會針對上面的處理選項來對細節進行更多的說明。需要特別注意的是,批量處理程序使用提交和鎖定策略將會根據批量處理的不同而有所不同。作為最佳實踐,在線鎖策略應該使用相同的原則。因此,在設計批處理整體架構時不能簡單地拍腦袋決定,需要進行詳細的分析和論證。

鎖定策略可以僅僅使用常見的數據庫鎖或者你也可以在系統架構中使用其他的自定義鎖定服務。這個鎖服務將會跟蹤數據庫的鎖(例如在一個專用的數據庫表(db-table)中存儲必要的信息),然后在應用程序請求數據庫操作時授予權限或拒絕。重試邏輯應該也需要在系統架構中實現,以避免批量作業中的因資源鎖定而導致批量任務被終止。

批量處理作業窗口中的常規處理

針對運行在一個單獨批處理窗口中的簡單批量處理,更新的數據對在線用戶或其他批處理來說并沒有實時性要求,也沒有并發問題,在批處理運行完成后執行單次提交即可。

大多數情況下,一種更健壯的方法會更合適.要記住的是,批處理系統會隨著時間的流逝而增長,包括復雜度和需要處理的數據量。如果沒有合適的鎖定策略,系統仍然依賴于一個單一的提交點,則修改批處理程序會是一件痛苦的事情。?因此,即使是最簡單的批處理系統,也應該為重啟-恢復(restart-recovery)選項考慮提交邏輯。針對下面的情況,批量處理就更加復雜了。

并發批量 / 在線處理

批處理程序處理的數據如果會同時被在線用戶實時更新,就不應該鎖定在線用戶需要的所有任何數據(不管是數據庫還是文件),即使只需要鎖定幾秒鐘的時間。

還應該每處理一批事務就提交一次數據庫。這減少了其他程序不可用的數據數據量,也壓縮了數據不可用的時間。

另一個可以使用的方案就是使用邏輯行基本的鎖定實現來替代物理鎖定。通過使用樂觀鎖(Optimistic Locking?)或悲觀鎖(Pessimistic Locking)模式。

  • 樂觀鎖假設記錄爭用的可能性很低。這通常意味著并發批處理和在線處理所使用的每個數據表中都有一個時間戳列。當程序讀取一行進行處理時,同時也獲得對應的時間戳。當程序處理完該行以后嘗試更新時,在 update 操作的 WHERE 子句中使用原來的時間戳作為條件.如果時間戳相匹配,則數據和時間戳都更新成功。如果時間戳不匹配,這表明在本程序上次獲取和此次更新這段時間內已經有另一個程序修改了同一條記錄,因此更新不會被執行。

  • 悲觀鎖定策略假設記錄爭用的可能性很高,因此在檢索時需要獲得一個物理鎖或邏輯鎖。有一種悲觀邏輯鎖在數據表中使用一個專用的 lock-column 列。當程序想要為更新目的而獲取一行時,它在 lock column 上設置一個標志。如果為某一行設置了標志位,其他程序在試圖獲取同一行時將會邏輯上獲取失敗。當設置標志的程序更新該行時,它也同時清除標志位,允許其他程序獲取該行。請注意,在初步獲取和初次設置標志位這段時間內必須維護數據的完整性,比如使用數據庫鎖(例如,SELECT FOR UPDATE)。還請注意,這種方法和物理鎖都有相同的缺點,除了它在構建一個超時機制時比較容易管理。比如記錄而用戶去吃午餐了,則超時時間到了以后鎖會被自動釋放。

這些模式并不一定適用于批處理,但他們可以被用在并發批處理和在線處理的情況下(例如,數據庫不支持行級鎖)。作為一般規則,樂觀鎖更適合于在線應用,而悲觀鎖更適合于批處理應用。只要使用了邏輯鎖,那么所有訪問邏輯鎖保護的數據的程序都必須采用同樣的方案。

請注意:這兩種解決方案都只鎖定(address locking)單條記錄。但很多情況下我們需要鎖定一組相關的記錄。如果使用物理鎖,你必須非常小心地管理這些以避免潛在的死鎖。如果使用邏輯鎖,通常最好的解決辦法是創建一個邏輯鎖管理器,使管理器能理解你想要保護的邏輯記錄分組(groups),并確保連貫和沒有死鎖(non-deadlocking)。這種邏輯鎖管理器通常使用其私有的表來進行鎖管理、爭用報告、超時機制 等等。

并行處理

并行處理允許多個批量處理運行(run)/任務(job)同時并行地運行。以使批量處理總運行時間降到最低。如果多個任務不使用相同的文件、數據表、索引空間時,批量處理這些不算什么問題。如果確實存在共享和競爭,那么這個服務就應該使用分區數據來實現。另一種選擇是使用控制表來構建一個架構模塊以維護他們之間的相互依賴關系。控制表應該為每個共享資源分配一行記錄,不管這些資源是否被某個程序所使用。執行并行作業的批處理架構或程序隨后將查詢這個控制表,以確定是否可以訪問所需的資源。

如果解決了數據訪問的問題,并行處理就可以通過使用額外的線程來并行實現。在傳統的大型主機環境中,并行作業類上通常被用來確保所有進程都有充足的 CPU 時間。無論如何,解決方案必須足夠強勁,以確保所有正在運行的進程都有足夠的運行處理時間。

并行處理的其他關鍵問題還包括負載平衡以及一般系統資源的可用性(如文件、數據庫緩沖池等)。請注意,控制表本身也可能很容易變成一個至關重要的資源(有可能發生嚴重競爭)。

分區

分區技術允許多版本的大型批處理程序并發地(concurrently)運行。這樣做的目的是減少超長批處理作業過程所需的時間。

可以成功分區的過程主要是那些可以拆分的輸入文件 和/或 主要的數據庫表被分區以允許程序使用不同的數據來運行。

此外,被分區的過程必須設計為只處理分配給他的數據集。分區架構與數據庫設計和數據庫分區策略是密切相關的。請注意,數據庫分區并不一定指數據庫需要在物理上實現分區,盡管在大多數情況下這是明智的。

下面的圖片展示了分區的方法:

partitioned.png?version=1&modificationDate=15473%C3%97%C3%97%C3%970000&api=v2

上圖: 分區處理

系統架構應該足夠靈活,以允許動態配置分區的數量。自動控制和用戶配置都應該納入考慮范圍。自動配置可以根據參數來決定,例如輸入文件大小 和/或 輸入記錄的數量。

分區方案

面列出了一些可能的分區方案,至于具體選擇哪種分區方案,要根據具體情況來確定:

固定和均衡拆分記錄集

這涉及到將輸入的記錄集合分解成均衡的部分(例如,拆分為 10 份,這樣每部分是整個數據集的十分之一)。每個拆分的部分稍后由一個批處理/提取程序實例來處理。

為了使用這種方案,需要在預處理時候就將記錄集進行拆分。拆分的結果有一個最大值和最小值的位置,這兩個值可以用作限制每個 批處理/提取程序處理部分的輸入。

預處理可能有一個很大的開銷,因為它必須計算并確定的每部分數據集的邊界。

通過關鍵字段(Key Column)拆分

這涉及到將輸入記錄按照某個關鍵字段來拆分,比如一個地區代碼(location code),并將每個鍵分配給一個批處理實例。為了達到這個目標,也可以使用列值。

通過分區表來指派給一個批量處理實例

請查看下面的詳細說明。

在使用這種方法時, 新值的添加將意味著需要手動重新配置批處理/提取程序,以確保新值被添加到某個特定的實例。

通過數據的部分值指派給一個批量處理實例

例如,值?0000-0999, 1000 - 1999, 等。

使用這種方法的時候,將確保所有的值都會被某個批處理作業實例處理到。然而,一個實例處理的值的數量依賴于列值的分布(即可能存在大量的值分布在0000-0999范圍內,而在1000-1999范圍內的值卻很少)。如果使用這種方法,設計時應該考慮到數據范圍的切分。

使用?通過分區表來指派 和?通過數據的部分值,?在這兩種方法中,并不能將指定給批處理實例的記錄實現最佳均勻分布。批處理實例的數量并不能動態配置。

通過視圖(Views)

這種方法基本上是根據鍵列來分解,但不同的是在數據庫級進行分解。它涉及到將記錄集分解成視圖。這些視圖將被批處理程序的各個實例在處理時使用。分解將通過數據分組來完成。

使用這個方法時,批處理的每個實例都必須為其配置一個特定的視圖(而非主表)。當然,對于新添加的數據,這個新的數據分組必須被包含在某個視圖中。也沒有自動配置功能,實例數量的變化將導致視圖需要進行相應的改變。

附加的處理識別器

這涉及到輸入表一個附加的新列,它充當一個指示器。在預處理階段,所有指示器都被標志為未處理。在批處理程序獲取記錄階段,只會讀取被標記為未處理的記錄,一旦他們被讀取(并加鎖),它們就被標記為正在處理狀態。當記錄處理完成,指示器將被更新為完成或錯誤。批處理程序的多個實例不需要改變就可以開始,因為附加列確保每條紀錄只被處理一次。

使用該選項時,表上的I/O會動態地增長。在批量更新的程序中,這種影響被降低了,因為寫操作是必定要進行的。

提取表到無格式文件

這包括將表中的數據提取到一個文件中。然后可以將這個文件拆分成多個部分,作為批處理實例的輸入。

使用這個選項時,將數據提取到文件中,并將文件拆分的額外開銷,有可能抵消多分區處理(multi-partitioning)的效果。可以通過改變文件分割腳本來實現動態配置。

With this option, the additional overhead of extracting the table into a file, and splitting it, may cancel out the effect of multi-partitioning. Dynamic configuration can be achieved via changing the file splitting script.

使用哈希列(Hashing Column)

這個計劃需要在數據庫表中增加一個哈希列(key/index)來檢索驅動(driver)記錄。這個哈希列將有一個指示器來確定將由批處理程序的哪個實例處理某個特定的行。例如,如果啟動了三個批處理實例,那么 “A” 指示器將標記某行由實例 1 來處理,“B”將標記著將由實例 2 來處理,以此類推。

稍后用于檢索記錄的過程(procedure)程序,將有一個額外的 WHERE 子句來選擇以一個特定指標標記的所有行。這個表的插入(insert)需要附加的標記字段,默認值將是其中的某一個實例(例如“A”)。

一個簡單的批處理程序將被用來更新不同實例之間的重新分配負載的指標。當添加足夠多的新行時,這個批處理會被運行(在任何時間,除了在批處理窗口中)。

批處理應用程序的其他實例只需要像上面這樣的批處理程序運行著以重新分配指標,以決定新實例的數量。

數據庫和應用設計原則

如果一個支持多分區(multi-partitioned)的應用程序架構,基于數據庫采用關鍵列(key column)分區方法拆成的多個表,則應該包含一個中心分區倉庫來存儲分區參數。這種方式提供了靈活性,并保證了可維護性。這個中心倉庫通常只由單個表組成,叫做分區表。

存儲在分區表中的信息應該是是靜態的,并且只能由 DBA 維護。每個多分區程序對應的單個分區有一行記錄,組成這個表。這個表應該包含這些列:程序 ID 編號,分區編號(分區的邏輯ID),一個分區對應的關鍵列(key column)的最小值,分區對應的關鍵列的最大值。

在程序啟動時,應用程序架構(Control Processing Tasklet, 控制處理微線程)應該將程序 id 和分區號傳遞給該程序。這些變量被用于讀取分區表,來確定應用程序應該處理的數據范圍(如果使用關鍵列的話)。另外分區號必須在整個處理過程中用來:

  • 為了使合并程序正常工作,需要將分區號添加到輸出文件/數據庫更新

  • 向框架的錯誤處理程序報告正常處理批處理日志和執行期間發生的所有錯誤

死鎖最小化

當程序并行或分區運行時,會導致數據庫資源的爭用,還可能會發生死鎖(Deadlocks)。其中的關鍵是數據庫設計團隊在進行數據庫設計時必須考慮盡可能消除潛在的競爭情況。

還要確保設計數據庫表的索引時考慮到性能以及死鎖預防。

死鎖或熱點往往發生在管理或架構表上,如日志表、控制表、鎖表(lock tables)。這些影響也應該納入考慮。為了確定架構可能的瓶頸,一個真實的壓力測試是至關重要的。

要最小化數據沖突的影響,架構應該提供一些服務,如附加到數據庫或遇到死鎖時的 等待-重試(wait-and-retry)間隔時間。這意味著要有一個內置的機制來處理數據庫返回碼,而不是立即引發錯誤處理,需要等待一個預定的時間并重試執行數據庫操作。

參數處理和校驗

對程序開發人員來說,分區架構應該相對透明。框架以分區模式運行時應該執行的相關任務包括:

  • 在程序啟動之前獲取分區參數

  • 在程序啟動之前驗證分區參數

  • 在啟動時將參數傳遞給應用程序

驗證(validation)要包含必要的檢查,以確保:

  • 應用程序已經足夠涵蓋整個數據的分區

  • 在各個分區之間沒有遺漏斷代(gaps)

如果數據庫是分區的,可能需要一些額外的驗證來保證單個分區不會跨越數據庫的片區。

體系架構應該考慮整合分區(partitions).包括以下關鍵問題:

  • 在進入下一個任務步驟之前是否所有的分區都必須完成?

  • 如果一個分區 Job 中止了要怎么處理?

https://www.cwiki.us/display/SpringBatchZH/Batch+Processing+Strategies