go-redis使用入門

安裝go-redis

//redis 6
go get github.com/go-redis/redis/v8
//redis 7
go get github.com/go-redis/redis/v9

初始化連接redis

func redisInit() {
    //初始化redis,連接地址和端口,密碼,數(shù)據(jù)庫名稱
    rdb = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "wmq12138",
        DB:       0,
    })
}

入門案例

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)

var rdb *redis.Client

func main() {
    redisInit()
    //創(chuàng)建上下文
    ctx := context.Background()
    //set方法設(shè)置key和value,處理返回的錯誤,參數(shù)(上下文,key名,value值,過期時間)
    err := rdb.Set(ctx, "goredistest", "test", 0).Err()
    if err != nil {
        fmt.Print(err)
        return
    }
    //get方法獲取value
    val, err := rdb.Get(ctx, "goredistest").Result()
    if err != nil {
        fmt.Print(err)
        return
    }
    //do方法使用原生命令,返回值是一個interface類型
    result, err := rdb.Do(ctx, "get", "goredistest").Result()
    if err != nil {
        fmt.Print(err)
        return
    }
    fmt.Println("get:", val)
    fmt.Print("原生命令:", result.(string))

}

連接配置

redis.NewClient(&redis.Options{}),其中Options是連接的配置,是一個結(jié)構(gòu)體類型,以下是配置選項和說明

type Options struct {
  // 網(wǎng)絡(luò)類型:[ tcp , unix ]
  // 默認(rèn)是 tcp
  Network string

  // host:port 地址
  Addr string

  // 要使用的 TLS 配置。 當(dāng)設(shè)置 TLS 時將協(xié)商。
  TLSConfig *tls.Config
  //創(chuàng)建一個新的連接,優(yōu)先于Newwork和Addr選項
  Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
  // 新建一個redis連接的時候,會回調(diào)這個函數(shù)
  OnConnect func(ctx context.Context, cn *Conn) error
  // 當(dāng)連接到使用 Redis ACL 系統(tǒng)的 Redis 6.0 或更高版本的實例時,
  // 使用指定的 用戶名 對當(dāng)前連接進行身份驗證  (ACL 列表中定義的連接之一)。
  Username string

  // 可選密碼。 
  // 必須與 requirepass 服務(wù)器配置選項中指定的密碼(如果連接到 Redis 5.0 或更低版本的實例)
  // 或 連接到使用 Redis ACL 系統(tǒng)的 Redis 6.0 或更高版本的實例時的用戶密碼 匹配。
  Password string

  // 連接到服務(wù)器后要選擇的數(shù)據(jù)庫。
  DB int

  // ====== 重試、退避時間======
  // 放棄前的最大重試次數(shù)。
  // 默認(rèn)是 3 次重試; -1(非 0)禁用重試。
  MaxRetries int
  // 每次重試之間的最小退避。
  // 默認(rèn)為 8 毫秒; -1 禁用退避。
  MinRetryBackoff time.Duration
   // 每次重試之間的最大退避。
  // 默認(rèn)為 512 毫秒; -1 禁用退避。
  MaxRetryBackoff time.Duration

  // ======連接超時、讀超時、寫超時======
  // 建立新連接的撥號超時。
  // 默認(rèn)為 5 秒。
  DialTimeout time.Duration
  // 套接字讀取超時。 
  // 如果達到,命令將失敗并超時而不是阻塞。
  // 使用值 -1 表示無超時,使用 0 表示默認(rèn)值。
  // 默認(rèn)為 3 秒。
  ReadTimeout time.Duration
  // 套接字寫入超時。 
  // 如果達到,命令將失敗并超時而不是阻塞。
  // 默認(rèn)為 ReadTimeout。
  WriteTimeout time.Duration

  // 連接池的類型。
  // FIFO 池為 true,LIFO 池為 false。
  // 請注意,與 lifo 相比,fifo 的開銷更高。
  PoolFIFO bool

  // 最大套接字連接數(shù)。
  // 默認(rèn)為每個可用 CPU 10 個連接,由 runtime.GOMAXPROCS 報告。  
  PoolSize int

  // 建立新連接緩慢時有用的最小空閑連接數(shù)。
  MinIdleConns int

  // 客戶端退出(關(guān)閉)連接的連接年齡。
  // 默認(rèn)是不關(guān)閉老化的連接。
  MaxConnAge time.Duration

  // 如果所有連接都忙,則客戶端在返回錯誤之前等待連接的時間。
  // 默認(rèn)為 ReadTimeout + 1 秒。
  PoolTimeout time.Duration

  // 客戶端關(guān)閉空閑連接的時間。
  // 應(yīng)該小于服務(wù)器的超時時間。
  // 默認(rèn)為 5 分鐘。 -1 禁用空閑超時檢查。
  IdleTimeout time.Duration

  // 空閑連接 reaper 進行空閑檢查的頻率。
  // 默認(rèn)為 1 分鐘。 -1 禁用空閑連接reaper,
  // 但如果設(shè)置了 IdleTimeout,空閑連接仍會被客戶端丟棄。
  IdleCheckFrequency time.Duration
  
  // 在從節(jié)點上啟用只讀查詢。
  readOnly bool

  // 用于實現(xiàn)斷路器或速率限制器的限制器接口。
  Limiter Limiter
}

基本使用

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
    "time"
)

var rdb *redis.Client          //創(chuàng)建redis客戶端實例
var ctx = context.Background() //創(chuàng)建上下文

string類型的操作方法

  • Get
  • Set
  • GetSet
  • SetNX
  • MGset
  • MSet
  • Incr,IncrBy
  • Decr,DecrBy
  • Del
  • Expire

Get 獲取key的值,返回值:錯誤信息error和value

//get 方法 返回值和錯誤信息
func Get(k string) string {
    str, err := rdb.Get(ctx, k).Result()
    if err != nil {
        fmt.Print(err)
    }
    fmt.Println("key", k, "的值:", str)
    return str
}

Set 設(shè)置key和value,以及key的過期時間expiration 返回值:error

//set 方法
func Set(key string, val interface{}, expiration time.Duration) {
    err := rdb.Set(ctx, key, val, expiration).Err()
    if err != nil {
        fmt.Print(err)
        return
    }
}

GetSet 設(shè)置一個key的值,并且返回這個key的舊值

func GetSet(k string, v interface{}) interface{} {
    oldValue, err := rdb.GetSet(ctx, k, v).Result()
    if err != nil {
        fmt.Print(err)
    }
    fmt.Println("設(shè)置一個key的值,并返回這個key的舊值:", oldValue)
    return oldValue
}

SetNX 如果key不存在,則設(shè)置這個key的值

func SetNx(k string, v interface{}, t time.Duration) {
    err := rdb.SetNX(ctx, k, v, t)
    if err != nil {
        fmt.Print(err)
    }
}

MGet 批量查詢key的值

func MGet(k ...string) {
    err := rdb.MGet(ctx, k...)
    if err != nil {
        fmt.Print(err)
    }
}

MSet 批量設(shè)置key的值


//MSet 批量設(shè)置key的值
func MSet(values ...interface{}) {
    rdb.MSet(ctx, values)
}

Del 刪除單個或者多個key

//delOneKeys 刪除單個key
func delOneKeys(k string) {
    rdb.Del(ctx, k)
}

//delKeys 刪除多個key
func delKeys(k ...string) {
    rdb.Del(ctx, k...)
}

Expire 設(shè)置key的過期時間

func expire(k string, t time.Duration) {
    rdb.Expire(ctx, k, t)
}

Incr針對一個key的數(shù)值進行遞增操作

IncrBy指定每次遞增多少 IncrByFloat 指定每次遞增多少,跟IncrBy的區(qū)別是累加的是浮點數(shù)

