Ristretto v2 是目前 Go 生态中最先进的高性能、并发安全、支持泛型和智能淘汰策略的内存缓存库,由 Dgraph Labs 开发。它在 v2 版本中引入了 原生泛型支持,大幅提升了类型安全性和开发体验。
✅ 一、安装
go get github.com/dgraph-io/ristretto/v2
要求:Go 1.18+(因使用泛型)
二、快速入门(带完整示例)
场景:缓存用户信息(key: string, value: User struct)
package main
import (
"fmt"
"time"
"github.com/dgraph-io/ristretto/v2"
)
type User struct {
ID int
Name string
Age int
}
func main() {
// 1. 创建缓存:最多缓存 1000 个用户(每个 cost=1)
cache, err := ristretto.NewCache[string, User](&ristretto.Config[string, User]{
MaxCost: 1000, // 最多 1000 个条目
NumCounters: 10_000, // 预估唯一 key 数 × 10
BufferItems: 64, // 写缓冲区大小
Metrics: true, // 启用命中率统计
})
if err != nil {
panic(err)
}
defer cache.Close()
// 2. 写入缓存(cost=1 表明占 1 个“槽位”)
user1 := User{ID: 1, Name: "Alice", Age: 30}
cache.Set("user:1", user1, 1)
// 3. 带 TTL 写入(5 分钟后过期)
user2 := User{ID: 2, Name: "Bob", Age: 25}
cache.SetWithTTL("user:2", user2, 1, 5*time.Minute)
// wait for value to pass through buffers
cache.Wait()
// 4. 读取(类型安全!无需断言)
if val, ok := cache.Get("user:1"); ok {
fmt.Printf("Found: %+v
", val) // 直接是 User 类型
}
// 5. 读取不存在的 key
if _, ok := cache.Get("user:999"); !ok {
fmt.Println("User not found")
}
// 6. 删除
cache.Del("user:1")
// 7. 查看缓存指标
metrics := cache.Metrics
fmt.Printf("命中率: %.2f%%
", metrics.Ratio()*100)
}
输出示例:
Found: {ID:1 Name:Alice Age:30}
User not found
命中率: 50.00%
⚙️ 三、核心配置详解(ristretto.Config)
|
字段 |
类型 |
说明 |
推荐值 |
|
MaxCost |
int64 |
缓存总“成本”上限 |
按条目数:设为最大条目数;按字节:设为字节数 |
|
NumCounters |
int64 |
键计数器数量(用于 LFU) |
≈ 预期唯一 key 数 × 10 |
|
BufferItems |
int64 |
每个写缓冲区大小 |
默认 64,高并发写可增至 128/256 |
|
Metrics |
bool |
是否收集命中率等指标 |
开发/监控时开启 |
|
OnEvict |
func(K, V, int64) |
淘汰回调(可选) |
用于日志、埋点 |
示例:限制内存为 50MB(按 JSON 序列化大小计算 cost)
import "encoding/json"
func getUserCost(u User) int64 {
data, _ := json.Marshal(u)
return int64(len(data))
}
cache, _ := ristretto.NewCache[string, User](&ristretto.Config[string, User]{
MaxCost: 50 << 20, // 50MB
NumCounters: 1_000_000,
})
user := User{...}
cache.Set("u1", user, getUserCost(user))
四、常用 API(泛型版)
|
方法 |
签名 |
说明 |
|
Set |
Set(key K, value V, cost int64) bool |
写入缓存 |
|
SetWithTTL |
SetWithTTL(key K, value V, cost int64, ttl time.Duration) bool |
带过期时间写入 |
|
Get |
Get(key K) (value V, ok bool) |
读取(类型安全) |
|
Del |
Del(key K) |
删除 |
|
Wait |
Wait() |
等待所有 pending 写入完成 |
|
Metrics |
Metrics() *ristretto.Metrics |
获取统计信息 |
|
Close |
Close() |
关闭缓存(flush + 停止后台任务) |
✅ 所有方法都是 并发安全 的。
五、淘汰策略与 TTL
- 淘汰算法:基于 TinyLFU 的采样 LFU,能高效识别热点数据。
- TTL 行为:
- 过期 key 不会立即删除。
- 当调用 Get(key) 时,若已过期,返回 (zeroValue, false) 并标记为可淘汰。
- 后台 goroutine 会定期清理过期和低频数据。
TTL 是“软过期”,适合大多数场景。如需准确过期,提议结合外部定时器。
六、监控与指标
m := cache.Metrics()
fmt.Println("Hits:", m.Hits())
fmt.Println("Misses:", m.Misses())
fmt.Println("Hit Ratio:", m.Ratio()) // 0.0 ~ 1.0
fmt.Println("Current Cost:", m.CostAdded() - m.CostEvicted())
可用于 Prometheus 监控或日志告警。
⚠️ 七、注意事项
- Key 必须是 comparable 类型✅ 支持:string, int, int64, struct{}(不含 slice/map)❌ 不支持:[]byte, map, slice, func
- Value 是值拷贝修改外部变量不会影响缓存中的值。
- Cost 设计决定内存行为
若按“条目数”限制 → cost = 1
若按“内存大小”限制 → cost = len(serialize(value))
- TTL 不保证实时性适合“最终一致性”场景,不适合金融级准确过期。
八、适用场景推荐
|
场景 |
是否推荐 |
|
API 响应缓存 |
✅ 强烈推荐 |
|
数据库查询结果缓存 |
✅ |
|
会话(Session)存储 |
✅(配合 TTL) |
|
高频计数器 |
⚠️ 可用,但思考 bigcache 或 sync.Map |
|
超大对象缓存(>10MB) |
❌ 不适合(提议用磁盘或 Redis) |
✅ 总结
- Ristretto v2 = 泛型 + 高性能 + 智能 LFU + TTL + 内存可控
- 使用简单,类型安全,适合现代 Go 项目。
- 配置关键:合理设置 MaxCost 和 NumCounters。
- 新项目缓存首选,尤其适合需要高命中率的场景。
小贴士:如果只是缓存少量配置,用 sync.Map 更轻量;如果是热点数据、高频访问,选 Ristretto v2!











暂无评论内容