go-redis的应用
前言感悟
笔者先是学习了redis的基础知识,然后再学习go-redis的,但是我搜寻了网络中的go-redis的教程,好像没有这么讲解。都是在使用,没有特别的讲解。
-
是因为没有像go等语言系统那么有庞杂的知识库
-
作为一个导入的包使用,就像go中的其他导入包一样,你懂怎么使用就行,了解基本用法,有需要再像官网查询。
这里提供一下go redis中文官网Golang Redis客户端 (uptrace.dev)
再看一下官方文档的目录
感觉是在简单进行存储的进阶版,需要对于安全等性能问题提出要求时,会使用,如哨兵等。
但是对于笔者而言,这些过于高端,我暂时还是先理清后端大项目的基本流程,如gin,gorm,现在的redis,后面的docker部署等。
安装下载go-redis包
如果自身没有redis的包,请先在终端运行命令下载
go get github.com/redis/go-redis/v9
存储相关的简单操作
简单示例
func main() {
//上下文
ctx := context.Background()
//设置key
err := rds.Set(ctx, "greet", "hello world", 10*time.Second).Err()
if err != nil {
panic(err)
}
//用get获取数据
val, err := rds.Get(ctx, "greet").Result()
if err != nil {
panic(err)
}
fmt.Println("greet is ", val)
//利用命令获取数据
result, err := rds.Do(ctx, "get", "greet").Result()
if err != nil {
panic(err)
}
fmt.Println("greet is ", result.(string))
}
确保你的redis服务器时打开的,不然会出现错误。
不同类型下的操作
因为redis可以存储常见的string,hash,list,set,zset,还有新版本HyperLogLog 结构。所以下面分别介绍常见的类型相关的函数信息。有需要可以看笔者介绍redis的博客简单了解redis及其相关操作_法耶会输出的博客-CSDN博客。
其实大多时根据redis命令来进行命名的,所以熟悉redis的人应该看函数名就能知道函数的作用。在go-redis中,只是函数内部参数改变,如果使用goland这种集成开发环境下,他会给予提示,所以看看理解就行。
string类型
-
set设置值
//设置key err := rds.Set(ctx, "greet", "hello world", 10*time.Second).Err()
-
get获取值
//用get获取数据 val, err := rds.Get(ctx, "greet").Result()
-
GetSet设置key的值,并返回key的旧值
//用getset来返回旧值 oldVal, err := rds.GetSet(ctx, "greet", "hello,my lover").Result() fmt.Println("old greet is ", oldVal)
-
SetNx 不存在该键就设置该key对应的值
-
MSet,Mget–设置多个key,获取多个key对应的value。
//批量设置key的值
err := rdb . MSet(ctx, "key1", "value1", "key2", "value2", "key3", "value3").Err()
//批量查询key的值
vals,err := rdb . MGet(ctx,"key1", "key2", "key3") . Result()
- del 删除
rds.Del(ctx,"greet")
其他类型
hash
类型就是在常见string的命令上添加了H。就不一一介绍了。
接着介绍**list
类型**
函数名 | 参数 | 作用描述 |
---|---|---|
LPush | key string, values …string | 将一个或多个元素插入到列表的左侧。 |
RPush | key string, values …string | 将一个或多个元素插入到列表的右侧。 |
LPop | key string | 从列表的左侧弹出并返回一个元素。 |
RPop | key string | 从列表的右侧弹出并返回一个元素。 |
LRange | key string, start int64, stop int64 | 获取列表中指定范围内的元素。 |
LLen | key string | 获取列表的长度(即元素个数)。 |
LTrim | key string, start int64, stop int64 | 修剪列表,保留指定范围内的元素,移除其他元素。 |
LRem | key string, count int64, value string | 从列表中删除指定数量的匹配元素。 |
LIndex | key string, index int64 | 获取列表中指定索引位置的元素。 |
LSet | key string, index int64, value string | 设置列表中指定索引位置的元素值。 |
set
类型函数如下:
函数名 | 参数 | 作用描述 |
---|---|---|
SAdd | key string, members …string | 向集合中添加一个或多个成员。 |
SRem | key string, members …string | 从集合中移除一个或多个成员。 |
SCard | key string | 获取集合的基数(集合中成员的数量)。 |
SMembers | key string | 获取集合中的所有成员。 |
SIsMember | key string, member string | 检查一个成员是否存在于集合中。 |
SRandMember | key string, count int64 | 从集合中随机获取指定数量的成员,可重复。 |
SPop | key string | 从集合中随机弹出并返回一个成员。 |
SInter | keys …string | 返回给定多个集合的交集。 |
SUnion | keys …string | 返回给定多个集合的并集。 |
SDiff | keys …string | 返回给定多个集合的差集。 |
SScan | key string, cursor uint64, match string, count int64 | 迭代遍历集合中的元素。 |
讲解一下SScan
函数,使用该函数可以有效地遍历和处理大型集合中的元素,而无需一次性加载整个集合到内存中。这对于处理大规模的集合数据非常有用,同时也可以减轻Redis服务器的负载。
var cursor uint64 = 0
var count int64 = 10
for {
results, nextCursor, err := client.SScan("myset", cursor, "", count).Result()
if err != nil {
// 处理错误
}
// 处理返回的元素 results
// 更新游标
cursor = nextCursor
// 当游标为0时,表示遍历完成
if cursor == 0 {
break
}
}
zset类型
函数名 | 参数 | 作用描述 |
---|---|---|
ZAdd | key string, members …*Z | 向有序集合中添加一个或多个成员。 |
ZRem | key string, members …interface{} | 从有序集合中移除一个或多个成员。 |
ZCard | key string | 获取有序集合的基数(集合中成员的数量)。 |
ZScore | key string, member string | 获取有序集合中指定成员的分值。 |
ZRange | key string, start int64, stop int64, withScores bool | 按照排名范围获取有序集合中的成员。 |
ZRevRange | key string, start int64, stop int64, withScores bool | 按照逆序排名范围获取有序集合中的成员。 |
ZRangeByScore | key string, opt *ZRangeBy | 按照分值范围获取有序集合中的成员。 |
ZRevRangeByScore | key string, opt *ZRangeBy | 按照逆序分值范围获取有序集合中的成员。 |
ZRank | key string, member string | 获取有序集合中指定成员的排名(按照分值从小到大的顺序)。 |
ZRevRank | key string, member string | 获取有序集合中指定成员的逆序排名(按照分值从大到小的顺序)。 |
ZIncrBy | key string, increment float64, member string | 将有序集合中指定成员的分值增加指定的增量值。 |
ZCount | key string, min, max string | 统计有序集合中分值在指定范围内的成员数量。 |
ZRemRangeByRank | key string, start, stop int64 | 移除有序集合中排名在指定范围内的成员。 |
ZRemRangeByScore | key string, min, max string | 移除有序集合中分值在指定范围内的成员。 |
ZScan | key string, cursor uint64, match string, count int64 | 迭代遍历有序集合中的元素。 |
发布订阅操作
以下是go-redis库中常见的用于实现Redis发布订阅(Pub/Sub)功能的函数名、参数和作用描述的Markdown表格:
函数名 | 参数 | 作用描述 |
---|---|---|
Subscribe | channels …string | 订阅一个或多个频道,接收对应频道的消息。 |
PSubscribe | patterns …string | 订阅一个或多个匹配模式,接收匹配的频道的消息。 |
Unsubscribe | channels …string | 取消订阅一个或多个频道,停止接收对应频道的消息。 |
PUnsubscribe | patterns …string | 取消订阅一个或多个匹配模式,停止接收匹配的频道的消息。 |
Publish | channel string, message interface{} | 向指定频道发布一条消息。 |
Ping | 检查与Redis服务器的连接状态。 |
这些函数用于实现发布订阅模式,其中:
Subscribe
函数用于订阅一个或多个频道,客户端会接收订阅的频道上发布的消息。PSubscribe
函数用于订阅一个或多个匹配模式,客户端会接收匹配的频道上发布的消息。Unsubscribe
函数用于取消订阅一个或多个频道,停止接收对应频道的消息。PUnsubscribe
函数用于取消订阅一个或多个匹配模式,停止接收匹配的频道的消息。Publish
函数用于向指定频道发布一条消息,订阅该频道的客户端会接收到该消息。Ping
函数用于检查与Redis服务器的连接状态。
下面讲解一个简单的Redis发布订阅的示例,通过使用Subscribe
、Publish
以及相应的接收消息的逻辑,实现了消息的发布和订阅功能。
1.订阅通道,并接受消息(receiver.go文件
func main() {
ctx := context.Background()
rds := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
})
//先订阅该通道
sub := rds.Subscribe(ctx, "channel1", "channel2")
for {
message, err := sub.ReceiveMessage(ctx)
if err != nil {
panic(err)
}
fmt.Printf("received message from %s,%s", message.Channel, message.Payload)
}
}
2.另一个文件的发送消息(publisher.go文件
func init() {
rds = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "",
DB: 0,
})
}
// 采用通道来发送消息
func main() {
ctx := context.Background()
//发送消息
rds.Publish(ctx, "channel1", "hello,my lover!")
}
最后运行,可以看到在receiver.go文件终端出现
注意
:订阅频道过多也可能会导致 Redis 服务器的性能下降,因此需要根据实际情况来设计订阅和发布系统,以保证系统的可扩展性和高性能。
事务处理
事务:一次性执行多行命令,当其中部分命令执行出现错误,不会造成其他命令的放弃执行。所以会造成事务不具备原子性
(要么都执行,要么都不执行,不可再分
在go-redis中,如果想要事务不受其他客户端命令的打扰,可以采用TxPipeline
方式,相较于pipeline
这是提供了一种将多个 Redis 命令打包到一个事务中的方式。这样可以减少网络往返的次数,
func main() {
rds = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
Password: "",
DB: 0,
})
ctx := context.Background()
pipe := rds.TxPipeline()
pipe.Set(ctx, "Name", "crystal", 0)
pipe.Get(ctx, "Name")
pipe.SAdd(ctx, "dormitory 622", []string{"wst", "zjy", "fhy", "prl"})
pipe.SMembers(ctx, "dormitory 622")
//开始执行
cmders, err := pipe.Exec(ctx)
if err != nil {
panic(err)
}
fmt.Println("Result of Name:", cmders[1].(*redis.StringCmd).Val())
var strings []string
strings = cmders[3].(*redis.StringSliceCmd).Val()
for _, s := range strings {
fmt.Println("Result of 622:", s)
}
}
得到结果
下面补充一下相关 cmders, err := pipe.Exec(ctx)
得到的cmder[1]等的类型转换:
命令类型 | 返回形式 |
---|---|
*redis.StringCmd | 返回字符串结果如 GET、SET、HGET、HSET 等 |
*redis.BoolCmd | 返回布尔值结果EXISTS、SISMEMBER、DEL 等 |
*redis.FloatCmd | 返回浮点数结果,如 HINCRBYFLOAT、INCRBYFLOAT |
*redis.StringSliceCmd | 返回字符串切片结果如 SMEMBERS、ZRANGE、LRANGE 等。 |
*redis.StringStringMapCmd | 返回字符串键值对结果如 HGETALL、CONFIG GET 等 |
*redis.DurationCmd | 返回时间间隔结果如 TTL、PTTL |
*redis.Cmd | 通用返回类型,需手动转换结果 |
请注意,最后一行的 *redis.Cmd
是一个通用的返回类型,需要根据具体情况手动转换结果。这些类型只是 Go Redis 客户端库中的一些常见命令类型,实际上还有其他类型可用于处理特定的命令和结果类型。
结束:我觉得如果只是简单的使用redis,这跟上一篇我写的关于redis的介绍相同,几乎没啥区别,所以真正发挥作用,还是redis的进阶~
urationCmd | 返回时间间隔结果如 TTL、PTTL | |
*redis.Cmd` | 通用返回类型,需手动转换结果 |
请注意,最后一行的 *redis.Cmd
是一个通用的返回类型,需要根据具体情况手动转换结果。这些类型只是 Go Redis 客户端库中的一些常见命令类型,实际上还有其他类型可用于处理特定的命令和结果类型。
结束:我觉得如果只是简单的使用redis,这跟上一篇我写的关于redis的介绍相同,几乎没啥区别,所以真正发挥作用,还是redis的进阶~