「GoCN酷Go推荐」高性能内存缓存 ristretto

共 4254字,需浏览 9分钟

 ·

2021-09-07 11:58

背景

ristrettodgraph 团队开源的一款高性能内存缓存库,旨在解决高并发场景下的缓存性能和吞吐瓶颈。dgraph 专攻的方向是高性能图数据库,ristretto 就是其图数据库和 KV 数据库产品的核心依赖。

与 golang 社区常见的其他单进程内存缓存类库(groupcachebigcachefastcache 等)相比,ristretto 在缓存命中率和读写吞吐率上的综合表现更优。

ristretto 简介

ristretto 主要有以下优点:

  • 高命中率 - 特殊设计的录入/驱逐政策
    • 驱逐(SampledLFU):与精确 LRU 相当,但在搜索和数据跟踪上有更好的性能
    • 录入(TinyLFU):以极小的内存开销获取额外的性能提升
  • 高吞吐率
  • 权重感知的驱逐策略 - 价值权重大的条目可以驱逐多个价值权重小的条目
    • 依托权重可以扩展出缓存最大内存占用、缓存最多条目数等场景
  • 完全并发支持
  • 性能指标 - 吞吐量、命中率及其他统计数据的性能指标
  • 用户友好的 API 设计
    • 支持指定缓存失效时间

ristretto 在 v0.1.0(2021-06-03) 版本发布时已正式标注为生产可用!

ristretto 使用举例

构建大小(条目数)受限的缓存

让我们利用 ristretto 构建一个缓存条目数最大为 10 的缓存试试看:

package main

import (
 "fmt"

 "github.com/dgraph-io/ristretto"
)

func main() {
 cache, err := ristretto.NewCache(&ristretto.Config{
  // num of keys to track frequency, usually 10*MaxCost
  NumCounters: 100,
  // cache size(max num of items)
  MaxCost: 10,
  // number of keys per Get buffer
  BufferItems: 64,
  // !important: always set true if not limiting memory
  IgnoreInternalCost: true,
 })
 if err != nil {
  panic(err)
 }

 // put 20(>10) items to cache
 for i := 0; i < 20; i++ {
  cache.Set(i, i, 1)
 }

 // wait for value to pass through buffers
 cache.Wait()

 cntCacheMiss := 0
 for i := 0; i < 20; i++ {
  if _, ok := cache.Get(i); !ok {
   cntCacheMiss++
  }
 }
 fmt.Printf("%d of 20 items missed\n", cntCacheMiss)
}

运行代码可以发现最后只有 10 个条目还保存在缓存中

$ go run main.go
10 of 20 item missed

注:当我们的缓存并非限制最大内存占用时,IgnoreInternalCost 一定要设为 true,否则创建出的缓存将出现诡异的表现。

测试缓存过期时间

还是创建一个简单的缓存,然后存一个过期时间为 1 秒的条目进去,看看接下来的缓存读写表现:

package main

import (
 "log"
 "time"

 "github.com/dgraph-io/ristretto"
)

func main() {
 cache, err := ristretto.NewCache(&ristretto.Config{
  NumCounters:        100,
  MaxCost:            10,
  BufferItems:        64,
  IgnoreInternalCost: true,
 })
 if err != nil {
  panic(err)
 }

 // set item with 1s ttl
 cache.SetWithTTL("foo""bar"1, time.Second)

 // wait for value to pass through buffers
 cache.Wait()

 if val, ok := cache.Get("foo"); !ok {
  log.Printf("cache missing")
 } else {
  log.Printf("got foo: %v", val)
 }

 // sleep longer and try again
 time.Sleep(2 * time.Second)
 if val, ok := cache.Get("foo"); !ok {
  log.Printf("cache missing")
 } else {
  log.Printf("got foo: %v", val)
 }
}

运行代码可以发现已过期的条目被正常清除出了缓存

$ go run main.go
2021/09/03 14:19:56 got foo: bar
2021/09/03 14:19:58 cache missing

总结

ristretto 是支持高并发高吞吐的内存缓存库,尤其适用于数据库、搜索引擎、文件系统等 io 密集场景。需要注意的是 ristretto 只适用于单机单进程的缓存方案,更像是 golang 中的 Caffeine (java),并不作为 redis 和 memcache 的替代品。

大家赶快试试吧!

参考资料

  • https://github.com/dgraph-io/ristretto
  • https://dgraph.io/blog/post/introducing-ristretto-high-perf-go-cache/
  • https://github.com/dgraph-io/badger
  • https://github.com/hashicorp/golang-lru
  • https://github.com/golang/groupcache

《酷Go推荐》招募:


各位Gopher同学,最近我们社区打算推出一个类似GoCN每日新闻的新栏目《酷Go推荐》,主要是每周推荐一个库或者好的项目,然后写一点这个库使用方法或者优点之类的,这样可以真正的帮助到大家能够学习到

新的库,并且知道怎么用。


大概规则和每日新闻类似,如果报名人多的话每个人一个月轮到一次,欢迎大家报名!戳「阅读原文」,即可报名


扫码也可以加入 GoCN 的大家族哟~


浏览 27
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报