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,處理返回的錯(cuò)誤,參數(shù)(上下文,key名,value值,過期時(shí)間)
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方法使用原生命令,返回值是一個(gè)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是連接的配置,是一個(gè)結(jié)構(gòu)體類型,以下是配置選項(xiàng)和說明
type Options struct {
??// 網(wǎng)絡(luò)類型:[ tcp , unix ]
??// 默認(rèn)是 tcp
??Network string
??// host:port 地址
??Addr string
??// 要使用的 TLS 配置。 當(dāng)設(shè)置 TLS 時(shí)將協(xié)商。
??TLSConfig *tls.Config
??//創(chuàng)建一個(gè)新的連接,優(yōu)先于Newwork和Addr選項(xiàng)
??Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
??// 新建一個(gè)redis連接的時(shí)候,會(huì)回調(diào)這個(gè)函數(shù)
??OnConnect func(ctx context.Context, cn *Conn) error
??// 當(dāng)連接到使用 Redis ACL 系統(tǒng)的 Redis 6.0 或更高版本的實(shí)例時(shí),
??// 使用指定的 用戶名 對當(dāng)前連接進(jìn)行身份驗(yàn)證 ?(ACL 列表中定義的連接之一)。
??Username string
??// 可選密碼。
??// 必須與 requirepass 服務(wù)器配置選項(xiàng)中指定的密碼(如果連接到 Redis 5.0 或更低版本的實(shí)例)
??// 或 連接到使用 Redis ACL 系統(tǒng)的 Redis 6.0 或更高版本的實(shí)例時(shí)的用戶密碼 匹配。
??Password string
??// 連接到服務(wù)器后要選擇的數(shù)據(jù)庫。
??DB int
??// ====== 重試、退避時(shí)間======
??// 放棄前的最大重試次數(shù)。
??// 默認(rèn)是 3 次重試; -1(非 0)禁用重試。
??MaxRetries int
??// 每次重試之間的最小退避。
??// 默認(rèn)為 8 毫秒; -1 禁用退避。
??MinRetryBackoff time.Duration
???// 每次重試之間的最大退避。
??// 默認(rèn)為 512 毫秒; -1 禁用退避。
??MaxRetryBackoff time.Duration
??// ======連接超時(shí)、讀超時(shí)、寫超時(shí)======
??// 建立新連接的撥號超時(shí)。
??// 默認(rèn)為 5 秒。
??DialTimeout time.Duration
??// 套接字讀取超時(shí)。
??// 如果達(dá)到,命令將失敗并超時(shí)而不是阻塞。
??// 使用值 -1 表示無超時(shí),使用 0 表示默認(rèn)值。
??// 默認(rèn)為 3 秒。
??ReadTimeout time.Duration
??// 套接字寫入超時(shí)。
??// 如果達(dá)到,命令將失敗并超時(shí)而不是阻塞。
??// 默認(rèn)為 ReadTimeout。
??WriteTimeout time.Duration
??// 連接池的類型。
??// FIFO 池為 true,LIFO 池為 false。
??// 請注意,與 lifo 相比,fifo 的開銷更高。
??PoolFIFO bool
??// 最大套接字連接數(shù)。
??// 默認(rèn)為每個(gè)可用 CPU 10 個(gè)連接,由 runtime.GOMAXPROCS 報(bào)告。 ?
??PoolSize int
??// 建立新連接緩慢時(shí)有用的最小空閑連接數(shù)。
??MinIdleConns int
??// 客戶端退出(關(guān)閉)連接的連接年齡。
??// 默認(rèn)是不關(guān)閉老化的連接。
??MaxConnAge time.Duration
??// 如果所有連接都忙,則客戶端在返回錯(cuò)誤之前等待連接的時(shí)間。
??// 默認(rèn)為 ReadTimeout + 1 秒。
??PoolTimeout time.Duration
??// 客戶端關(guān)閉空閑連接的時(shí)間。
??// 應(yīng)該小于服務(wù)器的超時(shí)時(shí)間。
??// 默認(rèn)為 5 分鐘。 -1 禁用空閑超時(shí)檢查。
??IdleTimeout time.Duration
??// 空閑連接 reaper 進(jìn)行空閑檢查的頻率。
??// 默認(rèn)為 1 分鐘。 -1 禁用空閑連接reaper,
??// 但如果設(shè)置了 IdleTimeout,空閑連接仍會(huì)被客戶端丟棄。
??IdleCheckFrequency time.Duration
??
??// 在從節(jié)點(diǎn)上啟用只讀查詢。
??readOnly bool
??// 用于實(shí)現(xiàn)斷路器或速率限制器的限制器接口。
??Limiter Limiter
}
基本使用
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"time"
)
var rdb *redis.Client ?????????//創(chuàng)建redis客戶端實(shí)例
var ctx = context.Background() //創(chuàng)建上下文
string類型的操作方法
·?Get
·?Set
·?GetSet
·?SetNX
·?MGset
·?MSet
·?Incr,IncrBy
·?Decr,DecrBy
·?Del
·?Expire
Get 獲取key的值,返回值:錯(cuò)誤信息error和value
//get 方法 返回值和錯(cuò)誤信息
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的過期時(shí)間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è)置一個(gè)key的值,并且返回這個(gè)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è)置一個(gè)key的值,并返回這個(gè)key的舊值:", oldValue)
return oldValue
}
SetNX 如果key不存在,則設(shè)置這個(gè)key的值x0;
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 刪除單個(gè)或者多個(gè)key
//delOneKeys 刪除單個(gè)key
func delOneKeys(k string) {
????rdb.Del(ctx, k)
}
//delKeys 刪除多個(gè)key
func delKeys(k ...string) {
rdb.Del(ctx, k...)
}
Expire 設(shè)置key的過期時(shí)間x0;
func expire(k string, t time.Duration) {
rdb.Expire(ctx, k, t)
}
Incr針對一個(gè)key的數(shù)值進(jìn)行遞增操作x0;
IncrBy指定每次遞增多少x0; IncrByFloat 指定每次遞增多少,跟IncrBy的區(qū)別是累加的是浮點(diǎn)數(shù)x0;
//addVal 針對一個(gè)key的數(shù)值進(jìn)行遞增操作
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ū)別是累加的是浮點(diǎn)數(shù)
valFloat, err := rdb.IncrByFloat(ctx, "key1", 2.2).Result()
if err != nil {
panic(err)
}
fmt.Println("最新值", valFloat)
}
Decr 針對一個(gè)key的數(shù)值進(jìn)行遞減操作x0;
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),采用鏈地址法解決哈希沖突。
·?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 根據(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和多個(gè)字段名,批量查詢多個(gè)hash字段值
b, err := rdb.HMGet(ctx, "user_1", "f1", "count").Result()
fmt.Println(b)
//● 8. HMSet根據(jù)key和多個(gè)字段名和字段值,批量設(shè)置hash字段值
// 初始化hash數(shù)據(jù)的多個(gè)字段值
data := make(map[string]interface{})
data["id"] = 1
data["username"] = "lisi"
// 一次性保存多個(gè)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字段
// 刪除一個(gè)字段id
rdb.HDel(ctx, "key", "id")
// 刪除多個(gè)字段
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的左側(cè),LPush支持一次插入一個(gè)或者任意個(gè)數(shù)據(jù)
rdb.LPush(ctx, "w1", "w2", "w3", "w4", "w")
//● 2. LPushX 跟LPush的區(qū)別是,僅當(dāng)列表存在的時(shí)候才插入數(shù)據(jù),用法完全一樣。
rdb.LPushX(ctx, "w1", "w2", "w3", "w4", "w")
//● 3. RPop從列表的右邊刪除第一個(gè)數(shù)據(jù),并返回刪除的數(shù)據(jù)
rdb.RPop(ctx, "w1")
//● 4. RPush
rdb.RPush(ctx, "w1", "wmq", "wmq2")
//● 5. RPushX 跟RPush的區(qū)別是,僅當(dāng)列表存在的時(shí)候才插入數(shù)據(jù), 他們用法一樣
rdb.RPushX(ctx, "w1", "wm3", "w3")
//● 6. LPop從列表左邊刪除第一個(gè)數(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返回列表的一個(gè)范圍內(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次,也就是刪除第一個(gè)
dels, _ := rdb.LRem(ctx, "key", 1, "w1").Result()
fmt.Println(dels)
//● 10. LIndex
// 列表索引從0開始計(jì)算,這里返回第6個(gè)元素
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是無序且不會(huì)重復(fù)的字符串集合 set和list的區(qū)別是set不包含重復(fù)的元素
·?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("返回刪除的個(gè)數(shù)", i)
//● 6. SPop,SPopN 隨機(jī)返回集合中的元素,并且刪除返回的元素
rdb.SPop(ctx, "set_key")
fmt.Println(rdb.SMembers(ctx, "set_key").Result())
// 隨機(jī)返回集合中的一個(gè)元素,并且刪除這個(gè)元素
val, _ := rdb.SPop(ctx,"key").Result()
fmt.Println(val)
// 隨機(jī)返回集合中的5個(gè)元素,并且刪除這些元素
vals, _ := rdb.SPopN(ctx,"key", 5).Result()
fmt.Println(vals)
}
sorted set操作方法
有序的,非重復(fù)的的字符串集合
·?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
發(fā)布訂閱
Redis提供了發(fā)布訂閱功能,可以用于消息的傳輸,Redis的發(fā)布訂閱機(jī)制包括三個(gè)部分,發(fā)布者,訂閱者和Channel。
?
發(fā)布者和訂閱者都是Redis客戶端,Channel則為Redis服務(wù)器端,發(fā)布者將消息發(fā)送到某個(gè)的頻道,訂閱了這個(gè)頻道的訂閱者就能接收到這條消息。
訂閱者 subscriber
//subscriber 訂閱者訂閱channel1的消息
func subscriber() {
// 訂閱channel1這個(gè)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客戶端實(shí)例
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這個(gè)通道上
for i := 1; i <= 100; i++ {
fmt.Println(i)
str := strconv.Itoa(i) + ".message收到前端回答"
rdb.Publish(ctx, "channel1", str)
}
}
?
其他的一些方法
func cancelSub() {
// 訂閱channel1這個(gè)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í)行多個(gè)命令, 并且?guī)в幸韵聝蓚€(gè)重要的保證:
·?事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會(huì)序列化、按順序地執(zhí)行。事務(wù)在執(zhí)行的過程中,不會(huì)被其他客戶端發(fā)送來的命令請求所打斷。
·?事務(wù)是一個(gè)原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部都不執(zhí)行。
TxPinline
//事務(wù)操作
//TxPinline
func Txline() {
// 開啟一個(gè)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ù)之前,實(shí)際上還沒開始運(yùn)行。
fmt.Println(incr.Val(), err)
}
watch
redis樂觀鎖支持,可以通過watch監(jiān)聽一些Key, 如果這些key的值沒有被其他人改變的話,才可以提交事務(wù)
func watch() {
// 定義一個(gè)回調(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ù)才會(huì)調(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, 同時(shí)綁定一個(gè)回調(diào)函數(shù)fn, 監(jiān)聽Key后的邏輯寫在fn這個(gè)回調(diào)函數(shù)里面
// 如果想監(jiān)聽多個(gè)key,可以這么寫:client.Watch(ctx,fn, "key1", "key2", "key3")
rdb.Watch(ctx, fn, "key")
}