1. 程式人生 > 其它 >Map-Reduce 思想在 ABAP 程式設計中的一個實際應用案例

Map-Reduce 思想在 ABAP 程式設計中的一個實際應用案例

ABAP 是一門企業級應用程式語言,其 740 版本於 2013 年釋出,增添了許多新的語法和關鍵字:

其中一個亮點就是新引入的 REDUCE 關鍵字。這個關鍵字的作用和在大規模資料集平行計算領域裡廣泛使用的 Map-Reduce 程式設計模型中的 Reduce 操作類似,可以按照字面意思理解為歸約

什麼是 Map-Reduce 思想?

Map-Reduce 是一種程式設計模型和相關實現,用於在叢集上使用並行分散式演算法,生成和處理大規模資料集。

一個 Map-Reduce 程式由一個 Map 過程和一個 Reduce 方法組成。Map 過程負責執行過濾和排序,例如將學生按名字排序到佇列中,每個名稱由一個佇列維護。

Reduce 方法負責執行彙總操作,例如計算學生的數量。
Map-Reduce 系統通過編排分散式伺服器,來並行執行各種任務,管理系統各個部分之間的所有通訊和資料傳輸,以及提供資料冗餘,實現容錯機制。

下圖是 Map Reduce 框架的工作步驟,統計一個海量輸入資料集(比如大於 1TB )中的單詞出現次數。工作步驟包含 Splitting, Mapping, Shuffling, Reducing 以得到最後的輸出結果。

Map-Reduce 程式設計模型已經廣泛運用於大資料處理領域的工具和框架,比如 Hadoop 之中。

Map-Reduce 在 CRM 系統中的一個實際應用

我們來看一個筆者工作中的實際任務。我需要在某個 CRM 測試系統上做個統計,列出在資料庫表 CRM_JSTO 裡,OBTYP(Object Type) 和 STSMA(Status Schema) 這兩列擁有相同值的內錶行的個數。大家可以把 OBTYP 和 STSMA 兩列具有相同值的內錶行

這個描述,類比成上圖中重複出現的單詞。

下圖是系統中資料庫表 CRM_JSTO 的部分行:

下圖是筆者最終完成的統計結果:

測試系統上資料庫表總的行數超過 55 萬行,其中有 90279 行,只維護了 OBTYP 為TGP,而沒有維護 STSMA.

排名第二的是 COH 和 CRMLEAD 的組合,出現了 78722 次。

上圖這個結果是怎麼統計出來的呢?

稍稍做過一些 ABAP 開發的朋友們,一定會立即寫出下面的程式碼:

利用 SELECT COUNT 直接在資料庫層完成統計工作。這也是 SAP 推薦的做法,即所謂 Code pusudown 準則,即能放到 HANA 資料庫層面進行的操作,就儘量放進去,以充分利用 HANA 強大的計算能力。在資料庫能夠完成計算邏輯的前提下,儘量避免把計算邏輯放到 Netweaver ABAP 應用層去做。

不過,我們也需要注意到這種方式的侷限性。SAP CTO 曾經有過一句名言:

There is no future with ABAP alone
There is no future in SAP without ABAP

未來的 ABAP 會走向開放,互聯的道路。回到這個需求本身,假設待檢索的輸入資料不是從 ABAP 資料庫表中來,而是來自 HTTP 請求,或者第三方系統發過來的 IDOC,此時我們無法再使用 OPEN SQL 本身的 SELECT COUNT 操作,而只能在 ABAP 應用層解決這個問題。

下面介紹兩種用 ABAP 程式語言完成這一需求的解決方案。

第一種方式比較傳統,實現在方法 get_result_traditional_way 裡:

ABAP 的 LOOP AT GROUP BY 這個關鍵字組合簡直就像是為這個需求量身定做一般:給 GROUP BY 指定 obtyp 和 stsma 這兩列,然後 LOOP AT 會自動將輸入內表的行記錄根據這兩列的值進行分組,每組行記錄的個數通過關鍵字 GROUP SIZE 自動計算出來,每組各自的 obtyp 和 stsma 的值,以及組內行記錄的條目數,儲存在 REFERENCE INTO 指定的變數 group_ref 裡。ABAP 開發人員需要做的事情,只是簡單地把這些結果儲存到輸出內表即可。

第二種辦法,就是本文標題所述,使用 ABAP 740 新引入的 REDUCE 關鍵字:

REPORT zreduce1.

DATA: lt_status TYPE TABLE OF crm_jsto.

SELECT * INTO TABLE lt_status FROM crm_jsto.

