Redis 新增 Array 資料型別:稀疏索引、正規表示式與四個月的設計演進
antirez.com · 2026-05-05
Redis 作者 Salvatore Sanfilippo(antirez)在近期文章中詳述了一個歷經四個月開發的新資料型別:Array。這個型別以數字索引為鍵,支援稀疏分布,並提供掃描、彈出與正規表示式搜尋等原生操作,填補了 Redis 長期以來缺乏真正整數索引集合的空缺。
設計演進:稀疏表示的難題
初版設計採用兩層目錄結構處理稀疏索引,但實際測試後發現這個結構在極大索引跨距(index gap)的情境下記憶體效率不足。最終方案演化為「超級目錄(super directory)加上切片式密集目錄(sliced dense directories)」的三層架構:每個切片包含 4096 個元素槽,實際有資料的切片才被分配記憶體。這讓陣列在邏輯上是連續的整數索引空間,但記憶體用量與實際元素數量成正比,而非與索引範圍成正比。
ARSET myarray 293842948324 "hello" ; 設定索引 293842948324 的值
ARGET myarray 293842948324 ; 讀取
ARSCAN myarray 0 COUNT 10 ; 從索引 0 開始掃描 10 個元素
ARPOP myarray 5 ; 彈出最小的 5 個索引元素ARSCAN 與 ARPOP 的執行時間與實際元素數量成正比,而非與索引跨距成正比,這是稀疏設計的核心要求。
ARGREP:內建正規表示式搜尋
ARGREP 命令整合正規表示式搜尋功能,引擎選用 TRE 函式庫(而非 PCRE),原因是 TRE 對病態輸入(pathological patterns)的效能行為更可預測,避免了 ReDoS 類型的攻擊面。作者特別針對常見的交替模式(如 `foo|bar|zap`)實作了優化路徑,減少不必要的回溯。
開發過程中 antirez 大量使用 AI 工具進行設計討論與逐行程式碼審查。他描述 AI 扮演「安全網」角色,讓他得以在處理 32-bit 相容性等繁瑣細節的同時,保持對整體演算法正確性的注意力。最終透過大規模自動化測試驗證實作,而非僅依賴肉眼審查。
影響範圍
Redis Array 型別填補了以下場景的需求:
- 事件日誌以時間戳記(大整數)為索引的稀疏序列
- 需要原子性 pop 操作的優先佇列
- 跨大索引範圍的高效掃描,不需要預先知道所有鍵值
- 結合正規表示式的值搜尋(ARGREP)
原始來源:antirez.com
Async Rust 從未離開 MVP 狀態:二進位大小膨脹的根源分析
tweedegolf.nl · 2026-05-05
Tweede golf 工程師在這篇技術文章中,系統性地剖析了 async Rust 在嵌入式系統上二進位大小膨脹的五個根本原因。作者的結論是:async Rust 從未真正兌現「零成本抽象」的承諾,在資源受限的裝置上,每個位元組都至關重要,而編譯器產生的中間表示讓 LLVM 難以有效優化。
問題一:不必要的狀態機生成
即使是從不 await 的簡單 async 區塊,編譯器也會為其生成完整的狀態機。以 `async { 5 }` 為例,對應的 MIR(Mid-level Intermediate Representation)高達 360 行,而等效的同步版本僅需 23 行。這種膨脹在小型嵌入式系統上會顯著增加 flash 佔用。
問題二:Panic 式控制流
當一個 Future 完成後,後續對它的 `poll()` 呼叫會觸發 panic,而非安全地回傳 `Poll::Pending`。這在最佳化路徑中引入了一條帶有副作用的分支,LLVM 無法輕易消除它。作者測試了修改這個行為後的效果,實現了 2%–5% 的二進位大小節省。
問題三–五:LLVM 優化失敗
作者透過 Compiler Explorer 展示了三類 LLVM 優化缺口:
- 只有單一 await 點的 Future 未被 inline,產生不必要的巢狀狀態機
- 不同程式碼路徑中的相同 Future 生成了重複狀態,應可合併
- 整體中間表示品質使 LLVM 在較低優化等級下無法有效壓縮
; 示意:poll() 結束後的 panic 路徑
; 這條 panic 分支使 LLVM 無法消除死碼
bb_after_complete:
call core::panicking::panic("polled after completion")作者明確指出:「LLVM 不是我們的救星,我們必須給它良好的輸入。」這意味著修復需要在 Rust 編譯器前端層面改善狀態機生成邏輯,而非期待後端優化器補救。目前提出的各項修補合計可節省 0.2%–3% 的二進位大小,尚不足以從根本解決問題,但指出了具體的改進方向。
原始來源:tweedegolf.nl
微核心 IPC 設計:FTL 的拉取式架構與 Plan 9 式極簡通訊
seiya.me · 2026-05-05
seiya 在這篇文章中詳述了為 FTL 微核心設計行程間通訊(IPC)機制的過程,最終選擇了非同步訊息傳遞加上拉取式(pull-based)架構,並刻意捨棄 IDL(Interface Definition Language),改以五種預定義請求型別構成整個通訊協定。
同步 vs 非同步的取捨
同步 IPC 的優點是不需要動態記憶體分配,也不易產生拒絕服務漏洞;缺點是伺服器對伺服器通訊容易形成死鎖,需要另外引入「通知」機制打破循環。FTL 最終選擇非同步訊息傳遞,以吞吐量換取同步語意的直觀性。
拉取式架構與背壓
FTL 採用消費者主動拉取(consumer pull)而非生產者推送(producer push)。以網路驅動為例,封包到達時驅動程式不主動推送給上層協議棧,而是等待上層準備好時發起請求。這讓背壓(backpressure)自然出現:當消費者的內部緩衝區滿時,它只需停止發出拉取請求,不需要額外的流量控制協定。
五種請求型別取代 IDL
FTL 的整個 IPC 協定只有五種請求:`open`、`read`、`write`、`getattr`、`setattr`。作者注意到這個設計無意間與 Plan 9 的 9P 協定高度相似——兩者都以「萬物皆檔案」的文件介面統一資源存取,避免了 gRPC、Cap'n Proto 等 IDL 方案帶來的序列化開銷與抽象層複雜度。
; 核心操作集合(FTL IPC)
open fd, path, flags
read fd, offset, len → data
write fd, offset, data → n_written
getattr fd → attrs
setattr fd, attrsPeek-then-receive 模式允許接收方在實際消費訊息前先查看標頭,從而直接把資料複製到目標緩衝區,省去中間緩衝的記憶體拷貝。這個模式同時也自然傳播背壓:若接收方緩衝區已滿,peek 後不 receive,生產方自動阻塞。FTL 的 IPC 設計示範了在微核心環境中,以明確語意的極簡協定取代複雜抽象框架的可行路徑。
原始來源:seiya.me