通過優化 S3 讀取來提高效率和減少執行時間

語言: CN / TW / HK

概述

本文將介紹一種提升 S3 讀取吞吐量的新方法,我們使用這種方法提高了生產作業的效率。結果非常令人鼓舞。單獨的基準測試顯示,S3 讀取吞吐量提高了 12 倍(從 21MB/s 提高到 269MB/s)。吞吐量提高可以縮短生產作業的執行時間。這樣一來,我們的 vcore-hours 減少了 22%,memory-hours 減少了 23%,典型生產作業的執行時間也有類似的下降。

雖然我們對結果很滿意,但我們將來還會繼續探索其他的改進方式。文末會有一個簡短的說明。

動機

我們每天要處理儲存在 Amazon S3 上的數以 PB 計的資料。如果我們看下 MapReduce/Cascading/Scalding 作業的相關指標就很容易發現:mapper 速度遠低於預期。在大多數情況下,我們觀測到的 mapper 速度大約是 5-7MB/s。這樣的速度要比 aws s3 cp 這類命令的吞吐量慢幾個數量級,後者的速度達到 200+MB/s 都很常見(在 EC2 c5.4xlarge 例項上的觀測結果)。如果我們可以提高作業讀取資料的速度,那麼作業就可以更快的完成,為我們節省相當多的處理時間和金錢。鑑於處理成本很高,節省的時間和金錢可以迅速增加到一個可觀的數量。

S3 讀取優化

問題:S3A 吞吐量瓶頸

如果我們看下 S3AInputStream 的實現,很容易就可以看出,以下幾個方面可以做些改進:

  1. 單執行緒讀:資料是在單執行緒中同步讀取的,導致作業把大量時間花在通過網路讀取資料上。

  2. 多次非必要重新開啟:S3 輸入流是不可定址的。每次執行定址或是遇到讀取錯誤時,總是要重複開啟“分割(split)”。分割越大,出現這種情況的可能性越高。每次重新開啟都會進一步降低總體的吞吐量。

解決方案:提高讀取吞吐量

架構

圖 1:S3 讀取器的預取+快取元件

為了解決上述問題,我們採取了以下措施:

  1. 我們將分割視為是由固定大小的塊組成的。預設大小是 8MB,但可配置。

  2. 每個塊在非同步讀取到記憶體後,呼叫者才能訪問。預取快取的大小(塊的數量)是可配置的。

  3. 呼叫者只能讀取已經預取到記憶體中的塊。這樣客戶端可以免受網路異常的影響,而我們也可以有一個額外的重試層來增加整體彈性。

  4. 每當遇到在當前塊之外定址的情況時,我們會在本地檔案系統中快取預取的塊。

我們進一步增強了這個實現,讓生產者-消費者互動幾乎不會出現鎖。根據一項單獨的基準測試(詳情見圖 2),這項增強將讀吞吐量從 20MB/s 提高到了 269MB/s。

順序讀

任何按照順序處理資料的消費者(如 mapper)都可以從這個方法中獲得很大的好處。雖然 mapper 處理的是當前檢索出來的資料,但序列中接下來的資料已經非同步預取。在大多數情況下,在 mapper 準備好處理下一個資料塊時,資料就已經預取完成。這樣一來,mapper 就把更多的時間花在了有用的工作上,等待的時間減少了,CPU 利用率因此增加了。

Parquet 檔案讀取更高效

Parquet 檔案需要非順序讀取,這是由它們的磁碟格式決定的。我們最初實現的時候沒有使用本地快取。 每當遇到在當前塊之外定址的情況時,我們就得拋棄預取的資料。在讀取 Parquet 檔案時,這比通常的讀取器效能還要差。

在引入預取資料的本地快取後,我們發現 Parquet 檔案讀取吞吐量有明顯的提升。目前,與通常的讀取器相比,我們的實現將 Parquet 檔案讀取吞吐量提升了 5 倍。

改進生產作業

讀取吞吐量的增加給生產作業帶來了多方面的提升。

降低了作業執行時間

作業的總體執行時間減少了,因為 mapper 等待資料的時間減少了,可以更快地完成。

減少 mapper 數量

如果 mapper 耗時大大減少,那麼我們就可以通過增加分割大小來減少 mapper 數量。Mapper 數量的減少可以減少由固定 mapper 開銷所導致的 CPU 浪費。更重要的是,這樣做並不會增加作業的執行時間。

提高 CPU 利用率

由於 mapper 完成同樣的工作所花費的時間減少,所以 CPU 整體的利用率會提高。

結果

現在,我們的實現(S3E)使用了一個單獨的儲存庫,提高了我們的迭代改進速度。最終,我們會將其合併到 S3A,把它回饋給社群。

單獨的基準測試

圖 2:S3A 和 S3E 的吞吐量對比

在每種情況下,我們都是順序讀取一個 3.5GB 的 S3 檔案,並將其寫入本地的一個臨時檔案。後半部分是為了模擬 mapper 操作期間發生的 IO 重疊。基準測試是在 EC2 c5.9xlarge 例項上進行的。我們測量了讀取檔案的總時間,並計算每種方法的有效吞吐量。

生產執行

我們在許多大型生產作業中測試了 S3E 實現。這些作業每次執行時通常都要使用數以萬計的 vcore。圖 3 是對比了啟用 S3E 和不啟用 S3E 時獲得的指標。

度量資源節省情況

我們使用以下方法度量這項優化所帶來的資源節省情況。

觀測到的結果

圖 3:MapReduce 作業資源消耗對比

雖然不同的生產作業工作負載有不同的特徵,但我們看到,在 30 個成本高昂的作業中,大部分的 vcore 都減少了 6%到 45%。

我們的方法有一個吸引人的地方,就是在一個作業中啟用時不需要對作業的程式碼做任何修改。

未來展望

目前,我們把這個增強實現放在了一個單獨的 Git 儲存庫中。將來,我們可能會升級已有的 S3A 實現,並把它回饋給社群。

我們正在把這項優化推廣到我們的多個叢集中,結果將發表在以後的博文上。

鑑於 S3E 輸入流的核心實現不依賴於任何 Hadoop 程式碼,我們可以在其他任何需要大量訪問 S3 資料的系統中使用它。目前,我們把這項優化用在 MapReduce、Cascading 和 Scalding 作業中。不過,經過初步評估,將其應用於 Spark 和 Spark SQL 的結果也非常令人鼓舞。

當前的實現可以通過進一步優化來提高效率。同樣值得探索的是,是否可以使用過去的執行資料來優化每個作業的塊大小和預取快取大小。

檢視英文原文: Improving efficiency and reducing runtime using S3 read optimization