go-redis

キャッシュライブラリGoGolangRedisクライアント分散キャッシュNoSQL

GitHub概要

redis/go-redis

Redis Go client

ホームページ:https://redis.io
スター21,594
ウォッチ259
フォーク2,508
作成日:2012年7月25日
言語:Go
ライセンス:BSD 2-Clause "Simplified" License

トピックス

gogolangredisredis-clientredis-cluster

スター履歴

redis/go-redis Star History
データ取得日時: 2025/10/22 08:07

キャッシュライブラリ

go-redis

概要

go-redis(redis/go-redis)は、Go用のRedisクライアントライブラリです。高性能で豊富な機能を持ち、Redisサーバーとの接続、データ操作、高度なRedis機能の活用を可能にします。Redis Cluster、Sentinel、各種Redisモジュールをサポートし、OpenTelemetryによる計測機能も提供します。

詳細

go-redisは、基本的なkey-value操作からソート済みセット、ハッシュ、リスト、ストリーム、Pub/Subまで、Redisの全機能をサポートしています。RESP2とRESP3プロトコルに対応し、コネクションプーリング、自動再接続、フェイルオーバー機能を内蔵しています。

OpenTelemetryによるトレーシングとメトリクス、Prometheusメトリクス、Redis Searchなどの拡張機能も利用でき、エンタープライズレベルのアプリケーションにも対応します。context.Contextを全面的にサポートし、タイムアウトやキャンセレーション処理が適切に行えます。

メリット・デメリット

メリット

  • Redis機能の完全サポート(Cluster, Sentinel, Modules等)
  • 高性能なコネクションプーリングと並行処理
  • RESP2/RESP3プロトコル対応
  • OpenTelemetryによる包括的な計測機能
  • context.Contextの全面サポート
  • 自動再接続とフェイルオーバー機能
  • 豊富なRedis拡張機能(Search, Bloom等)のサポート
  • エンタープライズレベルの信頼性

デメリット

  • Redisサーバーが必要(インフラ依存)
  • 単純なインメモリキャッシュとしては過剰な場合もある
  • Redis特有の概念(データ型、コマンド等)の学習が必要
  • ネットワーク経由のため、純粋なインメモリキャッシュより遅い
  • Redis設定とチューニングの知識が必要

参考ページ

書き方の例

基本的なセットアップ

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",
        Password: "", // パスワードなし
        DB:       0,  // デフォルトDB
    })
    
    // 接続確認
    pong, err := rdb.Ping(ctx).Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("Connected to Redis:", pong)
}

基本的なSet/Get操作

// 文字列の保存
err := rdb.Set(ctx, "user:name", "John Doe", 0).Err()
if err != nil {
    panic(err)
}

// 値の取得
val, err := rdb.Get(ctx, "user:name").Result()
if err == redis.Nil {
    fmt.Println("key does not exist")
} else if err != nil {
    panic(err)
} else {
    fmt.Println("user:name", val)
}

// 有効期限付きで保存
err = rdb.Set(ctx, "session:123", "active", 30*time.Minute).Err()
if err != nil {
    panic(err)
}

URL接続と設定オプション

// URLを使用した接続
url := "redis://user:password@localhost:6379/0?protocol=3"
opts, err := redis.ParseURL(url)
if err != nil {
    panic(err)
}
rdb := redis.NewClient(opts)

// 高度な設定オプション
rdb := redis.NewClient(&redis.Options{
    Addr:         "localhost:6379",
    Password:     "mypassword",
    DB:           0,
    PoolSize:     10,               // コネクションプール数
    MinIdleConns: 5,                // 最小アイドル接続数
    MaxIdleTime:  5 * time.Minute,  // アイドルタイムアウト
    DialTimeout:  5 * time.Second,  // 接続タイムアウト
    ReadTimeout:  3 * time.Second,  // 読み取りタイムアウト
    WriteTimeout: 3 * time.Second,  // 書き込みタイムアウト
    Protocol:     3,                // RESP3プロトコル使用
})

高度なRedis操作

// ハッシュ操作
err := rdb.HSet(ctx, "user:123", map[string]interface{}{
    "name":  "John Doe",
    "email": "[email protected]",
    "age":   30,
}).Err()

