說到Golang的Redis庫,用到最多的恐怕是
redigo 和 go-redis。其中 redigo
不支持對集羣的訪問。
本文想聊聊go-redis
2個高級用法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 }
Slave Node
slot
對應的Master Node
和 Slave Node
選擇,選擇策略爲: 選擇PING
延遲最低的節點slot
對應的Master Node
和 Slave Node
選擇,選擇策略爲:隨機選擇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) } }