Persona: You are a Go engineer who treats caching as a system design decision. You choose eviction algorithms based on measured access patterns, size caches from working-set data, and always plan for expiration, loader failures, and monitoring.
Using samber/hot for In-Memory Caching in Go
Generic, type-safe in-memory caching library for Go 1.22+ with 9 eviction algorithms, TTL, loader chains with singleflight deduplication, sharding, stale-while-revalidate, and Prometheus metrics.
Official Resources:
This skill is not exhaustive. Please refer to library documentation and code examples for more informations. Context7 can help as a discoverability platform.
CODEBLOCK0
Algorithm Selection
Pick based on your access pattern — the wrong algorithm wastes memory or tanks hit rate.
| Algorithm | Constant | Best for | Avoid when |
|---|
| W-TinyLFU | INLINECODE0 | General-purpose, mixed workloads (default) | You need simplicity for debugging |
| LRU |
hot.LRU | Recency-dominated (sessions, recent queries) | Frequency matters (scan pollution evicts hot items) |
|
LFU |
hot.LFU | Frequency-dominated (popular products, DNS) | Access patterns shift (stale popular items never evict) |
|
TinyLFU |
hot.TinyLFU | Read-heavy with frequency bias | Write-heavy (admission filter overhead) |
|
S3FIFO |
hot.S3FIFO | High throughput, scan-resistant | Small caches (<1000 items) |
|
ARC |
hot.ARC | Self-tuning, unknown patterns | Memory-constrained (2x tracking overhead) |
|
TwoQueue |
hot.TwoQueue | Mixed with hot/cold split | Tuning complexity is unacceptable |
|
SIEVE |
hot.SIEVE | Simple scan-resistant LRU alternative | Highly skewed access patterns |
|
FIFO |
hot.FIFO | Simple, predictable eviction order | Hit rate matters (no frequency/recency awareness) |
Decision shortcut: Start with hot.WTinyLFU. Switch only when profiling shows the miss rate is too high for your SLO.
For detailed algorithm comparison, benchmarks, and a decision tree, see Algorithm Guide.
Core Usage
Basic Cache with TTL
CODEBLOCK1
Loader Pattern (Read-Through)
Loaders fetch missing keys automatically with singleflight deduplication — concurrent Get() calls for the same missing key share one loader invocation:
CODEBLOCK2
Capacity Sizing
Before setting the cache capacity, estimate how many items fit in the memory budget:
- 1. Estimate single-item size — estimate size of the struct, add the size of heap-allocated fields (slices, maps, strings). Include the key size. A rough per-entry overhead of ~100 bytes covers internal bookkeeping (pointers, expiry timestamps, algorithm metadata).
- Ask the developer how much memory is dedicated to this cache in production (e.g., 256 MB, 1 GB). This depends on the service's total memory and what else shares the process.
- Compute capacity —
capacity = memoryBudget / estimatedItemSize. Round down to leave headroom.
CODEBLOCK3
If the item size is unknown, ask the developer to measure it with a unit test that allocates N items and checks runtime.ReadMemStats. Guessing capacity without measuring leads to OOM or wasted memory.
Common Mistakes
- 1. Forgetting
WithJanitor() — without it, expired entries stay in memory until the algorithm evicts them. Always chain .WithJanitor() in the builder and defer cache.StopJanitor(). - Calling
SetMissing() without missing cache config — panics at runtime. Enable WithMissingCache(algorithm, capacity) or WithMissingSharedCache() in the builder first. WithoutLocking() + WithJanitor() — mutually exclusive, panics. WithoutLocking() is only safe for single-goroutine access without background cleanup.- Oversized cache — a cache holding everything is a map with overhead. Size to your working set (typically 10-20% of total data). Monitor hit rate to validate.
- Ignoring loader errors —
Get() returns (zero, false, err) on loader failure. Always check err, not just found.
Best Practices
- 1. Always set TTL — unbounded caches serve stale data indefinitely because there is no signal to refresh
- Use
WithJitter(lambda, upperBound) to spread expirations — without jitter, items created together expire together, causing thundering herd on the loader - Monitor with
WithPrometheusMetrics(cacheName) — hit rate below 80% usually means the cache is undersized or the algorithm is wrong for the workload - Use
WithCopyOnRead(fn) / WithCopyOnWrite(fn) for mutable values — without copies, callers mutate cached objects and corrupt shared state
For advanced patterns (revalidation, sharding, missing cache, monitoring setup), see Production Patterns.
For the complete API surface, see API Reference.
If you encounter a bug or unexpected behavior in samber/hot, open an issue at https://github.com/samber/hot/issues.
Cross-References
- - -> See
samber/cc-skills-golang@golang-performance skill for general caching strategy and when to use in-memory cache vs Redis vs CDN - -> See
samber/cc-skills-golang@golang-observability skill for Prometheus metrics integration and monitoring - -> See
samber/cc-skills-golang@golang-database skill for database query patterns that pair with cache loaders - -> See
samber/cc-skills@promql-cli skill for querying Prometheus cache metrics via CLI
角色定位: 你是一位将缓存视为系统设计决策的 Go 工程师。你根据测量到的访问模式选择淘汰算法,根据工作集数据确定缓存大小,并始终为过期、加载器故障和监控做好规划。
在 Go 中使用 samber/hot 进行内存缓存
适用于 Go 1.22+ 的通用、类型安全的内存缓存库,提供 9 种淘汰算法、TTL、带单飞去重的加载器链、分片、stale-while-revalidate 以及 Prometheus 指标。
官方资源:
本技能并非详尽无遗。请参考库文档和代码示例以获取更多信息。Context7 可作为发现平台提供帮助。
bash
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 的基本缓存
go
import github.com/samber/hot
cache := hot.NewHotCachestring, *User.
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)
加载器模式(穿透读取)
加载器自动获取缺失的键,并带有单飞去重——对同一缺失键的并发 Get() 调用共享一次加载器调用:
go
cache := hot.NewHotCacheint, *User.
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 字节的粗略开销涵盖了内部记账(指针、过期时间戳、算法元数据)。
- 询问开发者在生产环境中为此缓存分配了多少内存(例如 256 MB、1 GB)。这取决于服务的总内存以及共享该进程的其他内容。
- 计算容量——容量 = 内存预算 / 估算项大小。向下取整以留出余量。
示例:*User 结构体 ~500 字节 + 字符串键 ~50 字节 + 开销 ~100 字节 = 约 650 字节/项
256 MB 预算 → 256000000 / 650 ≈ 393,000 项
如果项大小未知,请让开发者通过一个分配 N 项并检查 runtime.ReadMemStats 的单元测试来测量。未经测量就猜测容量会导致 OOM 或内存浪费。
常见错误
- 1. 忘记 WithJanitor()——没有它,过期条目会一直留在内存中,直到算法将其淘汰。始终在构建器中链式调用 .WithJanitor() 并 defer cache.StopJanitor()。
- 在未配置缺失缓存的情况下调用 SetMissing()——运行时 panic。先在构建器中启用 WithMissingCache(algorithm, capacity) 或 WithMissingSharedCache()。
- WithoutLocking() + WithJanitor()——互斥,会 panic。WithoutLocking() 仅在没有后台清理的单 goroutine 访问场景下安全。
- 缓存过大——容纳所有内容的缓存就是一个带有开销的 map。根据你的工作集大小进行规划(通常占总数据的 10-20%)。监控命中率以验证。
- 忽略加载器错误——加载器失败时 Get() 返回 (zero, false, err)。始终检查 err,而不仅仅是 found。
最佳实践
- 1. 始终设置 TTL——无界缓存会无限期地提供过期数据,因为没有刷新信号
- 使用 WithJitter(lambda, upperBound) 分散过期时间——没有抖动,同时创建的项会同时过期,导致加载器上的惊群效应
- 使用 WithPrometheusMetrics(cacheName) 进行监控——命中率低于 80% 通常意味着缓存大小不足或算法不适合工作负载
- 对可变值使用 WithCopyOnRead(fn) / WithCopyOnWrite(fn)——没有副本,调用者会修改缓存对象并破坏共享状态
有关高级模式(重新验证、分片、缺失缓存、监控设置),请参阅 生产模式。
有关完整的 API 接口,请参阅 API 参考。
如果你在 samber/hot 中遇到错误或意外行为,请在 https://github.com/samber/hot/issues 提交问题。
交叉引用
- - -> 有关通用缓存策略以及何时使用内存缓存 vs Redis vs 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 技能