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 言語を学ぶAdvent Calendar 2023

Day 16

redisとgo-cacheで多層キャッシュを実現する

Last updated at Posted at 2023-12-16

多層キャッシュ

 キャシュとしてRedisサーバーを別で建てることが多いと思いますが、今回は多層キャッシュとしてキャッシュのキャッシュを作成し、Redisサーバーへのアクセスを節約してみようと思います。
インメモリキャッシュはgo-cacheを使用します。

実装方針

インメモリキャッシュに存在すれば、利用。
存在しなければ、redisサーバーから取得し、インメモリキャッシュを作成します。

Redisサーバーの起動

redisの立ち上げに関しては以前こちらで説明してあります。

version: '3'
services:
  golang:
    build: .
    tty: true
    volumes:
      - ./:/app/
  redis:
    image: "redis:latest"
    ports:
      - "6381:6379"
    volumes:
      - "./data/redis:/data"
FROM golang:1.21.5
WORKDIR /app

ディレクトリ構成

$ tree -N .
.
├── Dockerfile
├── adapters
│   ├── memory_cache_adapter.go
│   └── redis_adapter.go
├── data
│   └── redis
│       └── dump.rdb
├── docker-compose.yml
├── go.mod
├── go.sum
├── main.go
└── interfaces
    └── cache.go

4 directories, 9 files

インメモリキャッシュのアダプターとRedisのアダプターをadapters内に作成しました。

adapters/redis_adapter.go
package adapters

import "github.com/go-redis/redis"


type  RedisAdapter struct {
	client *redis.Client
}



func NewRedisAdapter() *RedisAdapter {
	return &RedisAdapter{
		client: redis.NewClient(&redis.Options{
			Addr:     "redis:6379",
			Password: "",
			DB:       0,
		}),
	}
}

func (adapter *RedisAdapter) Get(key string) string {
	val, err := adapter.client.Get(key).Result()
	if err != nil {
		return ""
	}
	return val
}

func (adapter * RedisAdapter) Set(key string, value string) {
	adapter.client.Set(key, value, 0)
}

func (adapter * RedisAdapter) Delete(key string) {
	adapter.client.Del(key)
}
adapters/memory_cache_adapter.go
package adapters

import (
	"time"

	"github.com/patrickmn/go-cache"
)


type MemoryCacheAdapter struct {
	client *cache.Cache

}

func NewMemoryCacheAdapter() *MemoryCacheAdapter {
	return &MemoryCacheAdapter{
		client: cache.New(30*time.Second, 30*time.Second),
	}
}


func (adapter *MemoryCacheAdapter) Get(key string) string {
	val, found := adapter.client.Get(key)
	if found {
		return val.(string)
	}
	return ""
}

func (adapter *MemoryCacheAdapter) Set(key string, value string) {
	adapter.client.Set(key, value, cache.DefaultExpiration)
}

func (adapter *MemoryCacheAdapter) Delete(key string) {
	adapter.client.Delete(key)
}

次にinterfaceを定義します。

interfaces/cache.go
package interfaces

type Cache interface {
	Get(key string) string
	Set(key string, value string)
	Delete(key string)
}

func GetData(cache Cache, key string) string {
	return cache.Get(key)
}

func SetData(cache Cache, key string, value string) {
	cache.Set(key, value)
}

func DeleteData(cache Cache, key string) {
	cache.Delete(key)
}

最後にmain.goです。
繰り返しキャッシュからの取得を試みて、どこから取得しているのか、いつ揮発するのかを確認します。

main.go
package main

import (
	"fmt"
	"in_memory_cache_demo/adapters"
	"in_memory_cache_demo/interfaces"
	"time"
)

func main() {
	memory_cache_adapter := adapters.NewMemoryCacheAdapter()
	redis_adapter := adapters.NewRedisAdapter()
	totalTime := 0
	for {
		value := interfaces.GetData(memory_cache_adapter, "key")
		if value == "" {
			fmt.Println("インメモリキャッシュに無いのでRedisから取得します")
			value = interfaces.GetData(redis_adapter, "key")
			interfaces.SetData(memory_cache_adapter, "key", value)
		} else {
			fmt.Println("インメモリキャッシュにありました")
		}
		fmt.Println(value)
		// 3秒待機
		time.Sleep(3 * time.Second)
		// 開始から何秒経過したかを表示
		totalTime += 3
		fmt.Println(totalTime, "秒経過")
	}
}

検証

Redisサーバーにキャッシュを作る

最初に手動でキャッシュを作っておきます。

$ redis-cli -p 6381
127.0.0.1:6381> SET key hogehgoehoge
OK
127.0.0.1:6381> get key
"hogehgoehoge"

main.goの実行

最初はredisにしかキャッシュが存在しないため、redisから取得。以降はインメモリキャッシュから取得されるはずです。
インメモリキャッシュは30秒で揮発するように設定しているので、開始から30秒経過後、再びredisから取得されるはずです。

root@db2ef987e544:/app# go run main.go
インメモリキャッシュに無いのでRedisから取得します
hogehgoehoge
3 秒経過
インメモリキャッシュにありました
hogehgoehoge
6 秒経過
インメモリキャッシュにありました
hogehgoehoge
9 秒経過
インメモリキャッシュにありました
hogehgoehoge
12 秒経過
インメモリキャッシュにありました
hogehgoehoge
15 秒経過
インメモリキャッシュにありました
hogehgoehoge
18 秒経過
インメモリキャッシュにありました
hogehgoehoge
21 秒経過
インメモリキャッシュにありました
hogehgoehoge
24 秒経過
インメモリキャッシュにありました
hogehgoehoge
27 秒経過
インメモリキャッシュにありました
hogehgoehoge
30 秒経過
インメモリキャッシュに無いのでRedisから取得します
hogehgoehoge
33 秒経過
インメモリキャッシュにありました
hogehgoehoge

よさそうです。
この場合、redisサーバーへのアクセスが1/10になりました。

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?