javascript
Spring Batch 批量处理策略
為了幫助設計和實現(xiàn)批量處理系統(tǒng),基本的批量應用是通過塊和模式來構(gòu)建的,同時也應該能夠為程序開發(fā)人員和設計人員提供結(jié)構(gòu)的樣例和基礎的批量處理程序。
當你開始設計一個批量作業(yè)任務的時候,商業(yè)邏輯應該被拆分一系列的步驟,而這些步驟又是可以通過下面的標準構(gòu)件塊來實現(xiàn)的:
- 轉(zhuǎn)換應用程序(Conversion Applications):針對每一個從外部系統(tǒng)導出或者提供的各種類型的文件,我們都需要創(chuàng)建一個轉(zhuǎn)換應用程序來講這些類型的文件和數(shù)據(jù)轉(zhuǎn)換為處理所需要的標準格式。這個類型的批量應用程序可以是正規(guī)轉(zhuǎn)換工具模塊中的一部分,也可以是整個的轉(zhuǎn)換工具模塊(請查看:基本的批量服務(Basic Batch Services))。
- 校驗應用程序(Validation Applications):校驗應用程序能夠保證所有的輸入和輸出記錄都是正確和一致的。校驗通常是基于頭和尾進行校驗的,校驗碼和校驗算法通常是針對記錄的交叉驗證。
- 提取應用(Extract Applications):?這個應用程序通常被用來從數(shù)據(jù)庫或者文本文件中讀取一系列的記錄,并對記錄的選擇通常是基于預先確定的規(guī)則,然后將這些記錄輸出到輸出文件中。
- 提取/更新應用(Extract/Update Applications):這個應用程序通常被用來從數(shù)據(jù)庫或者文本文件中讀取記錄,并將每一條讀取的輸入記錄更新到數(shù)據(jù)庫或者輸出數(shù)據(jù)庫中。
- 處理和更新應用(Processing and Updating Applications):這種程序?qū)奶崛』蝌炞C程序 傳過來的輸入事務記錄進行處理。這處理通常包括有讀取數(shù)據(jù)庫并且獲得需要處理的數(shù)據(jù),為輸出處理更新數(shù)據(jù)庫或創(chuàng)建記錄。
- 輸出和格式化應用(Output/Format Applications):一個應用通過讀取一個輸入文件,對輸入文件的結(jié)構(gòu)重新格式化為需要的標準格式,然后創(chuàng)建一個打印的輸出文件,或?qū)?shù)據(jù)傳輸?shù)狡渌某绦蚧蛘呦到y(tǒng)中。
更多的,一個基本的應用外殼應該也能夠被針對商業(yè)邏輯來提供,這個外殼通常不能通過上面介紹的這些標準模塊來完成。
另外的一個主要的構(gòu)建塊,每一個引用通常可以使用下面的一個或者多個標準工具步驟,例如:
- 分類(Sort)- 一個程序可以讀取輸入文件后生成一個輸出文件,在這個輸出文件中可以對記錄進行重新排序,重新排序的是根據(jù)給定記錄的關鍵字段進行重新排序的。分類通常使用標準的系統(tǒng)工具來執(zhí)行。
- 拆分(Split)- 一個程序可以讀取輸入文件后,根據(jù)需要的字段值,將輸入的文件拆分為多個文件進行輸出。拆分通常使用標準的系統(tǒng)工具來執(zhí)行。
- 合并(Merge)- 一個程序可以讀取多個輸入文件,然后將多個輸入文件進行合并處理后生成為一個單一的輸出文件。合并可以自定義或者由參數(shù)驅(qū)動的(parameter-driven)系統(tǒng)實用程序來執(zhí)行.
批量處理應用程序可以通過下面的輸入數(shù)據(jù)類型來進行分類:
- 數(shù)據(jù)庫驅(qū)動應用程序(Database-driven applications)可以通過從數(shù)據(jù)庫中獲得的行或值來進行驅(qū)動。
- 文件驅(qū)動應用程序(File-driven applications)?可以通過從文件中獲得的數(shù)據(jù)來進行驅(qū)動。
- 消息驅(qū)動應用程序(Message-driven applications)?可以通過從消息隊列中獲得的數(shù)據(jù)來進行驅(qū)動。
所有批量處理系統(tǒng)的處理基礎都是策略(strategy)。對處理策略進行選擇產(chǎn)生影響的因素包括有:預估批量處理需要處理的數(shù)據(jù)量,在線并發(fā)量,和另外一個批量處理系統(tǒng)的在線并發(fā)量,可用的批量處理時間窗口(很多企業(yè)都希望系統(tǒng)是能夠不間斷運行的,基本上來說批量處理可能沒有處理時間窗口)。
針對批量處理的標準處理選項包括有:
- 在一個批處理窗口中執(zhí)行常規(guī)離線批處理
- 并發(fā)批量 / 在線處理
- 并發(fā)處理很多不同的批量處理或者有很多批量作業(yè)在同一時間運行
- 分區(qū)(Partitioning),就是在同一時間有很多示例在運行相同的批量作業(yè)
- 混合上面的一些需求
上面列表中的順序代表了批處理實現(xiàn)復雜性的排序,在同一個批處理窗口的處理最簡單,而分區(qū)實現(xiàn)最復雜。
上面的一些選項或者所有選項能夠被商業(yè)的任務調(diào)度所支持。
在下面的部分,我們將會針對上面的處理選項來對細節(jié)進行更多的說明。需要特別注意的是,批量處理程序使用提交和鎖定策略將會根據(jù)批量處理的不同而有所不同。作為最佳實踐,在線鎖策略應該使用相同的原則。因此,在設計批處理整體架構(gòu)時不能簡單地拍腦袋決定,需要進行詳細的分析和論證。
鎖定策略可以僅僅使用常見的數(shù)據(jù)庫鎖或者你也可以在系統(tǒng)架構(gòu)中使用其他的自定義鎖定服務。這個鎖服務將會跟蹤數(shù)據(jù)庫的鎖(例如在一個專用的數(shù)據(jù)庫表(db-table)中存儲必要的信息),然后在應用程序請求數(shù)據(jù)庫操作時授予權(quán)限或拒絕。重試邏輯應該也需要在系統(tǒng)架構(gòu)中實現(xiàn),以避免批量作業(yè)中的因資源鎖定而導致批量任務被終止。
批量處理作業(yè)窗口中的常規(guī)處理
針對運行在一個單獨批處理窗口中的簡單批量處理,更新的數(shù)據(jù)對在線用戶或其他批處理來說并沒有實時性要求,也沒有并發(fā)問題,在批處理運行完成后執(zhí)行單次提交即可。
大多數(shù)情況下,一種更健壯的方法會更合適.要記住的是,批處理系統(tǒng)會隨著時間的流逝而增長,包括復雜度和需要處理的數(shù)據(jù)量。如果沒有合適的鎖定策略,系統(tǒng)仍然依賴于一個單一的提交點,則修改批處理程序會是一件痛苦的事情。?因此,即使是最簡單的批處理系統(tǒng),也應該為重啟-恢復(restart-recovery)選項考慮提交邏輯。針對下面的情況,批量處理就更加復雜了。
并發(fā)批量 / 在線處理
批處理程序處理的數(shù)據(jù)如果會同時被在線用戶實時更新,就不應該鎖定在線用戶需要的所有任何數(shù)據(jù)(不管是數(shù)據(jù)庫還是文件),即使只需要鎖定幾秒鐘的時間。
還應該每處理一批事務就提交一次數(shù)據(jù)庫。這減少了其他程序不可用的數(shù)據(jù)數(shù)據(jù)量,也壓縮了數(shù)據(jù)不可用的時間。
另一個可以使用的方案就是使用邏輯行基本的鎖定實現(xiàn)來替代物理鎖定。通過使用樂觀鎖(Optimistic Locking?)或悲觀鎖(Pessimistic Locking)模式。
- 樂觀鎖假設記錄爭用的可能性很低。這通常意味著并發(fā)批處理和在線處理所使用的每個數(shù)據(jù)表中都有一個時間戳列。當程序讀取一行進行處理時,同時也獲得對應的時間戳。當程序處理完該行以后嘗試更新時,在 update 操作的 WHERE 子句中使用原來的時間戳作為條件.如果時間戳相匹配,則數(shù)據(jù)和時間戳都更新成功。如果時間戳不匹配,這表明在本程序上次獲取和此次更新這段時間內(nèi)已經(jīng)有另一個程序修改了同一條記錄,因此更新不會被執(zhí)行。
- 悲觀鎖定策略假設記錄爭用的可能性很高,因此在檢索時需要獲得一個物理鎖或邏輯鎖。有一種悲觀邏輯鎖在數(shù)據(jù)表中使用一個專用的 lock-column 列。當程序想要為更新目的而獲取一行時,它在 lock column 上設置一個標志。如果為某一行設置了標志位,其他程序在試圖獲取同一行時將會邏輯上獲取失敗。當設置標志的程序更新該行時,它也同時清除標志位,允許其他程序獲取該行。請注意,在初步獲取和初次設置標志位這段時間內(nèi)必須維護數(shù)據(jù)的完整性,比如使用數(shù)據(jù)庫鎖(例如,SELECT FOR UPDATE)。還請注意,這種方法和物理鎖都有相同的缺點,除了它在構(gòu)建一個超時機制時比較容易管理。比如記錄而用戶去吃午餐了,則超時時間到了以后鎖會被自動釋放。
這些模式并不一定適用于批處理,但他們可以被用在并發(fā)批處理和在線處理的情況下(例如,數(shù)據(jù)庫不支持行級鎖)。作為一般規(guī)則,樂觀鎖更適合于在線應用,而悲觀鎖更適合于批處理應用。只要使用了邏輯鎖,那么所有訪問邏輯鎖保護的數(shù)據(jù)的程序都必須采用同樣的方案。
請注意:這兩種解決方案都只鎖定(address locking)單條記錄。但很多情況下我們需要鎖定一組相關的記錄。如果使用物理鎖,你必須非常小心地管理這些以避免潛在的死鎖。如果使用邏輯鎖,通常最好的解決辦法是創(chuàng)建一個邏輯鎖管理器,使管理器能理解你想要保護的邏輯記錄分組(groups),并確保連貫和沒有死鎖(non-deadlocking)。這種邏輯鎖管理器通常使用其私有的表來進行鎖管理、爭用報告、超時機制 等等。
并行處理
并行處理允許多個批量處理運行(run)/任務(job)同時并行地運行。以使批量處理總運行時間降到最低。如果多個任務不使用相同的文件、數(shù)據(jù)表、索引空間時,批量處理這些不算什么問題。如果確實存在共享和競爭,那么這個服務就應該使用分區(qū)數(shù)據(jù)來實現(xiàn)。另一種選擇是使用控制表來構(gòu)建一個架構(gòu)模塊以維護他們之間的相互依賴關系。控制表應該為每個共享資源分配一行記錄,不管這些資源是否被某個程序所使用。執(zhí)行并行作業(yè)的批處理架構(gòu)或程序隨后將查詢這個控制表,以確定是否可以訪問所需的資源。
如果解決了數(shù)據(jù)訪問的問題,并行處理就可以通過使用額外的線程來并行實現(xiàn)。在傳統(tǒng)的大型主機環(huán)境中,并行作業(yè)類上通常被用來確保所有進程都有充足的 CPU 時間。無論如何,解決方案必須足夠強勁,以確保所有正在運行的進程都有足夠的運行處理時間。
并行處理的其他關鍵問題還包括負載平衡以及一般系統(tǒng)資源的可用性(如文件、數(shù)據(jù)庫緩沖池等)。請注意,控制表本身也可能很容易變成一個至關重要的資源(有可能發(fā)生嚴重競爭)。
分區(qū)
分區(qū)技術允許多版本的大型批處理程序并發(fā)地(concurrently)運行。這樣做的目的是減少超長批處理作業(yè)過程所需的時間。
可以成功分區(qū)的過程主要是那些可以拆分的輸入文件 和/或 主要的數(shù)據(jù)庫表被分區(qū)以允許程序使用不同的數(shù)據(jù)來運行。
此外,被分區(qū)的過程必須設計為只處理分配給他的數(shù)據(jù)集。分區(qū)架構(gòu)與數(shù)據(jù)庫設計和數(shù)據(jù)庫分區(qū)策略是密切相關的。請注意,數(shù)據(jù)庫分區(qū)并不一定指數(shù)據(jù)庫需要在物理上實現(xiàn)分區(qū),盡管在大多數(shù)情況下這是明智的。
下面的圖片展示了分區(qū)的方法:
上圖: 分區(qū)處理
系統(tǒng)架構(gòu)應該足夠靈活,以允許動態(tài)配置分區(qū)的數(shù)量。自動控制和用戶配置都應該納入考慮范圍。自動配置可以根據(jù)參數(shù)來決定,例如輸入文件大小 和/或 輸入記錄的數(shù)量。
分區(qū)方案
面列出了一些可能的分區(qū)方案,至于具體選擇哪種分區(qū)方案,要根據(jù)具體情況來確定:
固定和均衡拆分記錄集
這涉及到將輸入的記錄集合分解成均衡的部分(例如,拆分為 10 份,這樣每部分是整個數(shù)據(jù)集的十分之一)。每個拆分的部分稍后由一個批處理/提取程序?qū)嵗齺硖幚怼?/p>
為了使用這種方案,需要在預處理時候就將記錄集進行拆分。拆分的結(jié)果有一個最大值和最小值的位置,這兩個值可以用作限制每個 批處理/提取程序處理部分的輸入。
預處理可能有一個很大的開銷,因為它必須計算并確定的每部分數(shù)據(jù)集的邊界。
通過關鍵字段(Key Column)拆分
這涉及到將輸入記錄按照某個關鍵字段來拆分,比如一個地區(qū)代碼(location code),并將每個鍵分配給一個批處理實例。為了達到這個目標,也可以使用列值。
通過分區(qū)表來指派給一個批量處理實例
請查看下面的詳細說明。
在使用這種方法時, 新值的添加將意味著需要手動重新配置批處理/提取程序,以確保新值被添加到某個特定的實例。
通過數(shù)據(jù)的部分值指派給一個批量處理實例
例如,值?0000-0999, 1000 - 1999, 等。
使用這種方法的時候,將確保所有的值都會被某個批處理作業(yè)實例處理到。然而,一個實例處理的值的數(shù)量依賴于列值的分布(即可能存在大量的值分布在0000-0999范圍內(nèi),而在1000-1999范圍內(nèi)的值卻很少)。如果使用這種方法,設計時應該考慮到數(shù)據(jù)范圍的切分。
使用?通過分區(qū)表來指派 和?通過數(shù)據(jù)的部分值,?在這兩種方法中,并不能將指定給批處理實例的記錄實現(xiàn)最佳均勻分布。批處理實例的數(shù)量并不能動態(tài)配置。
通過視圖(Views)
這種方法基本上是根據(jù)鍵列來分解,但不同的是在數(shù)據(jù)庫級進行分解。它涉及到將記錄集分解成視圖。這些視圖將被批處理程序的各個實例在處理時使用。分解將通過數(shù)據(jù)分組來完成。
使用這個方法時,批處理的每個實例都必須為其配置一個特定的視圖(而非主表)。當然,對于新添加的數(shù)據(jù),這個新的數(shù)據(jù)分組必須被包含在某個視圖中。也沒有自動配置功能,實例數(shù)量的變化將導致視圖需要進行相應的改變。
附加的處理識別器
這涉及到輸入表一個附加的新列,它充當一個指示器。在預處理階段,所有指示器都被標志為未處理。在批處理程序獲取記錄階段,只會讀取被標記為未處理的記錄,一旦他們被讀取(并加鎖),它們就被標記為正在處理狀態(tài)。當記錄處理完成,指示器將被更新為完成或錯誤。批處理程序的多個實例不需要改變就可以開始,因為附加列確保每條紀錄只被處理一次。
使用該選項時,表上的I/O會動態(tài)地增長。在批量更新的程序中,這種影響被降低了,因為寫操作是必定要進行的。
提取表到無格式文件
這包括將表中的數(shù)據(jù)提取到一個文件中。然后可以將這個文件拆分成多個部分,作為批處理實例的輸入。
使用這個選項時,將數(shù)據(jù)提取到文件中,并將文件拆分的額外開銷,有可能抵消多分區(qū)處理(multi-partitioning)的效果。可以通過改變文件分割腳本來實現(xiàn)動態(tài)配置。
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)
這個計劃需要在數(shù)據(jù)庫表中增加一個哈希列(key/index)來檢索驅(qū)動(driver)記錄。這個哈希列將有一個指示器來確定將由批處理程序的哪個實例處理某個特定的行。例如,如果啟動了三個批處理實例,那么 “A” 指示器將標記某行由實例 1 來處理,“B”將標記著將由實例 2 來處理,以此類推。
稍后用于檢索記錄的過程(procedure)程序,將有一個額外的 WHERE 子句來選擇以一個特定指標標記的所有行。這個表的插入(insert)需要附加的標記字段,默認值將是其中的某一個實例(例如“A”)。
一個簡單的批處理程序?qū)⒈挥脕砀虏煌瑢嵗g的重新分配負載的指標。當添加足夠多的新行時,這個批處理會被運行(在任何時間,除了在批處理窗口中)。
批處理應用程序的其他實例只需要像上面這樣的批處理程序運行著以重新分配指標,以決定新實例的數(shù)量。
數(shù)據(jù)庫和應用設計原則
如果一個支持多分區(qū)(multi-partitioned)的應用程序架構(gòu),基于數(shù)據(jù)庫采用關鍵列(key column)分區(qū)方法拆成的多個表,則應該包含一個中心分區(qū)倉庫來存儲分區(qū)參數(shù)。這種方式提供了靈活性,并保證了可維護性。這個中心倉庫通常只由單個表組成,叫做分區(qū)表。
存儲在分區(qū)表中的信息應該是是靜態(tài)的,并且只能由 DBA 維護。每個多分區(qū)程序?qū)膯蝹€分區(qū)有一行記錄,組成這個表。這個表應該包含這些列:程序 ID 編號,分區(qū)編號(分區(qū)的邏輯ID),一個分區(qū)對應的關鍵列(key column)的最小值,分區(qū)對應的關鍵列的最大值。
在程序啟動時,應用程序架構(gòu)(Control Processing Tasklet, 控制處理微線程)應該將程序 id 和分區(qū)號傳遞給該程序。這些變量被用于讀取分區(qū)表,來確定應用程序應該處理的數(shù)據(jù)范圍(如果使用關鍵列的話)。另外分區(qū)號必須在整個處理過程中用來:
- 為了使合并程序正常工作,需要將分區(qū)號添加到輸出文件/數(shù)據(jù)庫更新
- 向框架的錯誤處理程序報告正常處理批處理日志和執(zhí)行期間發(fā)生的所有錯誤
死鎖最小化
當程序并行或分區(qū)運行時,會導致數(shù)據(jù)庫資源的爭用,還可能會發(fā)生死鎖(Deadlocks)。其中的關鍵是數(shù)據(jù)庫設計團隊在進行數(shù)據(jù)庫設計時必須考慮盡可能消除潛在的競爭情況。
還要確保設計數(shù)據(jù)庫表的索引時考慮到性能以及死鎖預防。
死鎖或熱點往往發(fā)生在管理或架構(gòu)表上,如日志表、控制表、鎖表(lock tables)。這些影響也應該納入考慮。為了確定架構(gòu)可能的瓶頸,一個真實的壓力測試是至關重要的。
要最小化數(shù)據(jù)沖突的影響,架構(gòu)應該提供一些服務,如附加到數(shù)據(jù)庫或遇到死鎖時的 等待-重試(wait-and-retry)間隔時間。這意味著要有一個內(nèi)置的機制來處理數(shù)據(jù)庫返回碼,而不是立即引發(fā)錯誤處理,需要等待一個預定的時間并重試執(zhí)行數(shù)據(jù)庫操作。
參數(shù)處理和校驗
對程序開發(fā)人員來說,分區(qū)架構(gòu)應該相對透明。框架以分區(qū)模式運行時應該執(zhí)行的相關任務包括:
- 在程序啟動之前獲取分區(qū)參數(shù)
- 在程序啟動之前驗證分區(qū)參數(shù)
- 在啟動時將參數(shù)傳遞給應用程序
驗證(validation)要包含必要的檢查,以確保:
- 應用程序已經(jīng)足夠涵蓋整個數(shù)據(jù)的分區(qū)
- 在各個分區(qū)之間沒有遺漏斷代(gaps)
如果數(shù)據(jù)庫是分區(qū)的,可能需要一些額外的驗證來保證單個分區(qū)不會跨越數(shù)據(jù)庫的片區(qū)。
體系架構(gòu)應該考慮整合分區(qū)(partitions).包括以下關鍵問題:
- 在進入下一個任務步驟之前是否所有的分區(qū)都必須完成?
- 如果一個分區(qū) Job 中止了要怎么處理?
總結(jié)
以上是生活随笔為你收集整理的Spring Batch 批量处理策略的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android 之ViewStub
- 下一篇: JSPatch Convertor 实现