//addVal 針對一個key的數(shù)值進行遞增操作
func addVal(k string) {
    // Incr函數(shù)每次加一
    val, err := rdb.Incr(ctx, "key").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("最新值", val)

    // IncrBy函數(shù),可以指定每次遞增多少
    valBy, err := rdb.IncrBy(ctx, "key", 2).Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("最新值", valBy)

    // IncrByFloat函數(shù),可以指定每次遞增多少,跟IncrBy的區(qū)別是累加的是浮點數(shù)
    valFloat, err := rdb.IncrByFloat(ctx, "key1", 2.2).Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("最新值", valFloat)
}

Decr 針對一個key的數(shù)值進行遞減操作

func Decr() {
    // Decr函數(shù)每次減一
    val, err := rdb.Decr(ctx, "key").Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("最新值", val)

    // DecrBy函數(shù),可以指定每次遞減多少
    valBy, err := rdb.DecrBy(ctx, "key", 2).Result()
    if err != nil {
        panic(err)
    }
    fmt.Println("最新值", valBy)
}

Hash類型的操作方法

內(nèi)部采用數(shù)組+鏈表結(jié)構(gòu),采用鏈地址法解決哈希沖突。

// HashMethods Hash 操作方法
func HashMethods() {
    //●  HSet
    // user_1 是hash key,username 是字段名, zhangsan是字段值
    rdb.HSet(ctx, "user_1", "username", "zhangsan", "f1", "f_v1")
    //● 2. HGet     根據(jù)key和field字段,查詢field字段的值
    result, _ := rdb.HGet(ctx, "user_1", "username").Result()
    fmt.Println(result)

    //● 3. HGetAll  獲取所有的字段和值
    all, _ := rdb.HGetAll(ctx, "user_1").Result()
    fmt.Println(all)
    //● 4. HIncrBy 累加count字段的值,一次性累加2, user_1為hash key
    count, err := rdb.HIncrBy(ctx, "user_1", "count", 2).Result()
    fmt.Println(count, err)
    //● 5. HKeys根據(jù)key返回所有的字段名
    keys := rdb.HKeys(ctx, "user_1")
    fmt.Println(keys)
    //● 6. HLen根據(jù)key,查詢hash的字段數(shù)量
    i, err := rdb.HLen(ctx, "user_1").Result()
    fmt.Println(i)
    //● 7. HMGet根據(jù)key和多個字段名,批量查詢多個hash字段值
    b, err := rdb.HMGet(ctx, "user_1", "f1", "count").Result()
    fmt.Println(b)
    //● 8. HMSet根據(jù)key和多個字段名和字段值,批量設(shè)置hash字段值
    // 初始化hash數(shù)據(jù)的多個字段值
    data := make(map[string]interface{})
    data["id"] = 1
    data["username"] = "lisi"
    // 一次性保存多個hash字段值
    rdb.HMSet(ctx, "key", data).Err()

    //● 9. HSetNX如果field字段不存在,則設(shè)置hash字段值
    rdb.HSetNX(ctx, "user_1", "f2", "f2value")
    //● 10. HDel根據(jù)key和字段名,刪除hash字段,支持批量刪除hash字段
    // 刪除一個字段id
    rdb.HDel(ctx, "key", "id")
    // 刪除多個字段
    rdb.HDel(ctx, "key", "id", "username")
    //● 11. HExists檢測hash字段名是否存在
    err = rdb.HExists(ctx,"key", "id").Err()
    if err != nil {
        fmt.Println(err)
    }

}

List的操作方法


