「GoCN酷Go推薦」高性能內(nèi)存緩存 ristretto
背景
ristretto 是 dgraph 團隊開源的一款高性能內(nèi)存緩存庫,旨在解決高并發(fā)場景下的緩存性能和吞吐瓶頸。dgraph 專攻的方向是高性能圖數(shù)據(jù)庫,ristretto 就是其圖數(shù)據(jù)庫和 KV 數(shù)據(jù)庫產(chǎn)品的核心依賴。
與 golang 社區(qū)常見的其他單進程內(nèi)存緩存類庫(groupcache,bigcache,fastcache 等)相比,ristretto 在緩存命中率和讀寫吞吐率上的綜合表現(xiàn)更優(yōu)。
ristretto 簡介
ristretto 主要有以下優(yōu)點:
高命中率 - 特殊設(shè)計的錄入/驅(qū)逐政策 驅(qū)逐(SampledLFU):與精確 LRU 相當,但在搜索和數(shù)據(jù)跟蹤上有更好的性能 錄入(TinyLFU):以極小的內(nèi)存開銷獲取額外的性能提升 高吞吐率 權(quán)重感知的驅(qū)逐策略 - 價值權(quán)重大的條目可以驅(qū)逐多個價值權(quán)重小的條目 依托權(quán)重可以擴展出緩存最大內(nèi)存占用、緩存最多條目數(shù)等場景 完全并發(fā)支持 性能指標 - 吞吐量、命中率及其他統(tǒng)計數(shù)據(jù)的性能指標 用戶友好的 API 設(shè)計 支持指定緩存失效時間
ristretto在 v0.1.0(2021-06-03) 版本發(fā)布時已正式標注為生產(chǎn)可用!
ristretto 使用舉例
構(gòu)建大小(條目數(shù))受限的緩存
讓我們利用 ristretto 構(gòu)建一個緩存條目數(shù)最大為 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)
}
運行代碼可以發(fā)現(xiàn)最后只有 10 個條目還保存在緩存中
$ go run main.go
10 of 20 item missed
注:當我們的緩存并非限制最大內(nèi)存占用時,
IgnoreInternalCost一定要設(shè)為true,否則創(chuàng)建出的緩存將出現(xiàn)詭異的表現(xiàn)。
測試緩存過期時間
還是創(chuàng)建一個簡單的緩存,然后存一個過期時間為 1 秒的條目進去,看看接下來的緩存讀寫表現(xiàn):
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)
}
}
運行代碼可以發(fā)現(xiàn)已過期的條目被正常清除出了緩存
$ go run main.go
2021/09/03 14:19:56 got foo: bar
2021/09/03 14:19:58 cache missing
總結(jié)
ristretto 是支持高并發(fā)高吞吐的內(nèi)存緩存庫,尤其適用于數(shù)據(jù)庫、搜索引擎、文件系統(tǒng)等 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同學,最近我們社區(qū)打算推出一個類似GoCN每日新聞的新欄目《酷Go推薦》,主要是每周推薦一個庫或者好的項目,然后寫一點這個庫使用方法或者優(yōu)點之類的,這樣可以真正的幫助到大家能夠?qū)W習到
新的庫,并且知道怎么用。
大概規(guī)則和每日新聞類似,如果報名人多的話每個人一個月輪到一次,歡迎大家報名!戳「閱讀原文」,即可報名
掃碼也可以加入 GoCN 的大家族喲~
