批量處理策略

批量處理策略

 

爲了幫助設計和開發人員設計和實現批量系統,應該以示例結構圖表和代碼框架的形式,爲他們提供基本的批量應用構建塊和模式。在開始設計一個批量作業時,應該把業務邏輯分解成一系列的步驟,這些步驟可以使用下列標準構建塊來實現:

1、  轉換程序:對於每一種外系統提供或產生的文件類型,需要創建轉換程序,將提供的交易記錄轉換成處理需要的標準格式。這種類型的批量應用,可以部分或全部地由轉換工具模塊組成(見:基本批量服務)

2、  驗證程序:驗證程序確保所有輸入/輸出記錄正確和一致。典型的驗證基於文件頭和尾、檢查計數和校驗算法以及記錄級別的交叉檢查

3、  抽取程序:從數據庫或文件讀取記錄集,基於預定義的規則選擇程序,並將記錄寫到輸出文件

4、  抽取/更新程序:從數據庫或文件讀取記錄集,基於預定義的規則選擇程序,根據從每次輸入記錄中找到的數據,更新數據庫或輸出文件

5、  處理/更新程序:對經過抽取或驗證的輸入交易進行處理,通常涉及讀取數據庫以獲取處理需要的數據,可能會更新數據庫併爲輸出處理創建記錄

6、  輸出/格式化程序:讀取輸入文件,根據標準的格式,重新組織每條記錄數據的結構,然後打印輸出到文件,或者傳給另一個程序或系統。

此外,利用前面提到的這些構建塊,還不能搭建提供給業務邏輯的基礎應用外殼。

除了這些主要的構建塊,應用可能會用到一個或多個標準的公共步驟,比如:

1、  排序:讀取輸入文件,根據記錄的某個排序字段,將重新排序的記錄寫到生成的輸出文件。排序通常通過標準的系統工具程序執行

2、  拆分:讀取單個輸入文件,基於某個字段的值,將每條記錄寫到若干個輸出文件中的一個。拆分可以由帶參數的標準系統工具程序來裁剪或執行

3、  合併:讀取多個輸入文件,組合輸入文件中的數據,輸出到生成的單個文件中。合併可以由帶參數的標準系統工具程序來裁剪或執行

 

另外,批量應用可以根據輸入源劃分爲:

1、  數據庫驅動型應用:以從數據庫獲取的記錄行或值驅動

2、  文件驅動型應用:以從文件獲取的記錄或值驅動

3、  消息驅動型應用:以從消息隊列獲取的消息驅動

 

任何批量系統的基礎,是處理策略,影響策略選擇的因素包括:估算的批量系統數據量、與聯機或其他批量系統的併發性、可用的批量窗口期(batch windows)(隨着越來越多的企業希望提升到7X24運行,已經空不出明顯的批量窗口期了)。

 

批量的典型處理選擇是:

1、  在離線期批量窗口時間裏的常規處理

2、  批量/聯機併發處理

3、  同時並行處理多個不同的批量運行或作業

4、  分塊(即同時處理相同作業的多個實例)

5、  上述情況的組合

 

上面列表的順序,將影響實現的複雜性,在批量窗口期的處理最簡單,而分塊實現起來最複雜。

 

商業調度程序可能支持部分或全部的選項。

 

這一節,將詳細討論這些處理選擇。有個重要問題需要注意,批量處理採用的提交和鎖定策略,依賴於執行的處理類型;而且一般來說,聯機的鎖策略也應該使用相同的原則。

 

策略可以只使用通常的數據庫鎖,或者在架構中實現另外的自定義鎖服務。鎖服務將跟蹤數據庫鎖(比如通過在一個專門的數據庫表保存必要的信息),提供或拒絕對應用程序請求數據庫操作的許可。還應該在架構中實現重試邏輯,避免萬一在鎖定狀態下,取消批量作業。

 

1       批量窗口期的常規處理

對於單獨批量窗口期的簡單批量處理的運行,它要更新的數據,不會被聯機用戶,或其他批量處理,不會有併發問題,可以在批量運行結束時,再做單次提交。

往往越健壯的方式,就越適合。要記住,批量系統不管在複雜性方面,還是在要處理的數據量方面,都有隨時間增長的趨勢。如果系統沒有適當的鎖策略,而是依然指望單點提交,批量系統修改起來將很痛苦。所以,即使是最簡單的批量系統,也跟下面關於更復雜的情況提到的一樣,要爲重啓-恢復的選擇,考慮提交邏輯。

2       批量/聯機併發處理

批量應用處理的數據,可能同時被聯機用戶更新,不應鎖定任何數據(不過是數據庫裏的還是文件裏的),可能僅僅幾秒鐘裏,聯機用戶就會需要這些數據;而且,每隔幾個交易,就應該提交更新到數據庫中。這將盡可能的減少對其他處理不可用的數據部分,縮短這些數據的不可用時間段。

 

另一個減少物理鎖的選擇,是實現一個邏輯的行級鎖,不論是採用樂觀鎖模式或悲觀鎖模式。

1、  樂觀鎖假定記錄競爭的可能性比較低。它通常意味着,在每個被批量和聯機處理併發訪問的數據庫表中,增加一個時間戳列。當一個程序在處理時,獲取到一行記錄,它同時也取到了時間戳。然後,當這個程序嘗試更新這行被處理的數據時,這個更新將在WHERE子句使用原來的時間戳。如果時間戳不匹配,說明有另一個程序已經在它獲取記錄和嘗試更新期間,更新了這同一行記錄,因而這次更新不會被執行。

2、  悲觀鎖假定記錄競爭的可能性非常高,因而在獲取數據時,需要物理鎖或邏輯鎖。一種邏輯悲觀鎖是,在數據庫表使用專門一列作爲鎖。當一個程序爲了更新獲取這行記錄時,會在鎖列設置一個標幟,由於存在這個標幟,其他程序在嘗試獲取這條記錄時邏輯上會失敗。當這個設置標幟的程序更新這條記錄時,它同時會清除這個標幟,讓其他程序能夠獲取到這行記錄。請注意,必須維護初始查詢和標幟設置之間的數據完整性,比如通過使用數據庫鎖(例如SELECT FOR UPDATE)。同時也要注意,這種方式與物理鎖定有着同樣的缺點,除非建立一種超時機制,假如用戶去喫午飯了,那麼被鎖定記錄能夠自動解鎖,這樣管理起來會更稍微容易些。

這些模式不一定適合批量處理,但是在批量和聯機的併發處理時可能用到(比如,在數據庫不支持行鎖的情況下)。一般來說,樂觀鎖更適合聯機應用,而悲觀鎖更適合批量應用。無論何時使用邏輯鎖,都必須用相同的方式,通過邏輯鎖保護到所有應用訪問的數據記錄上。

要注意,這兩個解決方案都只定位於對單條記錄的鎖定,我們經常需要鎖定邏輯相關的一組記錄。對於物理鎖,你必須非常小心的管理,以避免潛在的死鎖;對於邏輯鎖,最好是建立一個邏輯鎖管理器,它瞭解你要保護的邏輯記錄組,可以確保鎖的清晰一致,不會死鎖。這個邏輯鎖管理器,通常使用自己的表來實現鎖管理、競爭報告、超時機制等。

3       並行處理

並行處理允許多個批量作業並行運行,以最大地減少整個批量處理消耗的時間。如果作業之間不共享相同的文件、數據表或索引空間,這不會有什麼問題;如果有,那麼應該使用數據分塊來實現這個服務。另一個辦法是,構建一個架構模塊,使用一張控制表來維護依賴關係。控制表的數據行裏,應該包含每個共享資源,以及它是否在被某個應用使用。批量框架或並行作業的應用,應該從這張表讀取信息,再決定它是否能夠訪問自己需要的資源。

 

