当前位置: 首页 > 工具软件 > x-redis > 使用案例 >

Go语言:谷歌go-redis模块面向redis cluster集群的客户端的主要功能点及性能

谢墨竹
2023-12-01

版权所有,转载请注明原文链接。

谷歌的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主从切换的支持》

 类似资料: