Go 操作 Redis 的基本操作

qinhan發表於2019-09-19

Golang 操作 Redis 的基本方法

摘要

  • 看到有位老哥寫了PHP操作Redis的基本方法,於是就有了這篇部落格
  • 超喜歡Redis的,感覺使用起來很方便,但是社群不溫不火哈?
  • 就當是為 Redis 社群做貢獻吧,如果大家喜歡記得點個贊哦
  • 話不多說,直接上程式碼

Redis 連結池

  • Redis有叢集版和單機版,當配置中的Addrs寫成ip:port的陣列形式即可連結叢集版Redis
package redis

import (
    "errors"

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

type Cfg struct {
    Addrs    []string `json:"redis_addrs" env:"REDIS_ADDRS" envDefault:"127.0.0.1:6379"`
    Pwd      string   `json:"redis_pwd" env:"REDIS_PWD"`
    PoolSize int      `json:"redis_pool_size" env:"REDIS_POOL_SIZE" envDefault:"1000"`
    DB       int      `json:"redis_db" env:"REDIS_DB"` // 單機模式下選擇使用哪個DB,叢集模式下無效
}

func (c Cfg) Connect() (redis.Cmdable, error) {
    addrNum := len(c.Addrs)
    if addrNum == 0 {
        return nil, errors.New("redis addr is absent")
    }

    if addrNum > 1 {
        return redis.NewClusterClient(&redis.ClusterOptions{
            Addrs:    c.Addrs,
            Password: c.Pwd,
            PoolSize: c.PoolSize,
        }), nil
    }
    return redis.NewClient(&redis.Options{
        Addr:     c.Addrs[0],
        Password: c.Pwd,
        PoolSize: c.PoolSize,
        DB:       c.DB,
    }), nil
}
  • 接下來是一份測試,所有配置通過環境變數的形式來讀取
package redis

import (
    "os"
    "testing"
    "time"

    "github.com/caarlos0/env"
)

func TestConnect(t *testing.T) {
    os.Setenv("REDIS_ADDRS", "127.0.0.1:6379")
    os.Setenv("REDIS_PWD", "")
    os.Setenv("REDIS_POOL_SIZE", "100")
    os.Setenv("REDIS_DB", "1")

    c := Cfg{}
    if err := env.Parse(&c); err != nil {
        t.Error(err)
        return
    }

    redisPool, err := c.Connect()
    if err != nil {
        t.Error(err)
        return
    }

    res, err := redisPool.Set("test_key1", "test_value", time.Minute*10).Result()
    if err != nil {
        t.Error(err)
        return
    }

    t.Log(res)
}

Redis 的資料操作

  • 說完了如何連結到 Redis,接下來就說一說對 Redis 進行資料操作吧
  • Redis 中的資料結構可以參考官網(https://redis.io/commands
  • 這兒只舉例常用的一些資料結構,其他的讀者可以自行研究,上文中使用的Redis庫(github.com/go-redis/redis)的 redis.Cmdable 已經實現了所有的資料結構,有興趣可以自己去看
  • 最常用的 Get & Set 在上一個測試中已經有了,就不多做敘述

Hashs

  • Hashs 就類似於一個map
package redis

import (
    "os"
    "testing"
    "time"

    "github.com/caarlos0/env"
)

func TestRedis_Hash(t *testing.T) {
    os.Setenv("REDIS_ADDRS", "127.0.0.1:6379")
    os.Setenv("REDIS_PWD", "")
    os.Setenv("REDIS_POOL_SIZE", "100")
    os.Setenv("REDIS_DB", "1")

    c := Cfg{}
    if err := env.Parse(&c); err != nil {
        t.Error(err)
        return
    }

    redisPool, err := c.Connect()
    if err != nil {
        t.Error(err)
        return
    }

    hashKey := "HashKey"
    data := map[string]interface{}{
        "field1": "value1",
        "field2": 2,
        "field3": true,
    }
    for k, v := range data {
        _, err := redisPool.HSet(hashKey, k, v).Result()
        if err != nil {
            t.Error(err)
            return
        }
    }

    // get all
    results, err := redisPool.HGetAll(hashKey).Result()
    if err != nil {
        t.Error(err)
        return
    }

    t.Log("HGetAll Results : ", results)

    // get certain field
    value, err := redisPool.HGet(hashKey, "field1").Result()
    if err != nil {
        t.Error(err)
        return
    }

    t.Log("HGet field1 : ", value)
}
  • 測試執行結果如下

Go 操作 Redis 的基本操作

sorted sets

  • 排序的集合
  • 集合裡面是一對對的 k-v,並且v為float64,根據v進行排序
  • 這個資料結構可以用來做排行榜
package redis

import (
    "os"
    "testing"
    "time"

    "github.com/caarlos0/env"
    "github.com/go-redis/redis"
)

func TestRedis_Sorted_Sets(t *testing.T) {
    os.Setenv("REDIS_ADDRS", "127.0.0.1:6379")
    os.Setenv("REDIS_PWD", "")
    os.Setenv("REDIS_POOL_SIZE", "100")
    os.Setenv("REDIS_DB", "1")

    c := Cfg{}
    if err := env.Parse(&c); err != nil {
        t.Error(err)
        return
    }

    redisPool, err := c.Connect()
    if err != nil {
        t.Error(err)
        return
    }

    sortedSetKey := "SortedSets"
    data := map[interface{}]float64{
        "player1": 12,
        "player2": 2,
        "player3": 3,
    }

    // 設定兩遍
    for i := 0; i < 2; i++ {
        for k, v := range data {
            _, err := redisPool.ZIncr(sortedSetKey, redis.Z{
                Member: k,
                Score:  v,
            }).Result()
            if err != nil {
                t.Error(err)
                return
            }
        }
    }

    // 根據排名獲取資料
    // 有多種獲取資料的方法
    results, err := redisPool.ZRevRangeWithScores(sortedSetKey, 0, 10).Result()
    if err != nil {
        t.Error(err)
        return
    }

    for _, v := range results {
        t.Log(v)
    }
}
  • 測試執行結果如下
    Go 操作 Redis 的基本操作

Lists

  • 列表
package redis

import (
    "os"
    "testing"
    "time"

    "github.com/caarlos0/env"
)

func TestRedis_Lists(t *testing.T) {
    os.Setenv("REDIS_ADDRS", "127.0.0.1:6379")
    os.Setenv("REDIS_PWD", "")
    os.Setenv("REDIS_POOL_SIZE", "100")
    os.Setenv("REDIS_DB", "1")

    c := Cfg{}
    if err := env.Parse(&c); err != nil {
        t.Error(err)
        return
    }

    redisPool, err := c.Connect()
    if err != nil {
        t.Error(err)
        return
    }

    // 向list中插入資料
    listKey := "List"
    data := []interface{}{
        true,
        12,
        "test",
        22,
        "player3",
        3.9,
    }

    _, err = redisPool.LPush(listKey, data...).Result()
    if err != nil {
        t.Error(err)
        return
    }

    // 獲取長度
    len, err := redisPool.LLen(listKey).Result()
    if err != nil {
        t.Error(err)
        return
    }
    t.Log("LLen = ", len)

    // pop
    popData, err := redisPool.LPop(listKey).Result()
    if err != nil {
        t.Error(err)
        return
    }
    t.Log("LPop = ", popData)
}
  • 測試執行結果如下

Go 操作 Redis 的基本操作

最後

  • 最後說一下有關用Redis做排行榜的設計吧
  • 之前做的遊戲伺服器中有排行榜的需求,當時的設計是這樣的:玩家通過則會重新整理排行榜中的資料;伺服器定期從Redis中拉取最新的排行榜資料放到記憶體中(時間間隔是3s),玩家需要檢視排行榜的時候,遊戲伺服器則直接從記憶體中取出資料返回;此外還有定時任務取刪除Redis中的Sorted Sets,使得其長度保持在特定範圍(前端顯示的條目數)