DATA(lo_tool) = NEW zcl_status_calc_tool( ).

lo_tool = REDUCE #( INIT  o = lo_tool
                          local_item = VALUE zcl_status_calc_tool=>ty_status_result( )
                     FOR GROUPS <group_key> OF <wa> IN lt_status
                      GROUP BY ( obtyp = <wa>-obtyp stsma = <wa>-stsma )
       ASCENDING NEXT local_item = VALUE #( obtyp = <group_key>-obtyp
                                             stsma = <group_key>-stsma
       count = REDUCE i( INIT sum = 0 FOR m IN GROUP <group_key>
               NEXT sum = sum + 1 ) )
       o = o->add_result( local_item ) ).

DATA(ls_result) = lo_tool->get_result( ).

上面的程式碼乍一看可能覺得有點晦澀,但仔細閱讀後發現這種方式本質上也採用了和方法一 LOOP AT GROUP BY 同樣的分組策略——根據 obtyp 和 stsma 分組,這些子組通過變數 group_key標識,然後通過第 10 行的 REDUCE 關鍵字,通過累加的方式,手動計算這個組的條目數——把一個大的輸入集根據 GROUP BY 指定的條件歸約成一個個規模更小的子集,然後分別針對子集進行計算——這就是 REDUCE 關鍵字通過字面含義傳遞給 ABAP 開發人員的處理思想。

總結和比較一下這三種實現方式:當待統計的資料來源為 ABAP 資料庫表時,一定優先選用 OPEN SQL 的方式,使計算邏輯在資料庫層完成,以獲得最佳的效能。

當資料來源並非 ABAP 資料庫表,而分組統計的需求為簡單的計數操作(COUNT)時, 優先用LOOP AT ... GROUP BY ... GROUP SIZE,使得計數操作通過 GROUP SIZE 在ABAP kernel 完成,以獲得較好的效能。

當資料來源並非 ABAP 資料庫表,而分組統計的需求為自定義的邏輯時,用本文介紹的第三種 REDUCE 解法,將自定義統計邏輯寫在第 11 行的 NEXT 關鍵字後。

三種解法的效能評測

我編寫了一個簡單的報表進行效能評測:

DATA: lt_status TYPE zcl_status_calc_tool=>tt_raw_input.

SELECT * INTO TABLE lt_status FROM crm_jsto.

DATA(lo_tool) = NEW zcl_status_calc_tool( ).

zcl_abap_benchmark_tool=>start_timer( ).
DATA(lt_result1) = lo_tool->get_result_traditional_way( lt_status ).
zcl_abap_benchmark_tool=>stop_timer( ).

zcl_abap_benchmark_tool=>start_timer( ).
lo_tool = REDUCE #( INIT  o = lo_tool
                          local_item = VALUE zcl_status_calc_tool=>ty_status_result( )
                     FOR GROUPS <group_key> OF <wa> IN lt_status
                      GROUP BY ( obtyp = <wa>-obtyp stsma = <wa>-stsma )
       ASCENDING NEXT local_item = VALUE #( obtyp = <group_key>-obtyp
                                             stsma = <group_key>-stsma
       count = REDUCE i( INIT sum = 0 FOR m IN GROUP <group_key>
               NEXT sum = sum + 1 ) )
       o = o->add_result( local_item ) ).

DATA(lt_result2) = lo_tool->get_result( ).
zcl_abap_benchmark_tool=>stop_timer( ).

ASSERT lt_result1 = lt_result2.

測試資料如下:

這三種解法的效能依次遞減,不過適用的場合和靈活程度依次遞增。

LOOP AT ... GROUP BY ... GROUP SIZE 這種解決方案,在筆者工作的 ABAP 測試伺服器上,處理 55 萬條記錄,用了 0.3 秒,而 REDUCE 則需花費 0.8 秒, 兩種解法的效能處於同一數量級之內。

總結

Map-Reduce 是一種程式設計模型和相關實現,用於在叢集上使用並行分散式演算法,生成和處理大規模資料集。ABAP 程式語言從語言層面支援對大規模資料的 REDUCE 操作。本文分享了筆者工作中使用 Map-Reduce 思路處理大規模資料集的一個實際案例,並與傳統的另外兩種解決方案做了比較。在效能不遜於傳統解決方案的前提下,基於 Map-Reduce 的解決方案,具有更為廣泛的應用場合和可擴充套件性。希望本文分享的內容對大家使用 ABAP 處理類似問題時有所啟發,感謝閱讀。