Redisバックエンドのキャッシュライブラリ、go-redis/cacheについてまとめました。
このライブラリの良さそうなところ
- ローカルキャッシュ(多層キャッシュ)がサポートされている
- Onceメソッドが便利
初期化
redis/go-redisのクライアントを渡して初期化します。
func newCache() *cache.Cache {
addr := fmt.Sprintf("%s:%s", os.Getenv("REDIS_HOST"), os.Getenv("REDIS_PORT"))
client := redis.NewClient(&redis.Options{
Addr: addr,
DB: 0,
})
return cache.New(&cache.Options{
Redis: client,
})
}
基本操作
const testKey = "go-redis-cache-test-key"
type TestObject struct {
ID int
Name string
}
func TestGoRedisCache(t *testing.T) {
c := newCache()
ctx := context.TODO()
// ===== (1)Set =====
err := c.Set(&cache.Item{
Key: testKey,
Ctx: ctx,
Value: &TestObject{
ID: 1,
Name: "TEST",
},
TTL: time.Hour, // キャッシュ有効期間
})
assert.NilError(t, err)
// ===== (2)Get =====
var want TestObject
err = c.Get(ctx, testKey, &want)
assert.NilError(t, err)
t.Log(want) // {1 TEST}
// ===== (3)Exists =====
exists := c.Exists(ctx, testKey)
t.Log(exists) // true
// ===== (4)Delete =====
err = c.Delete(ctx, testKey)
assert.NilError(t, err)
exists = c.Exists(ctx, testKey)
t.Log(exists) // false
}
ローカルキャッシュ1
go-redis/cache
では、オプションでローカルキャッシュを設定できます。
ローカルキャッシュを使う場合、初期化時に指定します。
return cache.New(&cache.Options{
Redis: client,
+ LocalCache: cache.NewTinyLFU(1000, time.Minute),
})
このライブラリで用意されてるローカルキャッシュは、TinyLFUというインメモリキャッシュですが、インターフェースを満たせば他のアルゴリズムでローカルキャッシュを実装することも可能です。
type LocalCache interface {
Set(key string, data []byte)
Get(key string) ([]byte, bool)
Del(key string)
}
Onceメソッド
go-redis/cache
のOnce
メソッドは、指定されたキーに対してキャッシュされた値を取得するか、キャッシュに値が存在しない場合は引数で指定した関数を実行してその結果をキャッシュし、同時にその結果を返すメソッドです。
cache := newCache()
var want TestObject
err := cache.Once(&cache.Item{
Key: testKey,
Value: &want,
TTL: time.Hour, // キャッシュの有効期限
Do: func(*cache.Item) (any, error) {
// 実際にはここでDBから値を取得する
return &sqlc.Member{
ID: 1,
Name: "TEST",
}, nil
},
})
assert.NilError(t, err)
t.Log(want) // {1 TEST}
シンプルな要件であれば、このOnceメソッドだけでキャッシュ機能を十分に実現できそうです。
このメソッドは、Goのsingleflightパッケージを利用しており、複数の呼び出し元から同時に同じキーに対するリクエストがあった場合に、最初のリクエストが完了するまで他のリクエストが待機する仕組みになっています。これにより、キャッシュが期限切れとなった際に一斉にバックエンドにリクエストが殺到するのを防ぐことができ、システムの負荷を軽減する配慮がされています。
おわり
キャッシュライブラリgo-redis/cacheについてご紹介しました!すでにgo-redisを導入している場合、簡単にキャッシュ機能を追加できるため、試してみる価値はありそうです。両ライブラリは同じ組織によって開発されてるので、相性もいいと思います(多分)。
また、今回は試せてませんが、go-redis/cacheはOpenTelemetryによるモニタリングもサポートしているようなので、キャッシュのヒット率を調べてチューニングしたい場合にも一手間省けて良いのでは無いでしょうか