GO-REDIS的一些高級用法

1. 前言

說到Golang的Redis庫,用到最多的恐怕是
redigo 和 go-redis。其中 redigo 不支持對集羣的訪問。
本文想聊聊go-redis 2個高級用法node

2. 開啓對Cluster中Slave Node的訪問

在一個負載比較高的Redis Cluster中,若是容許對slave節點進行讀操做將極大的提升集羣的吞吐能力。git

開啓對Slave 節點的訪問,受如下3個參數的影響github

type ClusterOptions struct {
    // Enables read-only commands on slave nodes.
    ReadOnly bool
    // Allows routing read-only commands to the closest master or slave node.
    // It automatically enables ReadOnly.
    RouteByLatency bool
    // Allows routing read-only commands to the random master or slave node.
    // It automatically enables ReadOnly.
    RouteRandomly bool
    ... 
}

go-redis 選擇節點的邏輯以下redis

func (c *ClusterClient) cmdSlotAndNode(cmd Cmder) (int, *clusterNode, error) {
    state, err := c.state.Get()
    if err != nil {
        return 0, nil, err
    }

    cmdInfo := c.cmdInfo(cmd.Name())
    slot := cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo))

    if c.opt.ReadOnly && cmdInfo != nil && cmdInfo.ReadOnly {
        if c.opt.RouteByLatency {
            node, err := state.slotClosestNode(slot)
            return slot, node, err
        }

        if c.opt.RouteRandomly {
            node := state.slotRandomNode(slot)
            return slot, node, nil
        }

        node, err := state.slotSlaveNode(slot)
        return slot, node, err
    }

    node, err := state.slotMasterNode(slot)
    return slot, node, err
}
  • 若是ReadOnly = true,只選擇Slave Node
  • 若是ReadOnly = true 且 RouteByLatency = true 將從slot對應的Master Node 和 Slave Node選擇,選擇策略爲: 選擇PING 延遲最低的節點
  • 若是ReadOnly = true 且 RouteRandomly = true 將從slot對應的Master Node 和 Slave Node選擇,選擇策略爲:隨機選擇

3. 在集羣模式下使用pipeline功能

Redis的pipeline功能的原理是 Client經過一次性將多條redis命令發往Redis Server,減小了每條命令分別傳輸的IO開銷。同時減小了系統調用的次數,所以提高了總體的吞吐能力。算法

咱們在主-從模式的Redis中,pipeline功能應該用的不少,可是Cluster模式下,估計尚未幾我的用過。
咱們知道 redis cluster 默認分配了 16384 個slot,當咱們set一個key 時,會用CRC16算法來取模獲得所屬的slot,而後將這個key 分到哈希槽區間的節點上,具體算法就是:CRC16(key) % 16384。若是咱們使用pipeline功能,一個批次中包含的多條命令,每條命令涉及的key可能屬於不一樣的slot併發

go-redis 爲了解決這個問題, 分爲3步
源碼能夠閱讀 defaultProcessPipeline
1) 將計算command 所屬的slot, 根據slot選擇合適的Cluster Node
2)將同一個Cluster Node 的全部command,放在一個批次中發送(併發操做)
3)接收結果dom

注意:這裏go-redis 爲了處理簡單,每一個command 只能涉及一個key, 不然你可能會收到以下錯誤code

err CROSSSLOT Keys in request don't hash to the same slot

也就是說go-redis不支持相似 MGET 命令的用法ip

一個簡單的例子get

package main

import (
    "github.com/go-redis/redis"
    "fmt"
)

func main() {
    client := redis.NewClusterClient(&redis.ClusterOptions{
        Addrs: []string{"192.168.120.110:6380", "192.168.120.111:6380"},
        ReadOnly: true,
        RouteRandomly: true,
    })

    pipe := client.Pipeline()
    pipe.HGetAll("1371648200")
    pipe.HGetAll("1371648300")
    pipe.HGetAll("1371648400")
    cmders, err := pipe.Exec()
    if err != nil {
        fmt.Println("err", err)
    }
    for _, cmder := range cmders {
        cmd := cmder.(*redis.StringStringMapCmd)
        strMap, err := cmd.Result()
        if err != nil {
            fmt.Println("err", err)
        }
        fmt.Println("strMap", strMap)
    }
}
相關文章
相關標籤/搜索