likes
comments
collection
share

golang连接redis集群遇见的坑

作者站长头像
站长
· 阅读数 20

背景:redis集群

  1. 执行lua脚本时

不同于单机情况,lua脚本在执行时需要确保key在同一个node节点上,换句话说也就是需要保证 slot=crc16%16384,通过对key进行hash运算,其slot会分布在同一个node节点所属范围。

golang连接redis集群遇见的坑

解决思路:

为了使key都落在一个node节点上可以通过添加 {} hashTag来实现。

原理:

相同的hashtag被分配到相同的节点,相同的槽。
hash算法采用crc16。crc16算法为redis自己封装的,源码位置:https://github.com/redis/redis/blob/6.2.6/src/crc16.c。
  1. golang选择客户端

一开始复用了项目里存在的golang客户端 即采用的 go-redis

github.com/go-redis/redis v6.15.9+incompatible

项目上线后通过granfa监控打点观测到redis的集群远超预想 golang连接redis集群遇见的坑

从上面图形可以看出redis集群的p98响应在k级毫秒以上,其中间断是因为依次排除怀疑的点[qps为300,属实比较低了:scream: ]

  • lua脚本太复杂导致redis执行较慢(:sweat: 吃了知识点掌握不牢固的亏,仅用 exists 进行测试发现无变化 )
  • 怀疑集群存在某节点故障(:japanese_goblin: 换成单节点验证发现 无变化)
  • 怀疑命中key的概率低的问题(:pleading_face: 换成redis中存在的key验证 无变化)
  • 将问题还是放在了redis集群上 ,还好此集群未正式使用(也是敢压测的原因[:eyes: 要对线上环境抱有敬畏之心:eyes:])

执行下述命令进行redis的压测:

redis-benchmark -h 127.0.0.1 -p 6379 -t set,get -n 10000 -q
redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60

golang连接redis集群遇见的坑 golang连接redis集群遇见的坑

从压测数据来看 latency 平均为15.5906ms,观测还可行。而其set命令平均不到3k,对集群还是有所怀疑【有可能网络带宽非本地原因,此处无确定结论】,对比了另外一组redis集群发现无影响

  • 集群没问题,命令也简单(大佬的帮助下开始测试golang客户端的问题)
原使用的客户端
1、mod文件
github.com/go-redis/redis v6.15.9+incompatible

2、部分代码【注意6.15.9的时候SMembers 无上下文概念】
//构建客户端
ClusterPool := redis2.NewClusterClient(&redis2.ClusterOptions{
	Addrs: strings.Split(addr,","),
})
	
//执行命令并统计耗时
fmt.Println("时间redis cluster client begin:", time.Now())
testData := ClusterPool.SMembers("798176a1b809edaf956bb2c6ceda9280")
fmt.Println("时间redis cluster client end:", time.Now())
	
fmt.Println(testData) //为了验证成功输出数据

原使用的客户端升级下
1、mod文件
github.com/go-redis/redis/v8 v8.11.5


2、部分代码【此时SMembers 存在上下文了】
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
//构建客户端
ClusterPool := redis2.NewClusterClient(&redis2.ClusterOptions{
	Addrs: strings.Split(addr,","),
})
	
//执行命令并统计耗时
fmt.Println("时间redis cluster client begin:", time.Now())
testData := ClusterPool.SMembers(ctx,"798176a1b809edaf956bb2c6ceda9280")
fmt.Println("时间redis cluster client end:", time.Now())
	
fmt.Println(testData) //为了验证成功输出数据

Nice的客户端
1、mod文件


2、部分代码
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
//构建客户端
cluster, _ := (radix.ClusterConfig{}).New(ctx, strings.Split(addr,","))

//执行命令并统计耗时
fmt.Println("时间redis cluster client begin:", time.Now())
str:=[]string{}//此处一定要预先知道存储的类型
err:= cluster.Do(ctx, radix.Cmd(&str, "smembers", "798176a1b809edaf956bb2c6ceda9280"))
fmt.Println("时间redis cluster client end:", time.Now())

fmt.Println(testData,err) //为了验证成功输出数据

golang连接redis集群遇见的坑 golang连接redis集群遇见的坑

上述两图分别为 radix 与 go—redis(v6),radix 与 go-redis(v8),实验证明 v8是远强于v6的,但是与radix还是有10倍之差

友情提示,选用客户端的时候可以依照官方推荐的包

经过上述疑问点的排除法,最后采用radix的客户端,其耗时如下: golang连接redis集群遇见的坑

从上图可以看到redis集群在10k请求的情况下,p98为1.4ms左右,符合预期。大工高成~👺👺👺

程序猿的快乐就是如此简单了~,开心👌