golang-samber-hot

golang-samber-hot

熱門

使用 Golang 的 samber/hot 進行記憶體快取 — 支援淘汰演算法 (LRU、LFU、TinyLFU、W-TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO)、TTL、快取載入器、分片、stale-while-revalidate、遺失鍵快取以及 Prometheus 指標。適用於正在使用或採用 samber/hot 的情況,當程式碼庫匯入 github.com/samber/hot,或專案頻繁載入相同的中低基數資源且需要降低延遲或後端壓力時。

2261星標
150分支
更新於 2026/6/6
SKILL.md
readonlyread-only
name
golang-samber-hot
description

使用 Golang 的 samber/hot 進行記憶體快取 — 支援淘汰演算法 (LRU、LFU、TinyLFU、W-TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO)、TTL、快取載入器、分片、stale-while-revalidate、遺失鍵快取以及 Prometheus 指標。適用於正在使用或採用 samber/hot 的情況,當程式碼庫匯入 github.com/samber/hot,或專案頻繁載入相同的中低基數資源且需要降低延遲或後端壓力時。

角色定位: 你是一位將快取視為系統設計決策的 Go 工程師。你根據測量到的存取模式選擇淘汰演算法,根據工作集資料調整快取大小,並始終規劃過期、載入器失敗和監控事宜。

在 Go 中使用 samber/hot 進行記憶體快取

泛型、型別安全的 Go 記憶體快取函式庫,支援 Go 1.22+,提供 9 種淘汰演算法、TTL、具有 singleflight 去重功能的載入器鏈、分片、stale-while-revalidate 以及 Prometheus 指標。

官方資源:

此技能並非詳盡無遺。請參閱函式庫文件和程式碼範例以獲取更多資訊。Context7 可作為探索平台提供協助。如需 Go 套件文件、版本、符號和已知漏洞,→ 請參閱 samber/cc-skills-golang@golang-pkg-go-dev 技能。

go get -u github.com/samber/hot

演算法選擇

根據你的存取模式選擇 — 錯誤的演算法會浪費記憶體或降低命中率。

演算法 常數 最佳適用場景 應避免的情況
W-TinyLFU hot.WTinyLFU 通用、混合工作負載(預設) 你需要簡單性以便除錯
LRU hot.LRU 近期主導(工作階段、近期查詢) 頻率很重要(掃描污染會逐出熱門項目)
LFU hot.LFU 頻率主導(熱門商品、DNS) 存取模式會變動(過時的熱門項目永遠不會被逐出)
TinyLFU hot.TinyLFU 讀取密集型且偏重頻率 寫入密集型(准入過濾器開銷)
S3FIFO hot.S3FIFO 高吞吐量、抗掃描 小型快取(<1000 個項目)
ARC hot.ARC 自我調整、未知模式 記憶體受限(2 倍追蹤開銷)
TwoQueue hot.TwoQueue 混合熱/冷分離 無法接受調整複雜度
SIEVE hot.SIEVE 簡單的抗掃描 LRU 替代方案 高度傾斜的存取模式
FIFO hot.FIFO 簡單、可預測的逐出順序 命中率很重要(無頻率/近期感知)

決策捷徑:hot.WTinyLFU 開始。僅在效能分析顯示未命中率過高而無法滿足 SLO 時才切換。

如需詳細的演算法比較、基準測試和決策樹,請參閱演算法指南

核心用法

基本快取與 TTL

import "github.com/samber/hot"

cache := hot.NewHotCache[string, *User](hot.WTinyLFU, 10_000).
    WithTTL(5 * time.Minute).
    WithJanitor().
    Build()
defer cache.StopJanitor()

cache.Set("user:123", user)
cache.SetWithTTL("session:abc", session, 30*time.Minute)

value, found, err := cache.Get("user:123")

載入器模式(Read-Through)

載入器會自動擷取遺失的鍵,並透過 singleflight 去重 — 對相同遺失鍵的並發 Get() 呼叫會共享一次載入器調用:

