這幾天在看redis集群,之前官方沒有集群方案,大多都是twitter的twemproxy和豌豆莢的codis,redis3.x版本開始支持集群。
redis cluster的設計重點是去中心化,去中間件,每個節點都是平等的,都和其他所有節點連接,保存着自己的數據和集群狀態。
關注點在性能(p2p而非proxy方式、異步復制、客戶端重定向,犧牲了部分一致性)、水平擴展(1000節點)、可用性(之前高可用方案是redis sentinel,集群自動具有監控功能)。
為了保證數據的高可用性,加入了主從模式,一個主節點對應一個或多個從節點,主節點負責數據存取,而從節點通過異步復制的方式(發生寫操作時,客戶端寫入master,master返回ok給client,master更新到slave,問題在於如果第二步后主節點掛掉,那么從節點沒來得更新就被選為主節點,會造成數據不一致)從主節點拉取數據,做備份,一旦主節點掛掉,通過選舉的方式(其他主節點投票)找出一個從節點做新的主節點,如果沒有從節點來接替,集群就會掛掉。
在分片方面,redis cluster將鍵空間分為16384個哈希槽,同時通過crc16算法來計算key屬於哪個槽:HASH_SLOT = CRC16(key) mod 16384,然后將key分配到這個哈希槽所在的節點上。至於哈希槽跟節點的分配,比如三個節點的話,就平均分為三份,節點1覆蓋0-5460、節點2覆蓋5461-10922、節點3覆蓋10923-16383;如果新加入一個節點,那么分配時就會分別從前面3個節點截取相同部分,分配到新節點。
接下來就是詳細的配置了(在騰訊雲上試的,系統CentOS 7.2 64位)
首先下載redis3.x版本,之前的不支持集群
解壓安裝
新建集群目錄cluster
因為cluster至少要有三個節點才可運行,建六個節點,三主三從,建六個文件夾,7000-7005
將redis.conf文件復制到六個文件夾下,並修改
port 7000 //7000-7005
cluster-enabled yes //開啟集群
cluster-config-file nodes.conf //保存節點配置,自動創建,自動更新
cluster-node-timeout 5000 //集群超時時間,節點超過這個時間沒反應就斷定是宕機
appendonly yes //存儲方式,aof,將寫操作記錄保存到日志中
啟動六個節點,命令:redis-server redis.conf
查看redis情況,命令:ps -ef|grep redis
redis cluster的集群管理功能沒有寫到redis代碼中,而是用redis-trib的管理腳本,redis-trib依賴ruby和rubygems以及redis的擴展,所以正式建立集群之前要把這三個裝上。
yum install ruby
yum install rubygems
gem install redis -v 3.0.0
環境安裝完,就可以繼續了
./redis-trib.rb create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
其中--replicas 1是指每個主節點的從節點數目是1,默認前面的是主節點,這樣主節點就是7000-7002,從節點就是7003-7004。
可以查看集群狀態,命令 ./redis-trib.rb check 127.0.0.1:7000 //這里可以是集群中的任意一個端口
接下來就是連接集群了,redis-cli是默認客戶端工具,-c是連接集群
redis-cli -c -p 7000
set test "hello"
之前提到過分配數據時是通過crc16算法來計算的,所以這個slot=crc16(test)mod 16384,假裝slot結果是在5461-10922之間,也就是port7001上。
那么這時集群就會重定向跳轉到7001,然后把test存到7001端口。
這時get test,也會跳轉到7001。
接下來就是添加刪除新的節點了,假如新節點端口是7008和7009,先開啟節點。
然后命令,./redis-trib.rb add-node 127.0.0.1:7008 127.0.0.1:7000
后面的7000可以是集群內任意一個,同上,也同下
節點添加進去默認是master,但這時雖然添加了,但7008並沒有分配到slot,slot是0,所以還要手動做遷移(這也算是redis cluster的一個缺點,不能自動發現新節點,不能自動遷移,全都要手動)。
./redis-trib.rb reshard 127.0.0.1:7000 //同上
這時會出現提示,How many slots do you want to move (from 1 to 16384)?
16384/4=4096,輸入4096
又是提示,What is the receiving node ID?
前面會有7008的id,挺長的,復制過來
再然后,
Please enter all the source node IDs.
Type 'all' to use all the nodes as source nodes for the hash slots.
Type 'done' once you entered all the source nodes IDs.
Source node #1:
也就是說slot的源節點從哪些取,可以輸入特定id,也可以輸入all,即表示從現在所有的主節點平均取,輸入all
這樣就可以了,7008就覆蓋了0-1364,5461-6826,10923-12287,分別從前三個主節點里面取的。
或者直接一條命令
./redis-trib.rb reshard --from <node-id> --to <node-id> --slots <number of slots> --yes <host>:<port>
然后將7009加進去,因為是想讓7009做7008的從節點,所以命令有變化:
./redis-trib.rb add-node --slave --master-id (7008id) 127.0.0.1:7009 127.0.0.1:7000
--slave表示作為從節點加入,--master-id也就是指定他的主節點id。
PS:可在某從節點下通過cluster replicate <new-master-node-id> 使其更換主節點
接下來是刪除節點,
./redis-trib.rb del-node 127.0.0.1:7000 (7008id)
如果7008中有數據,那就要麻煩一點,再重新分片,將7008的slot分配給其他節點,然后再刪除。但是如果是刪除從節點的話,就不用重新分片。
而剩下的7009會自動給被分配的節點做從節點。
有種情況是宕機了,比如7001死掉了,那么7004會被選舉為新的主節點,get test時會跳轉到7004,如果后面7001又啟動了,那么他會作為7004的從節點。
redis cluster 有幾個問題,一是一致性,暫時沒看到有什么解決方案,或者client寫操作時,更新到slave成功后才給client發ok?可是這樣對性能就有影響,尤其當有多個從節點時。
再就是不支持多key操作,如果多個key分配在不同節點上,就會悲劇,看到的解決方法是hash tag方案(即對多個key的相同部分做hash),這樣就可以保證他們進的是同一節點。
還有負載均衡,從節點只負責異步備份,負載不均。
參考:Redis集群研究和實踐(基於redis 3.0.5)