題目與範圍
Python 3.14 將 Zstandard 支援加入標準函式庫 compression.zstd,提供一次性函式、open()、ZstdFile 以及增量壓縮和解壓類別。題目考察 API 語意、串流邊界、資源上限和漸進相容,分類為 coding。標準函式庫可用不代表所有部署都已升級,也不代表 zstd 對每類資料都更快或更省空間。
面試官考察點
應區分一次性壓縮與增量壓縮,說明 FLUSHBLOCK 與 FLUSHFRAME 的邊界,處理截斷、未知輸入和解壓炸彈風險。還要討論壓縮等級、字典、執行緒模型、舊 Python 回退,以及用吞吐、壓縮比、延遲和峰值記憶體做基準。
先釐清的問題
- 資料是完整檔案、分塊日誌,還是必須邊寫邊發的網路串流?
- 接收端是否支援 zstd,能否統一升級到 Python 3.14?
- 目標是吞吐、壓縮比、尾端延遲,還是峰值記憶體中的哪一項?
- 單一 frame 的最大大小和失敗後的重試邊界是什麼?
- 輸入是否可能來自不可信使用者,解壓端如何限制資源?
- 是否需要跨語言互通、字典協商或可稽核的壓縮參數?
30 秒答題框架
「先確認協定、資料區塊和部署版本,再選擇一次性或串流 API。對大輸入使用 ZstdCompressor,按區塊寫入並在訊息結束時刷新 frame;解壓端限制輸出大小和並發。用同一資料集比較 zstd 與現有 gzip/外部函式庫的壓縮比、吞吐、p95、CPU 和 RSS,在舊版本用可驗證的回退實作,並保留格式版本與參數記錄。」
分步作答
步驟 1:選擇 API 與邊界
完整且受控的小物件可用 compress();檔案介面可用 open() 或 ZstdFile。長連線和大型日誌使用增量物件,把每條訊息或批次映射到明確的 frame 邊界,避免把多個租戶資料拼進不可拆分的 frame。
步驟 2:設計串流刷新
增量壓縮時,compress() 產生中間輸出;flush(FLUSHBLOCK) 結束一個區塊但保持 frame,flush(FLUSHFRAME) 結束目前 frame。只有在協定允許接收端解碼時才刷新,不要為了每個小片段都結束 frame 而製造額外開銷。
from compression import zstd
cctx = zstd.ZstdCompressor(level=3)
out = cctx.compress(chunk)
tail = cctx.flush(zstd.ZstdCompressor.FLUSH_FRAME)步驟 3:控制解壓風險
高壓縮比輸入可能在解壓後佔用遠大於網路大小的記憶體。為不可信輸入設定最大輸出位元組數、單請求並發和逾時;校驗 frame 完整性,區分格式錯誤、截斷和業務取消。不要把壓縮大小當作記憶體預算。
步驟 4:處理相容與回退
啟動時探測 compression.zstd 是否存在,並把編碼協商寫進協定。Python 3.13 或更早版本需要明確鎖定的 backport 或外部函式庫,不能靜默改變格式。跨語言系統應以 zstd 標準 frame 互測,而不是只驗證 Python 對 Python。
步驟 5:用基準決定上線
固定資料集、區塊大小、壓縮等級和並發度,測量壓縮比、端到端吞吐、CPU、p50/p95、RSS、配置次數及 frame 數量。分別測試文字、重複日誌、隨機資料和小 payload;小區塊可能被呼叫開銷和 frame 標頭淹沒。灰度期間保留舊編碼回退與可觀測參數。
參考答案
「compression.zstd 適合 Python 3.14 服務中需要標準函式庫 zstd、串流邊界清楚且能控制資源的場景。我會用一次性 API 處理受控小物件,用增量壓縮處理大流,並在協定層定義 block、frame、最大輸出和取消行為。解壓端限制展開大小和並發,啟動時協商能力,舊版本使用明確回退。最後用固定資料集測壓縮比、吞吐、尾端延遲、CPU 與 RSS,再根據實測和跨語言互通結果灰度。」
常見錯誤
- 把
flush()當作關閉物件 → 可能只完成 block 或 frame → 明確選擇刷新模式並結束生命週期。 - 按網路位元組數限制解壓 → 高壓縮比輸入會耗盡記憶體 → 按展開後位元組數和並發限制。
- 每條日誌都新建 compressor → 初始化與 frame 開銷放大 → 按批次重用增量物件。
- 預設所有執行環境都有 3.14 API → 舊部署啟動失敗 → 啟動探測並固定回退版本。
- 只測隨機資料 → 看不到重複日誌的真實收益 → 覆蓋多種資料分布和 payload 大小。
- 只驗證 Python 對 Python → 跨語言 frame 或參數不相容 → 加入其他 zstd 實作的互測。
追問
追問 1:什麼時候使用一次性 compress()?
輸入完整、大小受控且無需中途傳送時可以使用。大型輸入或需要背壓時應採用增量 API,避免一次性建構完整壓縮結果。
追問 2:為什麼區分 block 和 frame?
block 是 frame 內的刷新邊界,frame 是可獨立解碼的壓縮單元。協定若要求訊息獨立解壓,應在訊息結束時刷新 frame,而不是只刷新 block。
追問 3:如何讓舊版本繼續工作?
在啟動檢查模組能力,協商編碼;鎖定並測試 backport 或外部函式庫,確保產生相同 zstd frame 語意,並在日誌中記錄實際編碼路徑。
追問 4:基準中最容易遺漏什麼?
常被遺漏的是解壓端 RSS、p95、frame 數量、壓縮等級變化和小 payload 的固定開銷。應把這些指標與吞吐、壓縮比一起比較。