golang高性能无锁缓存插件库otter的使用
Golang高性能无锁缓存插件库Otter的使用
内存缓存库
Otter旨在提供出色的开发者体验,同时保持高性能。它解决了其前身的缺点,并借鉴了其他语言中高性能库(如Caffeine)的设计原则。
✨ 特性
性能方面,Otter提供:
- 通过自适应W-TinyLFU算法,在各种工作负载类型下实现高命中率
- 在大多数工作负载类型下,高并发时具有出色的吞吐量
- 在所有缓存容量中内存开销最低
- 基于争用/并行性和工作负载模式的自动数据结构配置
Otter还提供高度可配置的缓存API,支持以下可选功能:
- 超过最大值时基于大小的驱逐
- 基于时间的条目过期(自上次访问或上次写入后计算)
- 自动加载条目到缓存中
- 当首次请求过期条目时异步刷新
- 将写入传播到外部资源
- 累积缓存访问统计信息
- 将缓存保存到文件并从文件加载缓存
📚 使用
📋 要求
Otter需要Go 1.24或更高版本。
🛠️ 安装
使用v1
go get -u github.com/maypok86/otter
使用v2
go get -u github.com/maypok86/otter/v2
✏️ 示例
Otter使用简单的Options
结构进行缓存配置。以下是完整的使用示例:
package main
import (
"context"
"time"
"github.com/maypok86/otter/v2"
"github.com/maypok86/otter/v2/stats"
)
func main() {
ctx := context.Background()
// 创建统计计数器来跟踪缓存操作
counter := stats.NewCounter()
// 配置缓存:
// - 容量:10,000个条目
// - 最后访问后1秒过期
// - 写入后500ms刷新间隔
// - 启用统计收集
cache := otter.Must(&otter.Options[string, string]{
MaximumSize: 10_000,
ExpiryCalculator: otter.ExpiryAccessing[string, string](time.Second), // 在读/写时重置计时器
RefreshCalculator: otter.RefreshWriting[string, string](500 * time.Millisecond), // 写入后刷新
StatsRecorder: counter, // 附加统计收集器
})
// 阶段1:测试基本过期
// -----------------------------
cache.Set("key", "value") // 添加初始值
// 等待过期(1秒)
time.Sleep(time.Second)
// 验证条目已过期
if _, ok := cache.GetIfPresent("key"); ok {
panic("key shouldn't be found") // 应该已过期
}
// 阶段2:测试缓存雪崩保护
// --------------------------------------
loader := func(ctx context.Context, key string) (string, error) {
time.Sleep(200 * time.Millisecond) // 模拟慢加载
return "value1", nil // 返回新值
}
// 并发Get会去重加载器调用
value, err := cache.Get(ctx, "key", otter.LoaderFunc[string, string](loader))
if err != nil {
panic(err)
}
if value != "value1" {
panic("incorrect value") // 应该获取新加载的值
}
// 阶段3:测试后台刷新
// --------------------------------
time.Sleep(500 * time.Millisecond) // 等待直到需要刷新
// 返回更新值的新加载器
loader = func(ctx context.Context, key string) (string, error) {
time.Sleep(100 * time.Millisecond) // 模拟刷新
return "value2", nil // 返回刷新后的值
}
// 这会触发异步刷新但返回当前值
value, err = cache.Get(ctx, "key", otter.LoaderFunc[string, string](loader))
if err != nil {
panic(err)
}
if value != "value1" { // 应该在刷新时获取旧值
panic("loader shouldn't be called during Get")
}
// 等待刷新完成
time.Sleep(110 * time.Millisecond)
// 验证刷新后的值
v, ok := cache.GetIfPresent("key")
if !ok {
panic("key should be found") // 应该仍然缓存
}
if v != "value2" { // 现在应该有刷新后的值
panic("refresh should be completed")
}
}
📊 性能
🚀 吞吐量
吞吐量基准测试是Caffeine基准测试的Go端口。这个微基准测试比较了在zipf分布下缓存的吞吐量,可以显示实现中的各种低效之处。
🎯 命中率
命中率模拟器在各种跟踪上测试缓存:
- 合成的(Zipf分布)
- 传统的(在各种项目和论文中广泛使用)
💾 内存消耗
这个基准测试量化了不同缓存容量下的额外内存消耗。
🏗️ 使用Otter的项目
以下是已知使用Otter的项目列表:
- Grafana:开放和可组合的可观测性和数据可视化平台。
- Centrifugo:以语言无关的方式实现的可扩展实时消息服务器。
- FrankenPHP:现代PHP应用服务器
- Unkey:开源API管理平台
🗃 相关工作
Otter基于以下论文:
- BP-Wrapper: A Framework Making Any Replacement Algorithms (Almost) Lock Contention Free
- TinyLFU: A Highly Efficient Cache Admission Policy
- Adaptive Software Cache Management
- Denial of Service via Algorithmic Complexity Attack
- Hashed and Hierarchical Timing Wheels
- A large scale analysis of hundreds of in-memory cache clusters at Twitter
👏 贡献
欢迎贡献,在提交新的PR之前,请确保打开一个新的问题,以便社区成员可以讨论它。
📄 许可证
该项目采用Apache 2.0许可证。
更多关于golang高性能无锁缓存插件库otter的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于golang高性能无锁缓存插件库otter的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Otter - Go语言高性能无锁缓存库
Otter是一个Go语言实现的高性能无锁缓存库,它提供了类似Java Caffeine的API设计,具有出色的并发性能。下面我将详细介绍Otter的使用方法。
基本特性
- 无锁设计:采用无锁算法,高并发场景下性能优异
- 高命中率:使用W-TinyLFU淘汰算法
- 丰富API:支持多种缓存操作方式
- 指标监控:内置缓存命中率等监控指标
安装
go get github.com/jellydator/otter
基本使用
创建缓存
package main
import (
"fmt"
"github.com/jellydator/otter"
"time"
)
func main() {
// 创建缓存
cache, err := otter.MustBuilder[string, string](1000).
Cost(func(key string, value string) uint32 {
return 1 // 每个条目成本设为1
}).
WithTTL(time.Minute * 10). // 10分钟过期
Build()
if err != nil {
panic(err)
}
// 设置值
cache.Set("key1", "value1")
// 获取值
if value, ok := cache.Get("key1"); ok {
fmt.Println("Got value:", value)
}
// 删除键
cache.Delete("key1")
// 关闭缓存
cache.Close()
}
高级配置
cache, err := otter.MustBuilder[string, int](10000).
Cost(func(key string, value int) uint32 {
return uint32(value) // 使用值作为成本
}).
WithTTL(time.Hour). // 1小时过期
WithStatsEnabled(). // 启用统计
Build()
缓存策略
加载策略
cache, _ := otter.MustBuilder[string, string](1000).
WithLoader(func(key string) (string, error) {
// 当缓存未命中时自动加载
return "loaded_" + key, nil
}).
Build()
value, _ := cache.Get("some_key") // 自动调用Loader
淘汰策略
Otter使用W-TinyLFU算法自动管理缓存淘汰,你也可以手动控制:
// 手动设置条目的权重
cache, _ := otter.MustBuilder[string, string](1000).
Cost(func(key string, value string) uint32 {
if len(value) > 100 {
return 2 // 大值权重更高
}
return 1
}).
Build()
批量操作
// 批量设置
cache.SetMany(map[string]string{
"k1": "v1",
"k2": "v2",
})
// 批量获取
values := cache.GetMany([]string{"k1", "k2", "k3"})
事件监听
cache, _ := otter.MustBuilder[string, string](1000).
WithRemovalListener(func(key string, value string, cause otter.RemovalCause) {
fmt.Printf("Key %s removed, cause: %v\n", key, cause)
}).
Build()
性能监控
cache, _ := otter.MustBuilder[string, string](1000).
WithStatsEnabled().
Build()
// 获取统计信息
stats := cache.Stats()
fmt.Printf("Hit rate: %.2f%%\n", stats.HitRate()*100)
fmt.Println("Miss count:", stats.MissCount())
最佳实践
- 合理设置缓存大小:根据应用内存情况设置
- 考虑条目权重:不均匀大小时使用Cost函数
- 监控命中率:定期检查优化缓存策略
- 合理设置TTL:根据数据更新频率设置
性能对比
Otter在大多数场景下性能优于sync.Map和普通map+mutex的组合,特别是在高并发读多写少的场景下。
注意事项
- Otter不保证强一致性,适合最终一致性场景
- 大对象缓存需谨慎,可能影响GC性能
- 缓存键和值都应该是不可变的
Otter是一个优秀的无锁缓存解决方案,适合需要高性能缓存的Go应用场景。通过合理配置可以显著提升应用性能。