0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

go-redis/cacheの使い方

Posted at

Redisバックエンドのキャッシュライブラリ、go-redis/cacheについてまとめました。

このライブラリの良さそうなところ:point_up:

  • ローカルキャッシュ(多層キャッシュ)がサポートされている
  • 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/cacheOnceメソッドは、指定されたキーに対してキャッシュされた値を取得するか、キャッシュに値が存在しない場合は引数で指定した関数を実行してその結果をキャッシュし、同時にその結果を返すメソッドです。

	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によるモニタリングもサポートしているようなので、キャッシュのヒット率を調べてチューニングしたい場合にも一手間省けて良いのでは無いでしょうか:grinning:

  1. ローカルキャッシュ(多層キャッシュ)については、こちらの記事の説明がわかりやすいと思います :bow:

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?