一條 128 位元組的對齊線先被畫好了:BqLog 的環形佇列,是一套被排好版的記憶體設計
從王者榮耀日誌元件 BqLog 的環形佇列與自適應數據總線設計看起,觀察一套高效能日誌系統如何在佇列布局與執行緒分工之間完成它的工程美學。
(本文回顧的原始文章發布於 2026 年 9 月 28 日,掘金平臺上王者榮耀團隊工程師所寫的 BqLog 系列第二篇。)
先從一個具體的數字看起:128。BqLog 在程式碼裡定義了一個對齊常量 BQ_CACHE_LINE_SIZE,把這個值當作記憶體布局的基本單位。這個數字本身值得細看,因為它並非硬體事實,作者特別說明,並非所有 CPU 的快取行都是 128 位元組。它是一個選擇出來的、偏保守的布局單位,目的是讓不同執行緒碰觸的資料在實體記憶體上被隔開,避免彼此的寫入互相幹擾。
一個日誌系統的設計,從這條看不見的對齊線開始。
效能數字先攤在桌上
文章給出了一組基準測試數據,機器是 AMD Ryzen 9 9950X,每個執行緒寫入 200 萬條日誌,每條帶 4 個格式化參數。在 10 個執行緒的情境下,BqLog 壓縮模式總耗時 507 毫秒;同一條件下,Log4j2 是它的約 14 倍時間,非同步 spdlog 約 65 倍,fmtlog 與 quill 也分別落後約 13.7 倍與 15.4 倍。glog 的差距更大,達到百倍等級。
另一個值得注意的細節是加密的成本。壓縮加密模式在 10 執行緒時耗時 493 毫秒,與純壓縮的 507 毫秒幾乎持平,甚至略快。加密這個通常被視為昂貴的操作,在這套設計裡被吸收到幾乎不可見。這份數據出自開發者自己公布的基準,配置細節列在專案的 BENCHMARK_CHS.md,讀者可以自行檢驗;但差距的量級大到即使打折扣,結論也不太會翻盤。
不可能三角,與一條被重新設計的通道
王者榮耀團隊面對的問題,作者用一個三角來描述:效能要好、問題要能追溯、儲存要省。國內發行的手遊通常保住一和三,放棄二,日誌能少寫就少寫。但國際版 HOK 面對全球玩家,時差、語言與隱私觀念都讓工程師難以直接找用戶重現問題,於是日誌必須「應寫盡寫」,同時又不能拖累幀率,也最好不要佔太多空間。
系列第二篇處理的,是日誌從幾十個業務執行緒交到單一後端執行緒手上這一段路。這條通道看似單純,實際上每個環節都是設計決策:多生產者同時寫入時,共享佇列的寫遊標會成為熱點;記錄長度不定,佇列得處理變長資料;執行緒會動態增刪;程式崩潰後緩衝區裡的日誌還得救得回來。
BqLog 的答案稱為「自適應數據總線」:低頻執行緒共用一條佇列,高頻執行緒則各自分配專用佇列。這個分流的邏輯,跟一般日誌庫把所有生產者塞進同一條 MPSC 佇列的做法正好相反。熱點被拆散了,競爭只留在真正低頻的地方。專案用 MISO 與 SISO 標記這兩種通道(即通稱的 MPSC 與 SPSC),命名本身也透露出這套系統對通道類型的分類學:資料流動的形狀,被當成設計的一級公民來對待。
這種「按使用頻率分配通道」的思路,與我們在花少8 一分鐘完成選房的流程設計裡看到的邏輯有點像:最會起衝突的地方,用重新分配的結構讓衝突根本發生不了。
設計在約束裡成形
回頭看這篇文章的美學。工程文章的好壞,往往取決於作者是否把約束講清楚。這篇的約束講得很具體:快取行不是 128 位元組卻選 128、加密成本幾乎歸零、測試時每加一個執行緒就多 200 萬條日誌所以比較吞吐要用總量除以耗時。這些註腳式的細節,讓數據可以被質疑,也因此可以被信任。
BqLog 是騰訊開源在 GitHub 上的專案,橫跨客戶端與伺服器、多種語言與作業系統。它的第一篇講壓縮檔案格式,第二篇講這條記憶體通道,第三篇講執行路徑。系列結構本身也是一種排版:把一條日誌從產生到落盤的旅程,切成一段一段各自獨立卻先後銜接的設計課。
一條看不見的佇列,被 128 位元組的對齊線、被按頻率分流的通道、被清楚標註的基準數字,排成了一份可讀的設計文件。效能在這裡不只是跑分,它是一組可以被閱讀的布局決策。