如果數據訪問沒有問題,可以通過使用另外的線程來實現並行處理。在主機環境,爲了確保讓所有的進程都有足夠的CPU時間,使用了傳統的並行作業類。不管怎樣,這種方案足夠健壯,能夠保證所有在運行進程的時間片。

 

並行處理的其他關鍵問題還包括,負載均衡和公共系統資源的可用性(如文件、數據庫緩衝池)等。還要注意,控制表本身也很容易變成臨界資源。

4       分塊

利用分塊(partitioning),可以讓大規模批量應用的多個版本併發運行。這樣做的目的,是減少處理長批量作業花費的時間。那些輸入文件可以拆分或(且)主要的數據庫表可以分塊,應用可以在不同的數據集上運行的長批量作業,就可以成功的進行分塊處理。

 

另外,必須設計好讓已分塊的處理,只能處理分配給他們的數據集。分塊架構必須與數據庫設計和數據庫分區策略緊密關聯。請注意,數據庫分區並不必然意味着數據庫的物理分區,儘管多數情況下這樣做是明智之舉。下圖闡明瞭分塊方式:

這個架構應該足夠靈活,允許動態配置分塊的數量。應該考慮批量自動配置和用戶控制配置,自動配置可以基於參數,如輸入文件大小,或輸入記錄數。

4.1     分塊方式

下面列出了可能的分塊方式。選擇分區方式,必須視情況而定。

4.1.1  整理和偶數拆散數據集

這種方式包括拆散輸入記錄集,分成偶數塊(如10),每部分塊正好是整個記錄集的1/10。然後,每一塊由一個批量/抽取程序的實例來處理

 

要使用這種方式,需要對記錄集進行拆分處理。拆分的結果,是一個從低到高的限定記錄位置的數字序列,批量/抽取程序用它作爲輸入,以限制只對自己單獨的那部分數據進行處理

 

由於需要計算和決定記錄集的每塊分區的邊界,這個處理可能開銷很大。

 

4.1.2  按關鍵列拆散

根據關鍵列,如地區代碼,來拆散輸入記錄集,一個批量實例分配一個鍵值的數據。要達到這個目的,列值可以:

1、  通過分區表分配給批量實例(稍後詳述)

2、  通過分塊部分的值分配給批量實例(如:分塊0000-09991000-1999等等)

對於選擇1,將意味着,增加新的值時,要手動配置批量/抽取,以確保這個新值添加給某個特定的實例。

對於選擇2,它將確保所有的值都會被批量作業的實例覆蓋到。但是,某個實例處理數據的數量,依賴於列值的分佈(例如,在0000-0999範圍內,可能分佈有大量的數據,而1000-1999範圍內卻很少)。這種方式,應該在設計數據範圍時就想到分塊。

這兩者選擇,都不能實現對批量實例的數據平均分佈,不能使用動態配置批量實例數量。

 

4.1.3  按視圖拆散

這種方式,根本上就是按關鍵列拆散,只不過是在數據庫層面。它涉及拆散記錄集到視圖,這些視圖由每個批量應用的實例在處理過程中使用,通過分組數據完成拆分。

 

用這種方式,將針對特定的視圖(而不是主表)配置每個批量應用實例。同樣,在增加新的數據值是,必須增加一個視圖來包含新增加的數據組。對實例數目的變更,同樣會導致視圖的變更,沒有任何動態配置能力。

4.1.4  增加處理指示符

在輸入表中增加一個新的列,作爲指示符。作爲一個處理步驟,所有的指示符將標記爲“未處理”。在批量應用抓取數據期間,只讀取那些標記爲“未處理”的記錄,一旦讀取(通過鎖定),則標記爲“處理中”。當這些記錄處理完成後,更新指示符爲“成功”或“出錯”。由於這個附加列保證了一條記錄只會被處理一次,多實例批量應用可以不用做修改就啓動。

 

使用這種方式,這張表的I/O對動態地增長。如果是更新類的批量應用,由於總是會發生寫入操作,這種方式的影響會減少。

4.1.5  抽取表到普通文件

