DistCp 指南



概觀

DistCp(分散式複製)是一個用於大型叢集間/叢集內複製的工具。它使用 MapReduce 來執行其分發、錯誤處理和復原,以及報告。它將檔案和目錄清單擴充為映射任務的輸入,每個任務將複製來源清單中指定檔案的一個分割區。

[DistCp 的前身實作] (https://hadoop.dev.org.tw/docs/r1.2.1/distcp.html) 在其用法、可擴充性和效能方面都有其怪癖和缺點。DistCp 重構的目的是修正這些缺點,使其能夠以程式化方式使用和擴充。已引入新的範例來改善執行時間和設定效能,同時保留舊有行為作為預設。

本文檔旨在描述新 DistCp 的設計、其嶄新的功能、其最佳使用方式,以及與舊有實作的任何差異。

用法

基本用法

DistCp 最常見的呼叫是叢集間複製

bash$ hadoop distcp hdfs://nn1:8020/foo/bar \
hdfs://nn2:8020/bar/foo

這將把 nn1 上 /foo/bar 下的命名空間擴充到一個暫存檔中,將其內容分割到一組映射任務中,並在每個 NodeManager 上從 nn1nn2 開始複製。

您還可以在命令列上指定多個來源目錄

bash$ hadoop distcp hdfs://nn1:8020/foo/a \
hdfs://nn1:8020/foo/b \
hdfs://nn2:8020/bar/foo

或者,等效地,從使用 -f 選項的檔案

bash$ hadoop distcp -f hdfs://nn1:8020/srclist \
hdfs://nn2:8020/bar/foo

其中 srclist 包含

hdfs://nn1:8020/foo/a
hdfs://nn1:8020/foo/b

從多個來源複製時,如果兩個來源發生衝突,DistCp 會中止複製並顯示錯誤訊息,但會根據指定的 選項 解決目標端的衝突。預設情況下,會略過目標端已存在的檔案(即不會以來源檔案取代)。每個工作結束時會報告略過的檔案數量,但如果複製器對某些檔案子集失敗,但在後續嘗試中成功,則報告可能不準確。

每個 NodeManager 都能連線並與來源和目標檔案系統通訊非常重要。對於 HDFS,來源和目標都必須執行相同版本的通訊協定,或使用向下相容的通訊協定;請參閱 [不同 HDFS 版本之間的複製] (#Copying_Between_Versions_of_HDFS)。

複製後,建議產生來源和目標的清單並進行交叉比對,以驗證複製是否真的成功。由於 DistCp 同時使用 Map/Reduce 和 FileSystem API,因此這三者之間或其中的任何問題都可能對複製造成負面且無聲的影響。有些人成功執行啟用 -update 的操作,以執行第二次傳遞,但使用者在嘗試此操作之前應先了解其語意。

另外值得注意的是,如果其他用戶端仍在寫入來源檔案,則複製可能會失敗。嘗試覆寫在目標端寫入的檔案也應該會在 HDFS 上失敗。如果來源檔案在複製之前被(重新)移除,則複製會失敗並顯示 FileNotFoundException

請參閱詳細的命令列參考,以取得 DistCp 中所有可用選項的資訊。

更新和覆寫

-update 用於複製目標端不存在或與目標版本不同的來源檔案。-overwrite 會覆寫目標端存在的目標檔案。

更新和覆寫選項需要特別注意,因為它們處理來源路徑的方式與預設值有非常細微的不同。考慮從 /source/first//source/second/ 複製到 /target/,其中來源路徑包含下列內容

hdfs://nn1:8020/source/first/1
hdfs://nn1:8020/source/first/2
hdfs://nn1:8020/source/second/10
hdfs://nn1:8020/source/second/20

當未在 DistCp 中呼叫 -update-overwrite 時,DistCp 預設會在 /target 下建立目錄 first/second/。因此

distcp hdfs://nn1:8020/source/first hdfs://nn1:8020/source/second hdfs://nn2:8020/target

會在 /target 中產生下列內容

hdfs://nn2:8020/target/first/1
hdfs://nn2:8020/target/first/2
hdfs://nn2:8020/target/second/10
hdfs://nn2:8020/target/second/20

當指定 -update-overwrite 時,會將來源目錄的內容複製到目標,而非來源目錄本身。因此

distcp -update hdfs://nn1:8020/source/first hdfs://nn1:8020/source/second hdfs://nn2:8020/target

會在 /target 中產生下列內容

hdfs://nn2:8020/target/1
hdfs://nn2:8020/target/2
hdfs://nn2:8020/target/10
hdfs://nn2:8020/target/20

進一步延伸,如果兩個來源資料夾都包含同名的檔案(例如 0),則兩個來源都會將一個項目對應到目的地的 /target/0。DistCp 會中止,而不是允許此衝突發生。

現在,考慮下列複製作業

distcp hdfs://nn1:8020/source/first hdfs://nn1:8020/source/second hdfs://nn2:8020/target

來源/大小

hdfs://nn1:8020/source/first/1 32
hdfs://nn1:8020/source/first/2 32
hdfs://nn1:8020/source/second/10 64
hdfs://nn1:8020/source/second/20 32

目標/大小

hdfs://nn2:8020/target/1 32
hdfs://nn2:8020/target/10 32
hdfs://nn2:8020/target/20 64

將會影響

hdfs://nn2:8020/target/1 32
hdfs://nn2:8020/target/2 32
hdfs://nn2:8020/target/10 64
hdfs://nn2:8020/target/20 32

略過 1,因為檔案長度和內容相符。複製 2,因為它不存在於目標中。覆寫 1020,因為內容與來源不符。

如果使用 -update,則會略過 1,因為檔案長度和內容相符。複製 2,因為它不存在於目標中。覆寫 1020,因為內容與來源不符。但是,如果另外使用 -append,則只會覆寫 10(來源長度小於目標)並將檔案變更附加到 20(如果檔案與目標的原始長度相符)。

如果使用 -overwrite,則也會覆寫 1

同步

-diff 選項會使用快照差異從來源叢集同步檔案到目標叢集。它會複製、重新命名和移除快照差異清單中的檔案。

使用 -diff 選項時,必須包含 -update 選項。

目前,大多數雲端供應商無法順利地進行同步。

用法

hadoop distcp -update -diff <from_snapshot> <to_snapshot> <source> <destination>

範例

hadoop distcp -update -diff snap1 snap2 /src/ /dst/

上述指令會將快照 snap1snap2 的變更(即從 snap1snap2 的快照差異)套用在 /src//dst/。顯然,它需要 /src/ 同時具有快照 snap1snap2。但是,目標 /dst/ 也必須具有與 <from_snapshot> 同名的快照,在本例中為 snap1。目標 /dst/snap1 以來不應有新的檔案作業(建立、重新命名、刪除)。請注意,當此指令完成時,不會在 /dst/ 建立新的快照 snap2

使用 -diff 選項需要 -update

例如,如果在 /src/ 中,在建立 snap1 之後和建立 snap2 之前,新增了 1.txt 並刪除了 2.txt,則上述指令會將 1.txt/src/ 複製到 /dst/,並從 /dst/ 刪除 2.txt

同步行為將使用以下實驗進行說明。

實驗 1:同步兩個相鄰快照的差異

開始前的一些準備工作。

# Create source and destination directories
hdfs dfs -mkdir /src/ /dst/
# Allow snapshot on source
hdfs dfsadmin -allowSnapshot /src/
# Create a snapshot (empty one)
hdfs dfs -createSnapshot /src/ snap1
# Allow snapshot on destination
hdfs dfsadmin -allowSnapshot /dst/
# Create a from_snapshot with the same name
hdfs dfs -createSnapshot /dst/ snap1

# Put one text file under /src/
echo "This is the 1st text file." > 1.txt
hdfs dfs -put 1.txt /src/
# Create the second snapshot
hdfs dfs -createSnapshot /src/ snap2

# Put another text file under /src/
echo "This is the 2nd text file." > 2.txt
hdfs dfs -put 2.txt /src/
# Create the third snapshot
hdfs dfs -createSnapshot /src/ snap3

然後執行 distcp 同步

hadoop distcp -update -diff snap1 snap2 /src/ /dst/

上述命令應會成功。1.txt 將從 /src/ 複製到 /dst/。同樣地,需要 -update 選項。

如果我們再次執行相同的命令,我們將會收到 DistCp sync failed 例外,因為自 snap1 以來,目的地已新增一個新檔案 1.txt。話雖如此,如果我們手動從 /dst/ 中移除 1.txt 並執行同步,命令將會成功。

實驗 2:同步兩個非相鄰快照的差異

首先從實驗 1 進行清理。

hdfs dfs -rm -skipTrash /dst/1.txt

執行同步命令,請注意 <to_snapshot> 已從實驗 1 中的 snap2 變更為 snap3

hadoop distcp -update -diff snap1 snap3 /src/ /dst/

1.txt2.txt 都將複製到 /dst/

實驗 3:同步檔案刪除作業

從實驗 2 的結尾繼續

hdfs dfs -rm -skipTrash /dst/2.txt
# Create snap2 at destination, it contains 1.txt
hdfs dfs -createSnapshot /dst/ snap2

# Delete 1.txt from source
hdfs dfs -rm -skipTrash /src/1.txt
# Create snap4 at source, it only contains 2.txt
hdfs dfs -createSnapshot /src/ snap4

現在執行同步命令

hadoop distcp -update -diff snap2 snap4 /src/ /dst/

2.txt 已複製,1.txt 已在 /dst/ 中刪除。

請注意,儘管 /src//dst/ 都具有名稱相同的快照 snap2,但快照不需要具有相同的內容。這表示,如果在 /dst/snap2 中有 1.txt,但它們的內容不同,1.txt 仍將從 /dst/ 中移除。同步命令不會檢查將要刪除的檔案的內容。它僅遵循 <from_snapshot><to_snapshot> 之間的快照差異清單。

此外,如果我們在上述步驟中建立 /dst/ 上的 snap2 之前從 /dst/ 中刪除 1.txt,以便 /dst/snap2 在執行同步命令之前沒有 1.txt,命令仍將成功。它不會在嘗試從不存在的 /dst/ 中刪除 1.txt 時擲回例外。

原始命名空間延伸屬性保留

此區段僅適用於 HDFS。

如果目標和所有來源路徑名稱都在 /.reserved/raw 層級結構中,則會保留「原始」命名空間延伸屬性。「原始」xattr 由系統用於內部功能,例如加密元資料。只有透過 /.reserved/raw 層級結構存取時,使用者才能看到它們。

原始 xattrs 的保留僅基於是否提供 /.reserved/raw 前綴。-p (保留,見下文) 標記不會影響原始 xattrs 的保留。

若要防止保留原始 xattrs,只需在任何來源和目標路徑上不使用 /.reserved/raw 前綴。

如果僅在來源和目標路徑的子集上指定 /.reserved/raw 前綴,將會顯示錯誤並傳回非 0 退出代碼。

命令列選項

標記 說明 備註
-p[rbugpcaxt] 保留 r:複製次數 b:區塊大小 u:使用者 g:群組 p:權限 c:檢查碼類型 a:ACL x:XAttr t:時間戳記 當指定 -update 時,除非檔案大小也有所不同(即除非重新建立檔案),否則狀態更新將不會同步。如果指定 -pa,DistCp 也會保留權限,因為 ACL 是權限的超集。選項 -pr 僅在來源和目標目錄均未進行刪除編碼時才有效。
-i 忽略失敗 如附錄中所述,此選項將保留比預設情況更準確的複製統計資料。它也會保留失敗複製的記錄,這對於除錯很有價值。最後,失敗的對應不會導致工作在嘗試所有分割之前就失敗。
-log <logdir> 將記錄寫入 <logdir> DistCp 會將其嘗試複製的每個檔案的記錄保留為對應輸出。如果對應失敗,則在重新執行時不會保留記錄輸出。
-v 在 SKIP/COPY 記錄中記錄額外資訊(路徑、大小) 此選項只能與 -log 選項一起使用。
-m <num_maps> 同時複製的最大數量 指定複製資料的對應數量。請注意,更多的對應不一定會改善傳輸量。
-overwrite 覆寫目的地 如果對應失敗且未指定 -i,則會重新複製分割中的所有檔案,而不僅僅是失敗的檔案。如使用說明檔中所述,它也會變更產生目的地路徑的語意,因此使用者應小心使用。
-update 如果來源和目的地在大小、區塊大小或檢查碼上不同,則覆寫 如前所述,這不是「同步」操作。檢查的條件是來源和目的地檔案大小、區塊大小和檢查碼;如果不同,來源檔案會取代目的地檔案。如使用說明文件中所述,它也會變更產生目的地路徑的語意,因此使用者應謹慎使用。
-append 具有相同名稱但不同長度的檔案的增量複製 如果來源檔案長度大於目的地檔案,則會比較共用長度部分的檢查碼。如果檢查碼相符,則只會使用讀取和附加功能複製差異。-append 選項僅與 -update 搭配使用,且不使用 -skipcrccheck
-f <urilist_uri> 使用 <urilist_uri> 中的清單作為來源清單 這等於在命令列中列出每個來源。urilist_uri 清單應為完全限定的 URI。
-filters 包含模式字串清單檔案的路徑,每行一個字串,符合該模式的路徑將從複製中排除。 支援由 java.util.regex.Pattern 指定的正規表示式。
-filelimit <n> 將檔案總數限制為 <= n 已棄用!新 DistCp 中會忽略。
-sizelimit <n> 將總大小限制為 <= n 位元組 已棄用!新 DistCp 中會忽略。
-delete 刪除存在於目的地但不存在於來源的檔案 刪除是由 FS Shell 執行。因此,如果啟用,將會使用垃圾桶。刪除僅適用於更新或覆寫選項。
-strategy {dynamic|uniformsize} 選擇要在 DistCp 中使用的複製策略。 預設使用 uniformsize。(即,根據每個映射複製的檔案總大小來平衡映射。類似於舊版。)如果指定「dynamic」,則會改用 DynamicInputFormat。(這在架構區段的 InputFormats 中有說明。)
-bandwidth 以 MB/秒指定每個映射的頻寬。 每個映射將被限制僅使用指定的頻寬。這並不總是精確的。映射會在複製期間降低其頻寬使用量,使得使用的頻寬趨向於指定的值。
-atomic {-tmp <tmp_dir>} 指定原子提交,並可選擇暫存目錄。 -atomic 指示 DistCp 將來源資料複製到暫時目標位置,然後將暫時目標原子性地移至最終位置。資料將以完整且一致的形式出現在最終目標,或根本不會出現。選擇性地,-tmp 可用於指定暫時目標的位置。如果未指定,將選擇預設值。注意:tmp_dir 必須在最終目標叢集上。
-async 非同步執行 DistCp。在 Hadoop 工作啟動後立即退出。 已記錄 Hadoop 工作識別碼,以進行追蹤。
-diff <oldSnapshot> <newSnapshot> 使用兩個快照之間的快照差異報告,找出來源和目標之間的差異,並套用差異到目標,使其與來源同步。 此選項僅與 -update 選項有效,且應符合下列條件。
  1. 來源和目標檔案系統都必須是 DistributedFileSystem。
  2. 兩個快照 <oldSnapshot><newSnapshot> 已建立在來源檔案系統上,且 <oldSnapshot> 早於 <newSnapshot>
  3. 目標具有相同的快照 <oldSnapshot>。自建立 <oldSnapshot> 以來,目標未進行任何變更,因此 <oldSnapshot> 與目標的目前狀態具有相同的內容。目標中的所有檔案/目錄都與來源的 <oldSnapshot> 相同。
-rdiff <newSnapshot> <oldSnapshot> 使用兩個快照之間的快照差異報告,找出自快照 <oldSnapshot> 建立在目標上以來,目標上已變更的內容,並反向套用差異到目標,並從來源的 <oldSnapshot> 複製已修改的檔案,使目標與 <oldSnapshot> 相同。 此選項僅與 -update 選項有效,且應符合下列條件。
  1. 來源和目標檔案系統都必須是 DistributedFileSystem。來源和目標可以是兩個不同的叢集/路徑,或它們可以完全是相同的叢集/路徑。在後一種情況下,已修改的檔案會從目標的 <oldSnapshot> 複製到目標的目前狀態)。
  2. 兩個快照 <newSnapshot><oldSnapshot> 已建立在目標檔案系統上,且 <oldSnapshot> 早於 <newSnapshot>。自建立 <newSnapshot> 以來,目標未進行任何變更。
  3. 來源具有相同的快照 <oldSnapshot>,其內容與目標上的 <oldSnapshot> 相同。目標的 <oldSnapshot> 中的所有檔案/目錄都與來源的 <oldSnapshot> 相同。
-numListstatusThreads 用於建立檔案清單的執行緒數目 最多 40 個執行緒。
-skipcrccheck 是否略過來源和目標路徑之間的 CRC 檢查。
-blocksperchunk <blocksperchunk> 每個區塊的區塊數目。指定時,將檔案分割成區塊以平行複製 如果設定為正值,區塊數目多於此值的檔案將分割成 <blocksperchunk> 個區塊,以便平行傳輸,並在目的地重新組合。預設情況下,<blocksperchunk> 為 0,檔案將完整傳輸而不分割。此開關僅適用於來源檔案系統實作 getBlockLocations 方法,且目標檔案系統實作 concat 方法時。
-copybuffersize <copybuffersize> 要使用的複製緩衝區大小。預設情況下,<copybuffersize> 設定為 8192B
-xtrack <path> 將遺失來源檔案的資訊儲存到指定的路徑。 此選項僅與 -update 選項有效。這是實驗性質,且無法與 -atomic 選項搭配使用。
-direct 直接寫入目的地路徑 當目的地為物件儲存時,可避免潛在耗時的暫時檔案重新命名作業,因此很有用
-useiterator 使用單執行緒 listStatusIterator 建立清單 可節省用戶端的記憶體。使用此選項將忽略 numListstatusThreads 選項

DistCp 的架構

新 DistCp 的元件可分類為下列類別

  • DistCp 驅動程式
  • 複製清單產生器
  • 輸入格式和 Map-Reduce 元件

DistCp 驅動程式

DistCp 驅動程式元件負責

  • 透過

    • OptionsParser 和
    • DistCpOptionsSwitch
  • 分析傳遞至命令列上 DistCp 命令的引數,並將命令引數組裝成適當的 DistCpOptions 物件,並初始化 DistCp。這些引數包括

    • 來源路徑
    • 目標位置
    • 複製選項(例如是否更新複製、覆寫、保留哪些檔案屬性等)
  • 透過

    • 呼叫複製清單產生器,以建立要複製的檔案清單。
    • 設定並啟動 Hadoop Map-Reduce 作業,以執行複製。
    • 根據選項,立即傳回 Hadoop MR 作業的控制代碼,或等到完成。

剖析器元素僅從命令列(或呼叫 DistCp::run() 時)執行。DistCp 類別也可以透過建構 DistCpOptions 物件和適當地初始化 DistCp 物件,以程式方式使用。

複製清單產生器

複製清單產生器類別負責建立要從來源複製的檔案/目錄清單。它們會檢查來源路徑的內容(包括萬用字元),並將所有需要複製的路徑記錄到 SequenceFile 中,供 DistCp Hadoop 作業使用。此模組中的主要類別包括

  1. CopyListing:任何複製清單產生器實作都應實作的介面。也提供具體 CopyListing 實作的選擇方式的工廠方法。
  2. SimpleCopyListingCopyListing 的實作,接受多個來源路徑(檔案/目錄),並遞迴列出每個路徑下的所有個別檔案和目錄,以供複製。
  3. GlobbedCopyListingCopyListing 的另一個實作,會在來源路徑中展開萬用字元。
  4. FileBasedCopyListingCopyListing 的實作,從指定檔案中讀取來源路徑清單。

根據 DistCpOptions 中是否指定來源檔案清單,來源清單會以下列其中一種方式產生

  1. 如果沒有來源檔案清單,則使用 GlobbedCopyListing。會展開所有萬用字元,並將所有展開項目轉送至 SimpleCopyListing,而 SimpleCopyListing 會建構清單(透過遞迴每個路徑)。
  2. 如果指定來源檔案清單,則使用 FileBasedCopyListing。來源路徑會從指定檔案中讀取,然後轉送至 GlobbedCopyListing。然後如上所述建構清單。

可以透過提供 CopyListing 介面的自訂實作,自訂建構複製清單的方法。DistCp 在這裡的行為與舊版 DistCp 不同,在於路徑如何考量複製。

也可以透過傳遞 CopyFilter 介面的目前支援實作或撰寫新的實作,自訂不應複製的檔案篩選。這可以透過在 DistCpOptions 中設定 distcp.filters.class 來指定

  1. distcp.filters.class 為 “RegexCopyFilter”。如果您使用此實作,則必須傳遞包含用於篩選的正規表示式的 “CopyFilter” distcp.filters.file。支援 java.util.regex.Pattern 指定的正規表示式。
  2. distcp.filters.class 為 “RegexpInConfigurationFilter”。如果您使用此實作,則必須使用 “DistCpOptions” 中的 distcp.exclude-file-regex 參數傳遞正規表示式。支援 java.util.regex.Pattern 指定的正規表示式。與 “RegexCopyFilter” 相比,這是一種更動態的方法。
  3. distcp.filters.class 為 “TrueCopyFilter”。如果未指定上述任何選項,則此選項會用作預設實作。

舊版實作只會列出必須複製到目標的路徑。例如,如果檔案已存在於目標(且未指定 -overwrite),則檔案甚至不會在 MapReduce 複製工作中考慮。在設定期間(即在 MapReduce 工作之前)確定這一點,涉及檔案大小和檢查總和比較,這可能會耗時。

新的 DistCp 會將此類檢查延後到 MapReduce 工作,從而減少設定時間。由於這些檢查會在多個映射中並行處理,因此效能會進一步提升。

InputFormats 和 MapReduce 元件

InputFormats 和 MapReduce 元件負責實際將檔案和目錄從來源複製到目的地路徑。在執行複製時,會使用在複製清單產生期間建立的清單檔案。這裡感興趣的類別包括

  • UniformSizeInputFormat:此 org.apache.hadoop.mapreduce.InputFormat 實作提供與 Legacy DistCp 相同的負載平衡功能,讓各個映射處理的資料量大致相同。UniformSizeInputFormat 的目標是讓每個映射複製的位元組數目大致相同。因此,清單檔案會分割成多個路徑群組,讓每個 InputSplit 中檔案大小的總和大致等於其他映射。分割並非總是完美,但其簡單的實作可將設定時間維持在低水準。

  • DynamicInputFormat 和 DynamicRecordReader:DynamicInputFormat 實作 org.apache.hadoop.mapreduce.InputFormat,是 DistCp 的新增功能。清單檔案會分割成多個「區塊檔案」,區塊檔案的數量會是 Hadoop 工作中要求的映射數目的倍數。在啟動工作之前,會將每個映射工作「指定」給其中一個區塊檔案(方法是將區塊重新命名為工作的 ID)。會使用 DynamicRecordReader 從每個區塊讀取路徑,並在 CopyMapper 中處理。處理完區塊中的所有路徑後,會刪除目前的區塊並取得新的區塊。此程序會持續進行,直到沒有更多區塊可用為止。這種「動態」方法讓速度較快的映射工作可以比速度較慢的工作處理更多路徑,進而加速整體 DistCp 工作。

  • CopyMapper:此類別實作實體檔案複製。會將輸入路徑與輸入選項(在工作的設定中指定)進行比對,以判斷是否需要複製檔案。只有符合下列其中一個條件時,才會複製檔案

    • 目標中不存在同名檔案。
    • 目標中存在同名檔案,但檔案大小不同。
    • 目標中存在同名檔案,但檢查碼不同,且未指定 -skipcrccheck
    • 目標中存在同名檔案,但指定了 -overwrite
    • 目標中存在同名檔案,但區塊大小不同(且需要保留區塊大小)。
  • CopyCommitter:此類別負責 DistCp 工作的提交階段,包括

    • 保留目錄權限(如果在選項中指定)
    • 清除暫存檔案、工作目錄等。

附錄

映射大小調整

預設情況下,DistCp 會嘗試調整每個映射的大小,讓每個映射複製的位元組數目大致相同。請注意,檔案是細微程度最高的層級,因此增加同時複製的數量(即映射)並不一定會增加同時複製的數量或整體處理量。

新的 DistCp 也提供一種「動態」調整映射大小的策略,讓速度較快的資料節點可以複製比速度較慢的節點更多的位元組。使用 -strategy dynamic(在架構中說明),而非將一組固定的來源檔案指定給每個映射工作,而是將檔案分割成多組。組數會超過映射數目,通常是 2-3 倍。每個映射會選取並複製區塊中列出的所有檔案。當區塊用盡時,會取得並處理新的區塊,直到沒有更多區塊為止。

不將來源路徑指定給固定映射,速度較快的映射工作(即資料節點)就能夠使用更多區塊,因此比速度較慢的節點複製更多資料。雖然這種分配並不均勻,但考量到每個映射器的容量,這種分配是公平的。

動態策略是由 DynamicInputFormat 實作。在大部分情況下,它都能提供優異的效能。

建議針對來源和目的地叢集的大小、複製的大小,以及可用的頻寬調整映射數目,以執行長時間執行和定期執行的作業。

在 HDFS 版本之間複製

要在兩個不同主要版本的 Hadoop 之間複製(例如,在 1.X 和 2.X 之間),通常會使用 WebHdfsFileSystem。與先前的 HftpFileSystem 不同,由於 webhdfs 可用於讀取和寫入作業,因此可以在來源和目的地叢集上執行 DistCp。遠端叢集指定為 webhdfs://<namenode_hostname>:<http_port>。在 Hadoop 叢集的相同主要版本之間複製時(例如,在 2.X 和 2.X 之間),請使用 hdfs 協定以獲得更好的效能。

透過 distcp 安全複製

當 webhdfs 使用 SSL 保護時,請使用「swebhdfs://」架構。如需更多資訊,請參閱 SWebHDFS 的 SSL 組態

MapReduce 和其他副作用

如前所述,如果映射無法複製其輸入之一,將會產生多個副作用。

  • 除非指定 -overwrite,否則在重新執行時,先前映射成功複製的檔案將標記為「已略過」。
  • 如果映射失敗 mapreduce.map.maxattempts 次,將會終止其餘的映射工作(除非設定 -i)。
  • 如果設定 mapreduce.map.speculative 為最終和 true,則複製結果未定義。

DistCp 和物件儲存

DistCp 可與物件儲存(例如 Amazon S3、Azure ABFS 和 Google GCS)搭配使用。

先決條件

  1. 包含物件儲存實作的 JAR 位於類別路徑上,以及其所有相依性。
  2. 除非 JAR 自動註冊其綑綁的檔案系統用戶端,否則可能需要修改組態以陳述實作檔案系統架構的類別。ASF 自有的所有物件儲存用戶端都是自註冊的。
  3. 相關的物件儲存存取憑證必須在叢集組態中提供,或在所有叢集主機中提供。

DistCp 可用於上傳資料

hadoop distcp -direct hdfs://nn1:8020/datasets/set1 s3a://bucket/datasets/set1

下載資料

hadoop distcp s3a://bucket/generated/results hdfs://nn1:8020/results

在物件儲存之間複製資料

hadoop distcp s3a://bucket/generated/results \
  wasb://updates@example.blob.core.windows.net

以及在物件儲存內複製資料

hadoop distcp wasb://updates@example.blob.core.windows.net/current \
  wasb://updates@example.blob.core.windows.net/old

並使用 -update 僅複製已變更的檔案。

hadoop distcp -update -numListstatusThreads 20  \
  s3a://history/2016 \
  hdfs://nn1:8020/history/2016

由於物件儲存列出檔案的速度較慢,因此請考慮在對大型目錄樹執行 -update 作業時設定 -numListstatusThreads 選項(限制為 40 個執行緒)。

DistCp -update 與物件儲存體搭配使用時,一般而言只會比較個別檔案的修改時間與長度,不會比較任何檢查碼,如果兩個儲存體之間的檢查碼演算法不同。

  • 具有不同檢查碼演算法的兩個物件儲存體之間的 distcp -update 會比較來源和目標檔案的修改時間以及檔案大小,以判斷是否略過檔案複製。此行為由屬性 distcp.update.modification.time 控制,預設設為 true。如果來源檔案比目標檔案最近修改,則假設內容已變更,且應更新檔案。我們需要確保機器之間沒有時鐘偏移。大多數物件儲存體確實具有目錄的有效時間戳記,這點無關緊要;只會比較檔案時間戳記。不過,重要的是讓用戶端電腦的時鐘接近基礎架構的時鐘,以便用戶端/HDFS 集群與物件儲存體之間的時間戳記一致。否則,變更的檔案可能會被遺漏/過於頻繁地複製。

  • distcp.update.modification.time 僅會在兩個儲存體都沒有檢查碼驗證,導致兩個儲存體之間的檢查碼比較不相容時使用。即使將屬性設為 true,如果兩個儲存體之間有有效的檢查碼比較,也不會使用它。

若要關閉修改時間檢查,請在 core-site.xml 中設定這項設定

<property>
        <name>distcp.update.modification.time</name>
        <value>false</value>
</property>

備註

  • -atomic 選項會導致暫時資料重新命名,因此會大幅增加作業結束時提交工作的時間。此外,由於除了 (選擇性的) wasb:// 以外的物件儲存體不提供目錄的原子重新命名,因此 -atomic 作業實際上並未提供所承諾的內容。避免使用。

  • 不支援 -append 選項。

  • 不支援 -diffrdiff 選項

  • 無論 -skipCrc 標記的值為何,都不會執行 CRC 檢查。

  • 一般而言,所有 -p 選項(包括保留權限、使用者和群組資訊、屬性檢查碼和複製)都會被忽略。wasb:// 連接器會保留資訊,但不會強制執行權限。

  • 有些物件儲存體連接器提供輸出記憶體內暫存的選項,例如 S3A 連接器。在複製大型檔案時使用此選項可能會觸發某種形式的記憶體不足事件,可能是堆疊溢位或 YARN 容器終止。如果叢集和物件儲存體之間的網路頻寬有限(例如在使用遠端物件儲存體時),這種情況特別常見。最好停用/避免此類選項,並依賴磁碟暫存。

  • 單一物件儲存體內的複製作業仍會在 Hadoop 群集中進行,即使物件儲存體內部實作更有效率的 COPY 作業

    也就是說,例如以下作業

    hadoop distcp s3a://bucket/datasets/set1 s3a://bucket/datasets/set2

    會將每個位元組複製到 Hadoop 工作節點,然後再複製回儲存區。除了速度慢之外,這表示可能會產生費用。

  • 可以使用 -direct 選項直接寫入物件儲存體目標路徑,避免可能非常耗時的暫時性檔案重新命名作業,否則會發生此類作業。

常見問題

  1. 為什麼 -update 沒有在預先存在的目標目錄下建立父來源目錄? -update-overwrite 的行為已在本文件的「使用」區段中詳細說明。簡而言之,如果將任一選項與預先存在的目標目錄搭配使用,則會複製每個來源目錄的內容,而不是來源目錄本身。此行為也與舊版 DistCp 實作一致。

  2. 新的 DistCp 在語意上與舊版 DistCp 有何不同?

    • 在使用舊版 DistCp 複製時,複製期間略過的檔案其檔案屬性(權限、擁有者/群組資訊等)也會保持不變。現在,即使略過檔案複製,這些屬性也會更新。
    • 在舊版 DistCp 中,來源路徑輸入中的空根目錄並未建立在目標中。現在已建立這些目錄。
  3. 為什麼新的 DistCp 使用的對應比舊版 DistCp 多? 舊版 DistCp 的運作方式是,在啟動複製工作之前找出實際需要複製到目標的檔案,然後啟動複製所需的對應數量。因此,如果需要略過大部分檔案(例如,因為它們已存在),則需要的對應數量會較少。因此,設定時間(即在 M/R 工作之前)會較長。新的 DistCp 只會計算來源路徑的內容。它不會嘗試篩選出可以略過的檔案。此決定會延後到執行 M/R 工作時才做。這樣會快很多(相對於執行時間),但啟動的對應數量會如 -m 選項中指定,或在未指定時為 20(預設值)。

  4. 為什麼在指定更多對應時,DistCp 沒有執行得更快? 目前,DistCp 的最小工作單位是檔案。也就是說,只會有一個對應處理一個檔案。將對應數量增加到超過檔案數量,並不會帶來效能上的好處。啟動的對應數量會等於檔案數量。

  5. 為什麼 DistCp 會用完記憶體?如果從來源路徑複製的個別檔案/目錄數量極為龐大(例如 1,000,000 個路徑),DistCp 在決定要複製的路徑清單時可能會用完記憶體。這並非新 DistCp 實作獨有的問題。為了解決此問題,請考慮變更 -Xmx JVM 堆積大小參數,如下所示

     bash$ export HADOOP_CLIENT_OPTS="-Xms64m -Xmx1024m"
     bash$ hadoop distcp /source /target