Spring Batch 批量處理策略

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

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

  • 轉換應用程序(Conversion Applications):針對每個從外部系統導出或者提供的各類類型的文件,咱們都須要建立一個轉換應用程序來說這些類型的文件和數據轉換爲處理所須要的標準格式。這個類型的批量應用程序能夠是正規轉換工具模塊中的一部分,也能夠是整個的轉換工具模塊(請查看:基本的批量服務(Basic Batch Services))。
  • 校驗應用程序(Validation Applications):校驗應用程序可以保證全部的輸入和輸出記錄都是正確和一致的。校驗一般是基於頭和尾進行校驗的,校驗碼和校驗算法一般是針對記錄的交叉驗證。
  • 提取應用(Extract Applications): 這個應用程序一般被用來從數據庫或者文本文件中讀取一系列的記錄,並對記錄的選擇一般是基於預先肯定的規則,而後將這些記錄輸出到輸出文件中。
  • 提取/更新應用(Extract/Update Applications):這個應用程序一般被用來從數據庫或者文本文件中讀取記錄,並將每一條讀取的輸入記錄更新到數據庫或者輸出數據庫中。
  • 處理和更新應用(Processing and Updating Applications):這種程序對從提取或驗證程序 傳過來的輸入事務記錄進行處理。這處理一般包括有讀取數據庫而且得到須要處理的數據,爲輸出處理更新數據庫或建立記錄。
  • 輸出和格式化應用(Output/Format Applications):一個應用經過讀取一個輸入文件,對輸入文件的結構從新格式化爲須要的標準格式,而後建立一個打印的輸出文件,或將數據傳輸到其餘的程序或者系統中。

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

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

  • 分類(Sort)- 一個程序能夠讀取輸入文件後生成一個輸出文件,在這個輸出文件中能夠對記錄進行從新排序,從新排序的是根據給定記錄的關鍵字段進行從新排序的。分類一般使用標準的系統工具來執行。
  • 拆分(Split)- 一個程序能夠讀取輸入文件後,根據須要的字段值,將輸入的文件拆分爲多個文件進行輸出。拆分一般使用標準的系統工具來執行。
  • 合併(Merge)- 一個程序能夠讀取多個輸入文件,而後將多個輸入文件進行合併處理後生成爲一個單一的輸出文件。合併能夠自定義或者由參數驅動的(parameter-driven)系統實用程序來執行.

批量處理應用程序能夠經過下面的輸入數據類型來進行分類:app

  • 數據庫驅動應用程序(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)運行。這樣作的目的是減小超長批處理做業過程所需的時間。

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

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

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

上圖: 分區處理

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

分區方案

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

固定和均衡拆分記錄集

這涉及到將輸入的記錄集合分解成均衡的部分(例如,拆分爲 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

相關文章
相關標籤/搜索