//ListOperateMethods List操作方法
func ListOperateMethods() {
    //● 1. LPush 添加到list的左側(cè),LPush支持一次插入一個或者任意個數(shù)據(jù)
    rdb.LPush(ctx, "w1", "w2", "w3", "w4", "w")
    //● 2. LPushX 跟LPush的區(qū)別是,僅當(dāng)列表存在的時候才插入數(shù)據(jù),用法完全一樣。
    rdb.LPushX(ctx, "w1", "w2", "w3", "w4", "w")
    //● 3. RPop從列表的右邊刪除第一個數(shù)據(jù),并返回刪除的數(shù)據(jù)
    rdb.RPop(ctx, "w1")
    //● 4. RPush
    rdb.RPush(ctx, "w1", "wmq", "wmq2")
    //● 5. RPushX 跟RPush的區(qū)別是,僅當(dāng)列表存在的時候才插入數(shù)據(jù), 他們用法一樣
    rdb.RPushX(ctx, "w1", "wm3", "w3")
    //● 6. LPop從列表左邊刪除第一個數(shù)據(jù),并返回刪除的數(shù)據(jù)
    val, _ := rdb.LPop(ctx, "w1").Result()
    fmt.Println(val)
    //● 7. LLen返回列表的大小
    lLen, _ := rdb.LLen(ctx, "w1").Result()
    fmt.Println(lLen)
    //● 8. LRange返回列表的一個范圍內(nèi)的數(shù)據(jù),也可以返回全部數(shù)據(jù)
    result, _ := rdb.LRange(ctx, "w1", 0, lLen).Result()
    fmt.Println(result)
    //● 9. LRem刪除列表中的數(shù)據(jù) 從列表左邊開始,刪除100, 如果出現(xiàn)重復(fù)元素,僅刪除1次,也就是刪除第一個
    dels, _ := rdb.LRem(ctx, "key", 1, "w1").Result()
    fmt.Println(dels)
    //● 10. LIndex
    // 列表索引從0開始計算,這里返回第6個元素
    val, _ = rdb.LIndex(ctx, "w1", 5).Result()

    fmt.Println(val)
    //● 11. LInsert// 在列表中5的前面插入4
    //// before是之前的意思
    insert := rdb.LInsert(ctx, "w1", "after", 1, 2)
    fmt.Println(insert)
}

Set的操作方法

**Set是無序且不會重復(fù)的字符串集合 **set和list的區(qū)別是set不包含重復(fù)的元素


//  Set操作方法
func setOperateMethods() {
    //● 1. SAdd
    rdb.SAdd(ctx, "set_key", 100, 10, 32, 4, 100, 5)
    //● 2. SCard
    res, _ := rdb.SCard(ctx, "set_key").Result()
    fmt.Println(res)

    //● 3. SIsMember判斷元素是否在集合中
    result, _ := rdb.SIsMember(ctx, "set_key", 900).Result()
    fmt.Println(result)
    //● 4. SMembers 獲取集合中所有的元素
    strings, _ := rdb.SMembers(ctx, "set_key").Result()
    fmt.Println(strings)

    //● 5. SRem刪除集合元素
    i, _ := rdb.SRem(ctx, "set_key", 100, 4).Result()
    fmt.Println("返回刪除的個數(shù)", i)
    //● 6. SPop,SPopN 隨機返回集合中的元素,并且刪除返回的元素
    rdb.SPop(ctx, "set_key")
    fmt.Println(rdb.SMembers(ctx, "set_key").Result())

    // 隨機返回集合中的一個元素,并且刪除這個元素
    val, _ := rdb.SPop(ctx,"key").Result()
    fmt.Println(val)

    // 隨機返回集合中的5個元素,并且刪除這些元素
    vals, _ := rdb.SPopN(ctx,"key", 5).Result()
    fmt.Println(vals)

}

sorted set操作方法

有序的,非重復(fù)的的字符串集合


發(fā)布訂閱

Redis提供了發(fā)布訂閱功能,可以用于消息的傳輸,Redis的發(fā)布訂閱機制包括三個部分,發(fā)布者,訂閱者和Channel。

image.png
image.png

發(fā)布者和訂閱者都是Redis客戶端,Channel則為Redis服務(wù)器端,發(fā)布者將消息發(fā)送到某個的頻道,訂閱了這個頻道的訂閱者就能接收到這條消息。

訂閱者 subscriber

//subscriber 訂閱者訂閱channel1的消息
func subscriber() {
    // 訂閱channel1這個channel
    sub := rdb.Subscribe(ctx, "channel1")
    // sub.Channel() 返回go channel,可以循環(huán)讀取redis服務(wù)器發(fā)過來的消息
    for msg := range sub.Channel() {
        // 打印收到的消息
        fmt.Println( msg.Channel, msg.Payload)
        fmt.Println()
    }
    //或者
    for {
        msg, err := sub.ReceiveMessage(ctx)
        if err != nil {
            fmt.Println(err)
        }
        fmt.Println(msg.Channel, msg.Payload)
    }
}

