大量小檔案傳輸實測:rsync、tar、SFTP、TCP 與 QUIC 比較
從德國到美國,在 141 ms 往返延遲下傳送 65,536 個小檔案。實測 rsync、tar、Btrfs 快照、完整磁碟映像,以及 TCP/QUIC 與不同壅塞控制設定。
我想知道,一個裝滿小檔案的目錄,複製起來到底慢在哪裡。我測了 rsync、串流 tar、快照複寫與完整磁碟映像複製,再用一個簡單的 Rust 傳輸程式調整傳輸設定。
資料是 65,536 個檔案,每個 4,096 bytes,合計 268.4 MB。從德國傳到美國,往返延遲約 141 ms RTT。兩台 VM 都有 4 個 vCPU 與 8 GB RAM。所有方法都使用 Btrfs loop volume,事先讀取來源資料,接收端從空白開始,不啟用壓縮。每種設定以打亂的順序測五次。速度以有效檔案位元組數,除以接收端檔案可用為止的時間計算;之後才做 sync 與 SHA-256 驗證。所有完成的複製都通過驗證。
先看現成工具,每種使用一條資料連線。完成傳輸的速度取五次測試的中位數:
| 方法 | 傳輸速度(MB/s) |
|---|---|
| OpenSSH SFTP | <0.299* |
rsync(--whole-file) | 10.65 |
| tar 串流經由 SSH 傳送 | 10.05 |
| Btrfs 完整快照傳送 | 10.84 |
| 完整原始磁碟映像複製 | 1.71† |
SFTP 五次都達到 15 分鐘的時間上限;表中的數字是完成整批傳輸的速度上界。†原始映像實際傳送的資料量是有效檔案資料的八倍。工具的全部觀測結果與計時細節(英文)。
在這個 RTT 下,每個檔案若多等一次網路往返,合計就會多出 2.6 小時。OpenSSH 9.6 的遞迴 SFTP 客戶端依序處理各個檔案,流水線只用在單一檔案內部。Tar 把封裝串流透過 SSH 送給另一個 tar 程式,接收端邊收邊解開。Rsync 本來就有跨檔流水線。打包與流水線都能讓後面的檔案在前面的檔案完成前開始傳送。不過,這組現成工具的測量也包含其他實作差異。
Btrfs 完整 send 以串流傳送檔案資料與檔案系統操作。原始映像複製則搬動整個 2.15 GB 映像,連未使用空間也一起傳,只為交付 268.4 MB 的檔案。增量快照與變更區塊複寫需要用資料更新的情境測試;這裡只測完整複製。
另一批測試使用 Rust 程式,在 Linux TCP/TLS/yamux 與 Quinn QUIC 之間共用檔案 I/O 程式碼。一種模式允許最多 4,096 條檔案串流同時進行,另一種把紀錄打包成一條連續串流。兩者都能跨越檔案邊界持續傳送,不必等每個檔案完成。
單一連線的傳輸速度(MB/s):
| 檔案處理方式 | TCP + CUBIC | TCP + BBR | QUIC + CUBIC | QUIC + BBR |
|---|---|---|---|---|
| 跨檔流水線 | 16.99 | 20.04 | 6.58 | 28.41 |
| 串流打包 | 20.90 | 19.09 | 6.91 | 29.26 |
五次測試的中位數。
開啟原尺寸圖表把 CUBIC 換成 BBR,打包的 QUIC 從 6.91 升到 29.26 MB/s。同樣的變動在 TCP 上,卻是從 20.90 變成 19.09 MB/s。更換壅塞控制器後,TCP 與 QUIC 的快慢排名反轉了;Linux 與 Quinn 使用的是各自的控制器實作。
改用八條獨立的 BBR 連線後,打包小檔的速度達到 TCP 38.04 MB/s、QUIC 36.83 MB/s。這也同時增加了讀取與寫入的並行度。大檔案仍然快得多:
開啟原尺寸圖表較早的工具測試,曾因另一個工作負載的 SSH 失敗而中斷,之後接續完成;80 次 Rust 測試則全程沒有中斷。這兩台共用 VM 出現過 UDP 緩衝區錯誤,以及偶發的 CPU steal,也就是虛擬機等待宿主機分配 CPU 的時間。兩批測試的實作與計時範圍也不同,不能把它們當成只改變傳輸協定的比較。設定與稽核細節(英文)。
我會先檢查,傳送端是否會在檔案邊界停下來等待,再測壅塞控制器。Tar、rsync 的流水線與自訂跨檔流水線,都在解決第一個問題。在這次 Quinn 的傳輸路徑上,跨檔連續傳送加上 BBR 的效果很好。TCP 沒有得到相同的 BBR 效益。光看協定名稱或複寫所在的層級,無法知道這兩件事是否做好了。若要逐步診斷自己的資料夾,請見大量小檔案傳輸為什麼很慢?原因與加速方法。
我參與 WarpSend 的開發,所以很在意這些細節。這個實驗沒有使用 WarpSend 的產品程式碼。