Distributed File System按塊(Block)存放資料,檔案大小比塊大小(64MB)小的檔案稱為小檔案。分布式系統不可避免會產生小檔案,比如SQL或其他分布式引擎的計算結果、Tunnel資料擷取。合并小檔案可以達到最佳化系統效能的目的。本文為您介紹如何在MaxCompute中合并小檔案。
背景資訊
小檔案過多,會帶來以下問題:
MaxCompute處理單個大檔案比處理多個小檔案更有效率,小檔案過多會影響整體的執行效能。
小檔案過多會給Pangu檔案系統帶來一定的壓力,影響儲存空間的有效利用。
因此從儲存和效能兩方面考慮,都需要將計算過程中產生的小檔案合并。MaxCompute在小檔案處理方面的功能日趨完善,主要體現在以下方面:
預設情況下,當作業完成之後,如果滿足一定的條件,系統會自動分配一個Fuxi Task進行小檔案合并,即使用過程中經常看到的MergeTask。
預設情況下,一個Fuxi Instance不再只能處理一個小檔案,而是最多可以處理100個小檔案,同時可以通過
odps.sql.mapper.merge.limit.size
參數來控制讀取檔案總大小。MaxCompute後台會定期掃描中繼資料庫,對小檔案較多的表或分區進行小檔案合并,這個合并過程對您透明。
但是通過中繼資料發現仍然存在大量的小檔案未被合并掉,例如有的表一直在寫入,無法自動執行合併作業,需要您先將寫入作業停止,然後再手工進行小檔案合併作業。
注意事項
使用合并小檔案功能需要用到計算資源,如果您購買的執行個體是隨用隨付,會產生相關費用,具體計費規則與SQL隨用隨付保持一致,詳情請參見計算費用。
合并小檔案命令不支援Transactional表,如果需要對Transactional表進行小檔案合并,請參見COMPACTION。
查看錶的檔案數
命令文法
您可以通過如下命令查看錶的檔案數:
desc extended <table_name> [partition (<pt_spec>)];
參數說明
table_name:必填。待查看錶的名稱。
pt_spec:可選。待查看分區表的指定分區。格式為
(partition_col1 = partition_col_value1, partition_col2 = partition_col_value2, ...)
。
結果樣本
如下圖所示,
odl_bpm_wfc_task_log
表的樣本分區有3607個檔案, 分區大小274MB (287869658Byte),平均檔案大小約為0.07MB,需要進行小檔案合并。
解決方案
通過中繼資料檢測,分區中含有100個以上的檔案且平均檔案大小小於64MB的都可以進行小檔案合并,合并的方案有如下兩種。
即時合并
使用如下命令進行小檔案即時合并。
ALTER TABLE <table_name> [partition (<pt_spec>)] MERGE SMALLFILES;
對
odl_bpm_wfc_task_log
表執行即時合併作業,完成後如下所示檔案數減少為19個,儲存大小也減少到37MB,在合并小檔案的同時,儲存效率也有了明顯提升。一般情況下,使用預設參數可以達到合并小檔案的效果。但MaxCompute同時提供一些參數完成定製需求,常用的一些參數如下:
set odps.merge.cross.paths=true|false
設定是否跨路徑合并,對於表下面有多個分區的情況,合并過程會將多個分區產生獨立的MergeAction進行合并,所以對於
odps.merge.cross.paths
設定為true,並不會改變路徑個數,只是分別去合并每個路徑下的小檔案。set odps.merge.smallfile.filesize.threshold = 64
設定合并檔案的小檔案大小閾值,檔案大小超過該閾值,則不進行合并,單位為MB。此參數可以不進行設定,不設定時,則使用全域變數
odps_g_merge_filesize_threshold
,該參數值預設為32MB,設定時必須大於32MB。set odps.merge.maxmerged.filesize.threshold = 500
設定合并輸出檔案量的大小,輸出檔案大於該閾值,則建立新的輸出檔案,單位為MB。此參數可以不進行設定,不設定時,則使用全域變
odps_g_max_merged_filesize_threshold
,該參數值預設為500MB,設定時必須大於500MB。
PyODPS指令碼合并
通過PyODPS非同步提交任務,合并前一天任務產出的小檔案,指令碼樣本如下:
import os from odps import ODPS # 確保 ALIBABA_CLOUD_ACCESS_KEY_ID 環境變數設定為使用者 Access Key ID, # ALIBABA_CLOUD_ACCESS_KEY_SECRET 環境變數設定為使用者 Access Key Secret, # 不建議直接使用 Access Key ID / Access Key Secret 字串 o = ODPS( os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'), os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'), project='your-default-project', endpoint='your-end-point', ) #注意需要替換$table_name為所需的表名 table_name = $table_name t = odps.get_table(table_name) #設定merge選項 hints = {'odps.merge.maxmerged.filesize.threshold': 256} #examples for multi partition insts = [] #iterate_partitions list列舉ds=$datetime下所有分區,分區格式也可能是其他格式 for partition in t.iterate_partitions(spec="ds=%s" % $datetime): instance=odps.run_merge_files(table_name, str(partition), hints=hints) #從這個logview的Waiting Queue點進去,才可以找到真正執行的logview print(instance.get_logview_address()) insts.append(instance) #等待分區結果 for inst in insts: inst.wait_for_completion()
運行上述指令碼需要提前安裝PyODPS,安裝方法請參見PyODPS。
使用案例
tbcdm.dwd_tb_log_pv_di
是資料穩定性體系識別出來的需要合并小檔案的物理表,通過中繼資料tbcdm.dws_rmd_merge_task_1d
提供的資訊,如下圖所示,可以看出此表相關分區的檔案個數大部分都在1000以上,多的甚至達到7000以上,但平均檔案大小有些還不到1MB。使用如下命令採用即時合并方案對其進行小檔案合并。
set odps.merge.cross.paths=true;
set odps.merge.smallfile.filesize.threshold=128;
set odps.merge.max.filenumber.per.instance = 2000;
alter table tbcdm.dwd_tb_log_pv_di partition (ds='20151116') merge smallfiles;
合并小檔案後結果如下圖所示: