はじめに
「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」22日目の記事は、バックエンドのパフォーマンス改善に欠かせないRedisについてです。
バックエンド開発を学んでいると、
- キャッシュにはRedisがいいよ
- Redisは速いよね
といった言葉を聞くかもしれません。
ただ正直なところ、私も「なんか速いやつ」くらいの、かなりふわっとした知識しかなく、本格的に触ったことがありませんでした。
この記事では、Redisとはそもそも何か、という点から、GoにおいてのRedisの使い方についてのハンズオンについて解説します。
- Redisについてよく知らない方
- バックエンドのパフォーマンスを少し意識し始めた方
- GoでRedisを触ってみたいと考えている方
この記事で、Redisの基本の「き」を理解し、Redisを取り入れる場面をしっかり見極められるように頑張りましょう!
Redisとは?
Redisは、メモリ上にデータを保存するオープンソースのNoSQLインメモリデータベースです。
フロントエンド開発におけるlocalStorageやsessionStorageを少しイメージしてみてください。キーとバリューのペアでデータを手軽に保存・取得できますよね。Redisもそれと似ていて、mykey: "Hello"のような形でデータを保存する、シンプルな**キーバリューストア(KVS)**です。
普段使っているMySQLなどのデータベースとの最大の違いは、Redisがデータを主にメモリ上に保存する点です。
ディスク(HDDやSSD)に読み書きするよりも、メモリ上での読み書きは圧倒的に高速です。そのため、Redisは超高速なデータアクセスを実現できます。
もちろん、メモリ上のデータはサーバーを再起動すると消えてしまいますが、Redisは設定によってデータをディスクに書き出して永続化する機能も持っています。
なので、単なる一時的なキャッシュだけでなく、データベースのように使うことも可能です。
Redisのユースケース
その高速性から、Redisは様々な場面で活躍します。
-
キャッシュ
- 最も代表的なユースケースです。
- 時間のかかるデータベースへの問い合わせ結果や、APIからのレスポンスなどを一時的にRedisに保存しておきます。
- 2回目以降の同じリクエストには、データベースではなくRedisから直接データを返すことで、アプリケーション全体を高速化し、データベースの負荷を下げることができます。
-
セッションストア
- Webサイトにログインしたユーザーのセッション情報(誰がログインしているか、など)を保存する場所として使われます。
- 複数台のサーバーでアプリケーションを動かしている場合でも、セッション情報をRedisで一元管理することで、どのサーバーにアクセスしてもログイン状態を維持できます。
【実践】GoからRedisを触ってみよう
それでは、実際にRedisを操作してみましょう。
go mod initまで終えた状態からスタートします。
今回は、Dockerを使ってローカルにRedis環境を簡単に立ち上げます。
STEP1: DockerでRedisを起動
ターミナルで以下のコマンドを実行するだけです。
docker run --name my-redis -p 6379:6379 -d redis
これで、localhost:6379でアクセスできるRedisサーバーが起動しました。
STEP2: GoからRedisを操作する
GoでRedisを扱うために、go-redis/redisというライブラリをインストールします。
go get github.com/redis/go-redis/v9
基本的なデータの読み書き
以下のようなコードでRedisに接続し、データの読み書きができます。
main.go
package main
import (
"context"
"fmt"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func main() {
// Redisクライアントの作成
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379", // Redisサーバーのアドレス
Password: "", // パスワードなし
DB: 0, // デフォルトのDB
})
// 1. STRING型のデータを書き込む (SET)
err := rdb.Set(ctx, "user:1", "Mamenz", 0).Err()
if err != nil {
panic(err)
}
fmt.Println("データを書き込みました")
// 2. STRING型のデータを読み込む (GET)
val, err := rdb.Get(ctx, "user:1").Result()
if err != nil {
panic(err)
}
fmt.Println("user:1 の値は:", val)
// 存在しないキーを読み込もうとした場合
val2, err := rdb.Get(ctx, "non-existent-key").Result()
if err == redis.Nil {
fmt.Println("non-existent-key は存在しません")
} else if err != nil {
panic(err)
} else {
fmt.Println("non-existent-key の値は:", val2)
}
}
go run main.goで実行すると、コンソールに以下のように表示されます。
データを書き込みました
user:1 の値は: Mamenz
non-existent-key は存在しません
キャッシュ読み込みパターン
次に、Redisの最も一般的なユースケースである「キャッシュ」として利用する際の、実践的なコードパターンを見てみます。
今回は「キャッシュ(Redis)を探して、なければDBから取得し、結果をキャッシュに保存する」というケースを実装します。
main.goに以下のように実装してみます。
package main
import (
"context"
"fmt"
"time"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
// DBからユーザー情報を取得する重い処理をシミュレート
func findUserFromDB(id string) (string, error) {
fmt.Printf("--- DBに問い合わせ中 (ID: %s) ---\n", id)
time.Sleep(2 * time.Second) // 意図的に2秒待つ
return fmt.Sprintf("User_Data_for_%s", id), nil
}
// キャッシュロジックを実装した関数
func getUser(rdb *redis.Client, id string) (string, error) {
key := fmt.Sprintf("user:%s", id)
// 1. まずRedisからデータを取得しようと試みる
val, err := rdb.Get(ctx, key).Result()
if err == nil {
// キャッシュヒット!Redisからデータを返す
fmt.Println("キャッシュからデータを取得しました")
return val, nil
}
if err != redis.Nil {
// Redisで予期せぬエラーが発生
return "", err
}
// 2. キャッシュミス... DBからデータを取得する
fmt.Println("キャッシュがなかったので、DBに問い合わせます")
dbData, err := findUserFromDB(id)
if err != nil {
return "", err
}
// 3. DBから取得したデータをRedisに保存する (有効期限10秒)
err = rdb.Set(ctx, key, dbData, 10*time.Second).Err()
if err != nil {
return "", err
}
fmt.Println("DBから取得したデータをキャッシュに保存しました")
return dbData, nil
}
func main() {
rdb := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
})
// --- 1回目のデータ取得 ---
fmt.Println("\n--- 1回目の呼び出し ---")
start := time.Now()
user, err := getUser(rdb, "123")
if err != nil {
panic(err)
}
fmt.Printf("取得データ: %s\n", user)
fmt.Printf("処理時間: %v\n", time.Since(start))
// --- 2回目のデータ取得 ---
fmt.Println("\n--- 2回目の呼び出し ---")
start = time.Now()
user, err = getUser(rdb, "123")
if err != nil {
panic(err)
}
fmt.Printf("取得データ: %s\n", user)
fmt.Printf("処理時間: %v\n", time.Since(start))
}
上記のコードを実行すると、以下のような結果になります。
--- 1回目の呼び出し ---
キャッシュがなかったので、DBに問い合わせます
--- DBに問い合わせ中 (ID: 123) ---
DBから取得したデータをキャッシュに保存しました
取得データ: User_Data_for_123
処理時間: 2.005s
--- 2回目の呼び出し ---
キャッシュからデータを取得しました
取得データ: User_Data_for_123
処理時間: 0.9ms
1回目の呼び出しでは、キャッシュが存在しないためDBへの問い合わせが発生し、処理に2秒以上かかっています。
しかし、2回目の呼び出しでは、1回目に保存されたキャッシュからデータを取得できたため、処理時間が短縮されているのがわかるかと思います。
また、rdb.Set(ctx, key, dbData, 10*time.Second)というコードでは、 SETコマンドの4番目の引数で、キャッシュの有効期限を指定しています。
これにより、10秒経過したデータは自動的にRedisから削除され、常に新しい情報をキャッシュできるようになります。
このように、キャッシュを適切に利用することで、アプリケーションのパフォーマンスを大幅に向上させることができます。
おわりに
この記事では、Redisについての基本、およびGoからの使い方を解説しました。
今回は単純なデータの読み書きを試しただけでしたが、次は実際の個人開発でAPIのレスポンスをキャッシュしたり、ユーザーのセッション情報をRedisで管理したりと、実践的な使い方に挑戦できるように頑張りたいと思います。
最後までお読みいただき、ありがとうございました!
この「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」では、GoやJavaを使い、APIの作り方、データベースとの接続、テストやDockerといった気になったバックエンド技術の基本を振り返った学びを共有しています。
ぜひ他の記事もチェックして、筆者がこのひとりアドカレを完遂することができるか、確認してみてください(^^)
学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025
それでは、明日の「学生ひよこ界隈が送るGo/Javaで実現する「はじめてのバックエンド」Advent Calendar 2025」の記事もお楽しみに!