天天看點

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,連接配接位址和端口,密碼,資料庫名稱
	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()
	//建立上下文
	ctx := context.Background()
	//set方法設定key和value,處理傳回的錯誤,參數(上下文,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是連接配接的配置,是一個結構體類型,以下是配置選項和說明

type Options struct {
  // 網絡類型:[ tcp , unix ]
  // 預設是 tcp
  Network string

  // host:port 位址
  Addr string

  // 要使用的 TLS 配置。 當設定 TLS 時将協商。
  TLSConfig *tls.Config
  //建立一個新的連接配接,優先于Newwork和Addr選項
  Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
  // 建立一個redis連接配接的時候,會回調這個函數
  OnConnect func(ctx context.Context, cn *Conn) error
  // 當連接配接到使用 Redis ACL 系統的 Redis 6.0 或更高版本的執行個體時,
  // 使用指定的 使用者名 對目前連接配接進行身份驗證  (ACL 清單中定義的連接配接之一)。
  Username string

  // 可選密碼。 
  // 必須與 requirepass 伺服器配置選項中指定的密碼(如果連接配接到 Redis 5.0 或更低版本的執行個體)
  // 或 連接配接到使用 Redis ACL 系統的 Redis 6.0 或更高版本的執行個體時的使用者密碼 比對。
  Password string

  // 連接配接到伺服器後要選擇的資料庫。
  DB int

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

  // ======連接配接逾時、讀逾時、寫逾時======
  // 建立新連接配接的撥号逾時。
  // 預設為 5 秒。
  DialTimeout time.Duration
  // 套接字讀取逾時。 
  // 如果達到,指令将失敗并逾時而不是阻塞。
  // 使用值 -1 表示無逾時,使用 0 表示預設值。
  // 預設為 3 秒。
  ReadTimeout time.Duration
  // 套接字寫入逾時。 
  // 如果達到,指令将失敗并逾時而不是阻塞。
  // 預設為 ReadTimeout。
  WriteTimeout time.Duration

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

  // 最大套接字連接配接數。
  // 預設為每個可用 CPU 10 個連接配接,由 runtime.GOMAXPROCS 報告。  
  PoolSize int

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

  // 用戶端退出(關閉)連接配接的連接配接年齡。
  // 預設是不關閉老化的連接配接。
  MaxConnAge time.Duration

  // 如果所有連接配接都忙,則用戶端在傳回錯誤之前等待連接配接的時間。
  // 預設為 ReadTimeout + 1 秒。
  PoolTimeout time.Duration

  // 用戶端關閉空閑連接配接的時間。
  // 應該小于伺服器的逾時時間。
  // 預設為 5 分鐘。 -1 禁用空閑逾時檢查。
  IdleTimeout time.Duration

  // 空閑連接配接 reaper 進行空閑檢查的頻率。
  // 預設為 1 分鐘。 -1 禁用空閑連接配接reaper,
  // 但如果設定了 IdleTimeout,空閑連接配接仍會被用戶端丢棄。
  IdleCheckFrequency time.Duration
  
  // 在從節點上啟用隻讀查詢。
  readOnly bool

  // 用于實作斷路器或速率限制器的限制器接口。
  Limiter Limiter
}           

基本使用

package main

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

var rdb *redis.Client          //建立redis用戶端執行個體
var ctx = context.Background() //建立上下文           

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 設定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 設定一個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("設定一個key的值,并傳回這個key的舊值:", oldValue)
	return oldValue
}           

SetNX 如果key不存在,則設定這個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 批量設定key的值

//MSet 批量設定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 設定key的過期時間

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

Incr針對一個key的數值進行遞增操作

IncrBy指定每次遞增多少 IncrByFloat 指定每次遞增多少,跟IncrBy的差別是累加的是浮點數

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

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

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

Decr 針對一個key的數值進行遞減操作

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

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

Hash類型的操作方法

内部采用數組+連結清單結構,采用鍊位址法解決哈希沖突。

  • 1. HSet
  • 2. HGet
  • 3. HGetAll
  • 4. HIncrBy
  • 5. HKeys
  • 6. HLen
  • 7. HMGet
  • 8. HMSet
  • 9. HSetNX
  • 10. HDel
  • 11. HExists
// HashMethods Hash 操作方法
func HashMethods() {
	//●  HSet
	// user_1 是hash key,username 是字段名, zhangsan是字段值
	rdb.HSet(ctx, "user_1", "username", "zhangsan", "f1", "f_v1")
	//● 2. HGet 	根據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根據key傳回所有的字段名
	keys := rdb.HKeys(ctx, "user_1")
	fmt.Println(keys)
	//● 6. HLen根據key,查詢hash的字段數量
	i, err := rdb.HLen(ctx, "user_1").Result()
	fmt.Println(i)
	//● 7. HMGet根據key和多個字段名,批量查詢多個hash字段值
	b, err := rdb.HMGet(ctx, "user_1", "f1", "count").Result()
	fmt.Println(b)
	//● 8. HMSet根據key和多個字段名和字段值,批量設定hash字段值
	// 初始化hash資料的多個字段值
	data := make(map[string]interface{})
	data["id"] = 1
	data["username"] = "lisi"
	// 一次性儲存多個hash字段值
	rdb.HMSet(ctx, "key", data).Err()

	//● 9. HSetNX如果field字段不存在,則設定hash字段值
	rdb.HSetNX(ctx, "user_1", "f2", "f2value")
	//● 10. HDel根據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的操作方法

  • 1. LPush
  • 2. LPushX
  • 3. RPop
  • 4. RPush
  • 5. RPushX
  • 6. LPop
  • 7. LLen
  • 8. LRange
  • 9. LRem
  • 10. LIndex
  • 11. LInsert
//ListOperateMethods List操作方法
func ListOperateMethods() {
	//● 1. LPush 添加到list的左側,LPush支援一次插入一個或者任意個資料
	rdb.LPush(ctx, "w1", "w2", "w3", "w4", "w")
	//● 2. LPushX 跟LPush的差別是,僅當清單存在的時候才插入資料,用法完全一樣。
	rdb.LPushX(ctx, "w1", "w2", "w3", "w4", "w")
	//● 3. RPop從清單的右邊删除第一個資料,并傳回删除的資料
	rdb.RPop(ctx, "w1")
	//● 4. RPush
	rdb.RPush(ctx, "w1", "wmq", "wmq2")
	//● 5. RPushX 跟RPush的差別是,僅當清單存在的時候才插入資料, 他們用法一樣
	rdb.RPushX(ctx, "w1", "wm3", "w3")
	//● 6. LPop從清單左邊删除第一個資料,并傳回删除的資料
	val, _ := rdb.LPop(ctx, "w1").Result()
	fmt.Println(val)
	//● 7. LLen傳回清單的大小
	lLen, _ := rdb.LLen(ctx, "w1").Result()
	fmt.Println(lLen)
	//● 8. LRange傳回清單的一個範圍内的資料,也可以傳回全部資料
	result, _ := rdb.LRange(ctx, "w1", 0, lLen).Result()
	fmt.Println(result)
	//● 9. LRem删除清單中的資料 從清單左邊開始,删除100, 如果出現重複元素,僅删除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是無序且不會重複的字元串集合 set和list的差別是set不包含重複的元素

  • 1. SAdd
  • 2. SCard
  • 3. SIsMember
  • 4. SMembers
  • 5. SRem
  • 6. SPop,SPopN
//	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("傳回删除的個數", 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操作方法

有序的,非重複的的字元串集合

  • 1. ZAdd
  • 2. ZCard
  • 3. ZCount
  • 4. ZIncrBy
  • 5. ZRange,ZRevRange
  • 6. ZRangeByScore
  • 7. ZRevRangeByScore
  • 8. ZRangeByScoreWithScores
  • 9. ZRem
  • 10. ZRemRangeByRank
  • 11.ZRemRangeByScore
  • 12. ZScore
  • 13. ZRank

釋出訂閱

Redis提供了釋出訂閱功能,可以用于消息的傳輸,Redis的釋出訂閱機制包括三個部分,釋出者,訂閱者和Channel。

go-redis使用入門

釋出者和訂閱者都是Redis用戶端,Channel則為Redis伺服器端,釋出者将消息發送到某個的頻道,訂閱了這個頻道的訂閱者就能接收到這條消息。

訂閱者 subscriber

//subscriber 訂閱者訂閱channel1的消息
func subscriber() {
	// 訂閱channel1這個channel
	sub := rdb.Subscribe(ctx, "channel1")
	// sub.Channel() 傳回go channel,可以循環讀取redis伺服器發過來的消息
	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)
	}
}           

釋出者 publisher

package main

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

var rdb *redis.Client          //建立redis用戶端執行個體
var ctx = context.Background() //建立上下文
func main() {
	//初始化redis,連接配接位址和端口,密碼,資料庫名稱
	rdb = redis.NewClient(&redis.Options{
		Addr:     "localhost:6379",
		Password: "",
		DB:       0,
	})
	// 将"message"消息發送到channel1這個通道上

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

其他的一些方法

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

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

事務操作

redis事務可以一次執行多個指令, 并且帶有以下兩個重要的保證:

  • 事務是一個單獨的隔離操作:事務中的所有指令都會序列化、按順序地執行。事務在執行的過程中,不會被其他用戶端發送來的指令請求所打斷。
  • 事務是一個原子操作:事務中的指令要麼全部被執行,要麼全部都不執行。

TxPinline

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

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

// 上面代碼等同于執行下面redis指令
//
//     MULTI
//     INCR pipeline_counter
//     EXPIRE pipeline_counts 3600
//     EXEC

// 通過Exec函數送出redis事務
_, err := pipe.Exec(ctx)

// 送出事務後,我們可以查詢事務操作的結果
// 前面執行Incr函數,在沒有執行exec函數之前,實際上還沒開始運作。
fmt.Println(incr.Val(), err)
}           

watch

redis樂觀鎖支援,可以通過watch監聽一些Key, 如果這些key的值沒有被其他人改變的話,才可以送出事務

func watch() {

	// 定義一個回調函數,用于處理事務邏輯
	fn := func(tx *redis.Tx) error {
		// 先查詢下目前watch監聽的key的值
		v, err := tx.Get(ctx, "key").Int()
		if err != nil && err != redis.Nil {
			return err
		}
		// 這裡可以處理業務
		v++

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

	// 使用Watch監聽一些Key, 同時綁定一個回調函數fn, 監聽Key後的邏輯寫在fn這個回調函數裡面
	// 如果想監聽多個key,可以這麼寫:client.Watch(ctx,fn, "key1", "key2", "key3")
	rdb.Watch(ctx, fn, "key")
}
           

繼續閱讀