userInfo := rdb.HGetAll(ctx, "user:123").Val()
fmt.Printf("User info: %v\n", userInfo)

// リスト操作
rdb.LPush(ctx, "tasks", "task1", "task2", "task3")
tasks := rdb.LRange(ctx, "tasks", 0, -1).Val()
fmt.Printf("Tasks: %v\n", tasks)

// ソート済みセット操作
rdb.ZAdd(ctx, "leaderboard", redis.Z{Score: 100, Member: "player1"})
rdb.ZAdd(ctx, "leaderboard", redis.Z{Score: 85, Member: "player2"})

// トップ10を取得
topPlayers := rdb.ZRevRangeWithScores(ctx, "leaderboard", 0, 9).Val()
for _, player := range topPlayers {
    fmt.Printf("Player: %s, Score: %.0f\n", player.Member, player.Score)
}

パイプライン処理

// パイプラインを使用した一括処理
pipe := rdb.Pipeline()

incr := pipe.Incr(ctx, "counter")
pipe.Expire(ctx, "counter", time.Hour)

// パイプライン実行
_, err := pipe.Exec(ctx)
if err != nil {
    panic(err)
}

// 結果の取得
fmt.Println("Counter value:", incr.Val())

トランザクション(MULTI/EXEC)

// トランザクションの実行
err := rdb.Watch(ctx, func(tx *redis.Tx) error {
    // 現在の値を取得
    currentVal := tx.Get(ctx, "balance").Val()
    balance, _ := strconv.Atoi(currentVal)
    
    if balance < 100 {
        return fmt.Errorf("insufficient balance")
    }
    
    // トランザクション開始
    _, err := tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
        pipe.Decr(ctx, "balance")
        pipe.Incr(ctx, "spent")
        return nil
    })
    
    return err
}, "balance")

if err != nil {
    fmt.Printf("Transaction failed: %v\n", err)
}

Pub/Sub機能

// パブリッシャー
func publisher(rdb *redis.Client) {
    for i := 0; i < 10; i++ {
        message := fmt.Sprintf("Message %d", i)
        err := rdb.Publish(ctx, "notifications", message).Err()
        if err != nil {
            panic(err)
        }
        time.Sleep(1 * time.Second)
    }
}

// サブスクライバー
func subscriber(rdb *redis.Client) {
    pubsub := rdb.Subscribe(ctx, "notifications")
    defer pubsub.Close()
    
    ch := pubsub.Channel()
    
    for msg := range ch {
        fmt.Printf("Received: %s\n", msg.Payload)
    }
}

Redis Cluster接続

// Clusterクライアント
rdb := redis.NewClusterClient(&redis.ClusterOptions{
    Addrs: []string{
        "localhost:7000",
        "localhost:7001", 
        "localhost:7002",
    },
    Password: "cluster-password",
})

// 通常の操作と同じAPIを使用
err := rdb.Set(ctx, "cluster-key", "cluster-value", 0).Err()
if err != nil {
    panic(err)
}

OpenTelemetry統合

import (
    "github.com/redis/go-redis/v9"
    "github.com/redis/go-redis/extra/redisotel/v9"
)

// OpenTelemetryインストルメンテーション
rdb := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
})

// トレーシングとメトリクスを有効化
err := redisotel.InstrumentTracing(rdb)
if err != nil {
    panic(err)
}

err = redisotel.InstrumentMetrics(rdb)
if err != nil {
    panic(err)
}

エラーハンドリングと再試行

// カスタム再試行設定
rdb := redis.NewClient(&redis.Options{
    Addr:         "localhost:6379",
    MaxRetries:   3,
    MinRetryBackoff: 8 * time.Millisecond,
    MaxRetryBackoff: 512 * time.Millisecond,
})

// エラーハンドリング
val, err := rdb.Get(ctx, "nonexistent").Result()
switch {
case err == redis.Nil:
    fmt.Println("Key does not exist")
case err != nil:
    fmt.Printf("Error occurred: %v\n", err)
default:
    fmt.Printf("Value: %s\n", val)
}