cache := hot.NewHotCache[int, *User](hot.WTinyLFU, 10_000).
    WithTTL(5 * time.Minute).
    WithLoaders(func(ids []int) (map[int]*User, error) {
        return db.GetUsersByIDs(ctx, ids) // 批次查詢
    }).
    WithJanitor().
    Build()
defer cache.StopJanitor()

user, found, err := cache.Get(123) // 未命中時觸發載入器

容量規劃

在設定快取容量之前,請估算記憶體預算中可容納多少項目:

  1. 估算單一項目大小 — 估算結構體的大小,加上堆積分配的欄位(切片、映射、字串)的大小。包含鍵的大小。每個條目約 100 位元組的粗略開銷涵蓋內部簿記(指標、過期時間戳、演算法元資料)。
  2. 詢問開發者在生產環境中為此快取分配了多少記憶體(例如 256 MB、1 GB)。這取決於服務的總記憶體以及與之共享程序的其他部分。
  3. 計算容量capacity = memoryBudget / estimatedItemSize。向下取整以保留餘量。
範例:*User 結構體 ~500 位元組 + 字串鍵 ~50 位元組 + 開銷 ~100 位元組 = 約 650 位元組/條目
         256 MB 預算 → 256_000_000 / 650 ≈ 393,000 個項目

如果項目大小未知,請要求開發者透過單元測試來測量,該測試分配 N 個項目並檢查 runtime.ReadMemStats。未經測量就猜測容量會導致 OOM 或記憶體浪費。

常見錯誤

  1. 忘記 WithJanitor() — 沒有它,過期的條目會留在記憶體中,直到演算法將其逐出。務必在 builder 中鏈式呼叫 .WithJanitor() 並使用 defer cache.StopJanitor()
  2. 在未設定遺失快取的情況下呼叫 SetMissing() — 執行時會 panic。請先在 builder 中啟用 WithMissingCache(algorithm, capacity)WithMissingSharedCache()
  3. WithoutLocking() + WithJanitor() — 互斥,會 panic。WithoutLocking() 僅在沒有背景清理的單 goroutine 存取下才安全。
  4. 快取過大 — 容納所有內容的快取就是一個帶有開銷的映射。根據你的工作集調整大小(通常是總資料的 10-20%)。監控命中率以驗證。
  5. 忽略載入器錯誤Get() 在載入器失敗時回傳 (zero, false, err)。務必檢查 err,而不僅僅是 found

最佳實踐

  1. 始終設定 TTL — 無限制的快取會無限期提供過時資料,因為沒有重新整理的訊號
  2. 使用 WithJitter(lambda, upperBound) 來分散過期時間 — 沒有抖動,同時建立的項目會同時過期,導致對載入器的驚群效應
  3. 使用 WithPrometheusMetrics(cacheName) 進行監控 — 命中率低於 80% 通常表示快取大小不足或演算法不適合工作負載
  4. 對可變值使用 WithCopyOnRead(fn) / WithCopyOnWrite(fn) — 沒有複製,呼叫者會變異快取物件並破壞共享狀態

如需進階模式(重新驗證、分片、遺失快取、監控設定),請參閱生產模式

如需完整的 API 表面,請參閱API 參考

如果你在 samber/hot 中遇到錯誤或非預期行為,請在 https://github.com/samber/hot/issues 開啟問題。

交叉參考

  • → 請參閱 samber/cc-skills-golang@golang-performance 技能,了解一般快取策略以及何時使用記憶體快取 vs Redis vs CDN
  • → 請參閱 samber/cc-skills-golang@golang-observability 技能,了解 Prometheus 指標整合和監控
  • → 請參閱 samber/cc-skills-golang@golang-database 技能,了解與快取載入器搭配的資料庫查詢模式
  • → 請參閱 samber/cc-skills@promql-cli 技能,了解透過 CLI 查詢 Prometheus 快取指標