這種方式涉及從表到文件的抽取,然後,把這個文件拆分成多個段,作爲批量實例的輸入使用。

 

使用這種方式,抽取表到文件並作拆分的額外開銷,可能抵消多個分塊帶來的好處。通過修改文件拆分腳本,可以實現動態配置。

4.1.6  使用哈希列

這種方案,要在數據庫表上增加一個哈希列(鍵/索引),用來獲取對應記錄。在哈希列上有一個指示器,它決定由哪個批量應用實例,處理這條特定記錄。例如,假如啓動了3批量實例,那麼指示符‘A’將標誌這一行由實例1處理,而指示符‘B’標誌該行由實例2處理,如此等等。

 

因而獲取記錄的過程,將增加一個WHERE子句,以通過特定的指示符選擇所標誌的全部行。插入這張表時,也要增加這個標示列,可以默認爲某個實例(如‘A’)。

 

可以用一個簡單批量程序來更新這個指示符,比如在不同的實例之間從新分佈負載。當增加了足夠多數目的新記錄行時,就可以運行這個批量(除批量窗口期外的其他任何時候),把這些新行從新分發給其他實例。

 

要增加新的批量應用實例,只需要運行上述這個批量,從新分發標識符給那些新的實例。

4.2     數據庫和應用設計原則

要支持運行在用關鍵列方式分區的數據庫表上的多分塊應用的架構,應該包含一箇中央分塊倉庫,以保存分塊參數。這樣做提供靈活性,保證可維護性。這個倉庫通常有單表構成,即所謂分區表。

 

保存在分區表中的信息將是靜態的,通過應用有DBA來維護。這張表應該由包含多分塊應用的每個分塊的信息的行組成,它應用有這些列:程序ID碼、分塊數(分塊的邏輯ID)、這個分塊的數據庫關鍵列最低值、該分塊的數據庫關鍵列最高值。

 

在啓動程序時,框架(小任務處理控制)應該把程序ID和分塊數傳給它,這些變量用於讀取分區表,決定這個程序應該處理哪些範圍的數據(如果使用關鍵列方式)。此外,整個處理過程都必須使用這個分塊數:

1、  爲了正確地進行合併處理,要在輸出文件中/數據庫更新時添加分塊數

2、  報告正常的處理批量日誌中,和執行期間發生的任何錯誤給框架的錯誤處理器。

4.3     儘量減少死鎖

當應用並行或分塊運行時,數據庫資源產生競爭,可能發生死鎖。作爲數據庫設計的一部分,儘可能地消除潛在的競爭條件,對於數據庫設計團隊來說,非常關鍵。

 

同樣,要確保數據庫索引表爲防止死鎖和關注性能而設計。

 

死鎖或熱點,經常發生在管理或框架表,比如日誌表、控制表。實現這些表時,也要考慮到這一點。爲了鑑別架構中的可能瓶頸,真實的壓力測試至關重要。

 

爲了將數據衝突的影響減小到最低,框架應該提供這樣一些服務,如:當連接到數據庫或碰到死鎖時,定期地等待並重試。這意味着需要一種內建機制,針對特定的數據庫返回碼做出反應,而不是拋出即時異常進行處理,等待一段預先確定的時間,再重試數據庫操作。

4.4     參數的傳遞和驗證

對應用開發者,分塊架構應該相對地透明。以分塊模式運行應用,框架應該執行所有與之相關的任務,包括:

1、  在應用啓動前獲取分塊參數

2、  在應用啓動前驗證分塊參數

3、  在啓動時傳遞參數給應用

 

驗證應該包括檢查和確保:

1、  應用有足夠的分塊,能覆蓋整個數據範圍

2、  分塊之間沒有缺失

 

如果數據庫已經分區,可能需要一下附加的驗證,以確保單個分塊不會橫跨數據庫分區。

 

框架同樣應該考慮分塊的合併,關鍵問題包括:

1、  所有的分塊是否必須在進入到下一個作業步驟之前完成?

2、  如果某個分塊取消了,會發生什麼?

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