發(fā)布者 publisher

package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
    "strconv"
)

var rdb *redis.Client          //創(chuàng)建redis客戶端實例
var ctx = context.Background() //創(chuàng)建上下文
func main() {
    //初始化redis,連接地址和端口,密碼,數(shù)據(jù)庫名稱
    rdb = redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",
        DB:       0,
    })
    // 將"message"消息發(fā)送到channel1這個通道上

    for i := 1; i <= 100; i++ {
        fmt.Println(i)
        str := strconv.Itoa(i) + ".message收到前端回答"
        rdb.Publish(ctx, "channel1", str)
    }
}

image.png
image.png

其他的一些方法

func cancelSub() {
    // 訂閱channel1這個channel
    sub := rdb.Subscribe(ctx, "channel1")
    // 取消訂閱
    sub.Unsubscribe(ctx, "channel1")
}

func querySubCount() {
    // 查詢channel_1通道的訂閱者數(shù)量
    chs, _ := rdb.PubSubNumSub(ctx, "channel_1").Result()
    for ch, count := range chs {
        fmt.Println(ch)    // channel名字
        fmt.Println(count) // channel的訂閱者數(shù)量
    }
}

事務(wù)操作

redis事務(wù)可以一次執(zhí)行多個命令, 并且?guī)в幸韵聝蓚€重要的保證:

  • 事務(wù)是一個單獨的隔離操作:事務(wù)中的所有命令都會序列化、按順序地執(zhí)行。事務(wù)在執(zhí)行的過程中,不會被其他客戶端發(fā)送來的命令請求所打斷。
  • 事務(wù)是一個原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部都不執(zhí)行。

TxPinline

//事務(wù)操作
//TxPinline
func Txline() {
    // 開啟一個TxPipeline事務(wù)
pipe := rdb.TxPipeline()

// 執(zhí)行事務(wù)操作,可以通過pipe讀寫redis
incr := pipe.Incr(ctx,"tx_pipeline_counter")
pipe.Expire(ctx,"tx_pipeline_counter", time.Hour)

// 上面代碼等同于執(zhí)行下面redis命令
//
//     MULTI
//     INCR pipeline_counter
//     EXPIRE pipeline_counts 3600
//     EXEC

// 通過Exec函數(shù)提交redis事務(wù)
_, err := pipe.Exec(ctx)

// 提交事務(wù)后,我們可以查詢事務(wù)操作的結(jié)果
// 前面執(zhí)行Incr函數(shù),在沒有執(zhí)行exec函數(shù)之前,實際上還沒開始運行。
fmt.Println(incr.Val(), err)
}

watch

redis樂觀鎖支持,可以通過watch監(jiān)聽一些Key, 如果這些key的值沒有被其他人改變的話,才可以提交事務(wù)


func watch() {

    // 定義一個回調(diào)函數(shù),用于處理事務(wù)邏輯
    fn := func(tx *redis.Tx) error {
        // 先查詢下當(dāng)前watch監(jiān)聽的key的值
        v, err := tx.Get(ctx, "key").Int()
        if err != nil && err != redis.Nil {
            return err
        }
        // 這里可以處理業(yè)務(wù)
        v++

        // 如果key的值沒有改變的話,Pipelined函數(shù)才會調(diào)用成功
        _, err = tx.Pipelined(ctx, func(pipe redis.Pipeliner) error {
            // 在這里給key設(shè)置最新值
            pipe.Set(ctx, "key", v, 0)
            return nil
        })
        return err
    }

    // 使用Watch監(jiān)聽一些Key, 同時綁定一個回調(diào)函數(shù)fn, 監(jiān)聽Key后的邏輯寫在fn這個回調(diào)函數(shù)里面
    // 如果想監(jiān)聽多個key,可以這么寫:client.Watch(ctx,fn, "key1", "key2", "key3")
    rdb.Watch(ctx, fn, "key")
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容