安裝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。
釋出者和訂閱者都是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)
}
}
其他的一些方法
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")
}