版权所有,转载请注明原文链接。
谷歌的go-redis@v6.15.7对redis cluster集群提供了支持,这也是相对于redigo的一个重要优势,后者目前还没有提供集群支持。go-redis@v6.15.7的主要功能点如下:
1.客户端缓存了每一组连续的slot所对应的起止slotID和存储节点列表,以提高一次性访问的成功率,避免MOVED重定向。
type clusterSlot struct {
start, end int
nodes []*clusterNode
}
2.如果执行命令时服务端返回MOVED错误,则客户端通过cluster slots命令重新获取节点信息
//如果是MOVED类型错误,则更新节点信息
func (c *ClusterClient) checkMovedErr(
cmd Cmder, err error, failedCmds *cmdsMap,
) bool {
moved, ask, addr := internal.IsMovedError(err)
if moved {
c.state.LazyReload()
...
//向节点发送CLUSTER SLOTS命令
func (c *ClusterClient) loadState() (*clusterState, error) {
...
slots, err := node.Client.ClusterSlots().Result()
...
3. 管理一组redis.Client,每一个redis.Client独立创建一个连接池。
4. 对key执行命令时,先计算key通过CRC16转换后的值然后用16384取余,从而得到一个0-16383的slotID。然后找到对应的主从节点列表,列表第一个元素是主节点。
//通过二分查找来找到slotID对应的节点列表
func (c *clusterState) slotNodes(slot int) []*clusterNode {
i := sort.Search(len(c.slots), func(i int) bool {
return c.slots[i].end >= slot
})
if i >= len(c.slots) {
return nil
}
x := c.slots[i]
if slot >= x.start && slot <= x.end {
return x.nodes
}
return nil
}
5. 可以通过配置来指定只读操作是否可以在从节点上执行,还可以进一步指定在多个从节点之间随机选取或者按Ping时间最短来选取。其中Ping命令是另起一个go routine来计算,Ping 10次求平均。
//10次Ping求平均
func (n *clusterNode) updateLatency() {
const probes = 10
var latency uint32
for i := 0; i < probes; i++ {
start := time.Now()
n.Client.Ping()
probe := uint32(time.Since(start) / time.Microsecond)
latency = (latency + probe) / 2
}
atomic.StoreUint32(&n.latency, latency)
}
6. 批量执行多条命令时,客户端先计算各条命令对应的slotID,然后把slotID相同的命令放到一起发送给对应节点批量执行,不同slotID之间是并行执行的。在计算slotID时,采用的是每条命令的第一个key,也就是说含有多个key的命令有可能会导致MOVED重定向,从而影响效率。
//取第一个key计算slotID
func cmdSlot(cmd Cmder, pos int) int {
if pos == 0 {
return hashtag.RandomSlot()
}
firstKey := cmd.stringArg(pos)
return hashtag.Slot(firstKey)
}
7. 经验证,可以支持在客户端redis写操作达到1000QPS负载的情况下,动态增加节点,期间性能几乎没有什么影响。(服务端5.0.8,客户端 v6.15.7)。因为,集群每次只是移动16384个slot中的一个,而被移动的slot也只是造成客户端多一次MOVED重定向,所以总体性能影响很小。
8. 经验证,可以支持在客户端redis写操作达到1000QPS负载的情况下,人为挂掉一个主节点,则集群自动切换主从,客户端自动更新节点信息表,期间性能几乎没有什么影响,客户端也没有出现报错。(服务端5.0.8,客户端 v6.15.7)。
9. 启动时不会建立到节点的连接,在需要执行命令时才建立连接。
集群设计原理:《Redis Cluster SpecificationRedis Cluster Specification》
相关文章:
《Go语言:谷歌的go-redis模块面向redis cluster集群的客户端参数配置》
《Go语言:go-redis客户端对sentinel模式下(非集群cluster)redis-server主从切换的支持》