golang-samber-hot について
Go 1.22 以降のジェネリックなインメモリキャッシュ samber/hot を扱う Go エンジニア向けスキルです。アクセス特性に応じたアルゴリズム選択や、期限、ローダー、監視の設定を説明します。
主な機能
- 019 種類のキャッシュ追い出し方式
- 02TTL と期限切れエントリーの掃除
- 03singleflight による重複排除付きローダー
- 04シャーディングと stale-while-revalidate
- 05Prometheus メトリクス
活用シーン
- 01アクセス特性に応じた追い出し方式の選択
- 02データベース読み取りのキャッシュと並行ロードの集約
- 03TTL とジッターによる期限管理
- 04ヒット率の監視と容量の調整
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ samber/ cc-skills-golang --skill golang-samber-hotインストール後、Claude Code で/golang-samber-hotを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/algorithm-guide.mdapi-reference.mdproduction-patterns.md詳細
ペルソナ: キャッシュをシステム設計上の判断として扱う Go エンジニアです。実測したアクセスパターンに基づいて削除アルゴリズムを選び、ワーキングセットのデータからキャッシュサイズを決め、有効期限、ローダーの失敗、モニタリングへの対応を常に計画します。
Go でのインメモリキャッシュに samber/hot を使用する
Go 1.22 以降に対応した、ジェネリックで型安全なインメモリキャッシュライブラリです。9 種類の削除アルゴリズム、TTL、singleflight による重複排除を備えたローダーチェーン、シャーディング、stale-while-revalidate、Prometheus メトリクスをサポートします。
公式リソース:
このスキルは網羅的ではありません。詳細については、ライブラリのドキュメントとコード例を参照してください。
- Go パッケージのドキュメント、シンボル、バージョン、インポーター、既知の脆弱性については、Context7 よりも優先して
samber/cc-skills-golang@golang-pkg-go-devスキル(godig)を参照してください。 - 独自のコードでこのライブラリの使い方を調べる場合(定義、呼び出し箇所、診断)には、
samber/cc-skills-golang@golang-goplsスキル(gopls)を参照してください。 - pkg.go.dev にインデックスされていないドキュメントについては、Context7 を引き続き代替手段として利用できます。
go get -u github.com/samber/hot
アルゴリズムの選択
アクセスパターンに基づいて選択してください。適切でないアルゴリズムを選ぶと、メモリを無駄にするか、ヒット率が大幅に低下します。
| アルゴリズム | 定数 | 最適な用途 | 避けるべきケース |
|---|---|---|---|
| W-TinyLFU | hot.WTinyLFU | 汎用、混合ワークロード(デフォルト) | デバッグのしやすさを優先する場合 |
| LRU | hot.LRU | 新しさが重要(セッション、最近のクエリ) | 頻度も重要な場合(スキャンによる汚染で頻繁に使われる項目が削除されます) |
| LFU | hot.LFU | 頻度が重要(人気の商品、DNS) | アクセスパターンが変化する場合(古くなった人気項目が削除されません) |
| TinyLFU | hot.TinyLFU | 頻度の偏りがある読み取り中心のワークロード | 書き込み中心の場合(登録フィルターのオーバーヘッドがあります) |
| S3FIFO | hot.S3FIFO | 高スループット、スキャンに強い | 小規模なキャッシュ(1,000 項目未満) |
| 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")
ローダーパターン(リードスルー)
ローダーは、見つからないキーを singleflight による重複排除付きで自動的に取得します。同じキーが見つからない場合、並行する Get() 呼び出しは 1 回のローダー呼び出しを共有します。
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) // batch query
}).
WithJanitor().
Build()
defer cache.StopJanitor()
user, found, err := cache.Get(123) // triggers loader on miss
容量のサイジング
キャッシュ容量を設定する前に、メモリ予算に収まる項目数を見積もります。
- 項目 1 つあたりのサイズを見積もります — 構造体のサイズを見積もり、ヒープに割り当てられるフィールド(スライス、マップ、文字列)のサイズを加算します。キーのサイズも含めます。エントリーあたり約 100 バイトを目安とすると、内部管理(ポインター、有効期限のタイムスタンプ、アルゴリズムのメタデータ)に必要なオーバーヘッドをおおよそカバーできます。
- 開発者に確認します — 本番環境で、このキャッシュに割り当てるメモリ量を確認します(例:256 MB、1 GB)。これはサービス全体のメモリ量や、プロセス内でほかに何がメモリを共有しているかによって異なります。
- 容量を計算します —
capacity = memoryBudget / estimatedItemSize。余裕を残せるよう、切り捨ててください。
Example: *User struct ~500 bytes + string key ~50 bytes + overhead ~100 bytes = ~650 bytes/entry
256 MB budget → 256_000_000 / 650 ≈ 393,000 items
項目のサイズが不明な場合は、N 個の項目を割り当てて runtime.ReadMemStats を確認するユニットテストで測定するよう、開発者に依頼してください。測定せずに容量を推測すると、OOM やメモリの無駄につながります。
よくある間違い
WithJanitor()を忘れる — これを指定しないと、期限切れのエントリーはアルゴリズムによって追い出されるまでメモリに残ります。ビルダーで必ず.WithJanitor()をチェーンし、defer cache.StopJanitor()を指定してください。- missing cache の設定なしで
SetMissing()を呼び出す — 実行時にパニックが発生します。まずビルダーでWithMissingCache(algorithm, capacity)またはWithMissingSharedCache()を有効にしてください。 WithoutLocking()とWithJanitor()の併用 — 相互に排他的で、パニックが発生します。WithoutLocking()は、バックグラウンドクリーンアップを行わず、単一ゴルーチンからアクセスする場合にのみ安全です。- キャッシュが大きすぎる — すべてを保持するキャッシュは、オーバーヘッドのあるマップにすぎません。ワーキングセットに合わせてサイズを設定してください(通常は全データの10~20%です)。ヒット率を監視して妥当性を確認してください。
- ローダーのエラーを無視する — ローダーでエラーが発生すると、
Get()は(zero, false, err)を返します。foundだけでなく、必ずerrも確認してください。
ベストプラクティス
- 必ず TTL を設定してください — 有効期限のないキャッシュは更新の合図がないため、古いデータを無期限に提供します
WithJitter(lambda, upperBound)を使用して有効期限を分散してください — ジッターがないと、同時に作成されたアイテムが同時に期限切れになり、ローダーにサンダリングハードが発生しますWithPrometheusMetrics(cacheName)で監視してください — ヒット率が80%を下回る場合、通常はキャッシュのサイズが不足しているか、ワークロードに適したアルゴリズムではありません- 可変値には
WithCopyOnRead(fn)/WithCopyOnWrite(fn)を使用してください — コピーがないと、呼び出し元がキャッシュされたオブジェクトを変更し、共有状態を破損させます
高度なパターン(再検証、シャーディング、missing cache、監視の設定)については、本番環境向けパターンを参照してください。
API の全容については、API リファレンスを参照してください。
samber/hot でバグや予期しない動作に遭遇した場合は、https://github.com/samber/hot/issues で issue を作成してください。
相互参照
- → 一般的なキャッシュ戦略や、インメモリーキャッシュ、Redis、CDN の使い分けについては、
samber/cc-skills-golang@golang-performanceスキルを参照してください - → Prometheus メトリクスの統合と監視については、
samber/cc-skills-golang@golang-observabilityスキルを参照してください - → キャッシュローダーと組み合わせるデータベースクエリのパターンについては、
samber/cc-skills-golang@golang-databaseスキルを参照してください - → CLI から Prometheus のキャッシュメトリクスをクエリする方法については、
samber/cc-skills@promql-cliスキルを参照してください