Redis详解(三)
一、Redis集群介紹
Clustering:
redis 3.0之后進入生產環(huán)境
分布式數據庫,通過分片機制來進行數據分布,clustering 內的每個節(jié)點,僅有數據庫的一部分數據;
去中心化的集群:
redis集群中的每一個節(jié)點,都可以作為集群的接入節(jié)點。//每一個node都有全局元數據,client訪問哪一個node都可以獲取所有的node
client向任意node發(fā)起請求,該node會返回給client真正的key所在node,然后讓client去獲取。
每一個節(jié)點持有全局元數據,但僅持有部分數據。
redis分布式的常見解決方案:
Twemproxy(Twitter)
Codis(豌豆莢)
Redis Cluster(官方)
Cerberus(芒果TV)
1、Twemproxy特點:
代理分片機制
優(yōu)點:
非常穩(wěn)定,企業(yè)級方案
缺點:
單點故障
需依賴第三方軟件,例如keepalived //進行HA
無法平滑地橫向擴展
沒有后臺界面
代理分片機制引入更多的來回次數并提高延遲
單核模式,無法重新利用多核,除非多實例
Twitter官方內部不再私用Twemproxy
圖1:
2、Codis(豌豆莢)
代理分片機制
2014年開源
基于Go以及C語言研發(fā)
優(yōu)點:
文檔充足,企業(yè)級方案
數據自動平衡
高性能
簡單的測試顯示較Twemproxy快一倍
善用多核CPU
簡單:
沒有paxos類的協(xié)調機制
沒有主從復制
有后臺界面
缺點:
代理分片機制引入更多的來回次數并提高延遲 //更多的ping(pong)操作
需要第三方軟件支持協(xié)調機制 //目前支持Zookeeper及Etcd
不支持主從復制,需要另外實現
Codis采用proxy方案,所以必然會帶來單機性能的損失,
靜測試,在不開pipeline的情況下,大概會損失40%左右的性能
3、Redis cluster(官方)
官方實現
需要Redis 3.0 或更高版本
優(yōu)點:
無中心的p2p Gossip分散式模式
更少的來回次數并降低延遲
自動于多個Redis節(jié)點進行分片,自動均衡
不需要第三方軟件支持協(xié)調機制
缺點:
依賴于Redis 3.0或更高版本
需要時間驗證其穩(wěn)定性
沒有后臺界面
需要智能客戶端 //萬一某個client下線了,client需要知道哪個node在線。
Redis客戶端必須支持Redis cluster架構
較Codis有更多的維護升級版本
4、Cerberus(芒果TV)
優(yōu)點:
數據自動平衡
本身實現了Redis的smart client//server端直接實現了智能client
支持讀寫分離
缺點:
依賴Redis 3.0或更高版本
代理分片機制引入更多的來回次數并增大延遲
需要時間驗證其穩(wěn)定性
沒有后臺界面
codis-proxy : 是客戶端連接的Redis代理服務,codis-proxy 本身實現了Redis協(xié)議,表現得和一個原生的Redis沒什么區(qū)別(就像Twemproxy),對于一個業(yè)務來說,可以部署多個codis-proxy,codis-proxy本身是沒狀態(tài)的。
codis-config :是Codis的管理工具,支持包括,添加/刪除Redis節(jié)點,添加/刪除Proxy節(jié)點,發(fā)起數據遷移等操作,codis-config本身還自帶了一個http server,會啟動一個dashboard,用戶可以直接在瀏覽器上觀察Codis集群的狀態(tài)。
codis-server:是Codis項目維護的一個Redis分支,基于2.8.13開發(fā),加入了slot的支持和原子的數據遷移指令,Codis上層的codis-proxy和codis-config只能和這個版本的Redis交互才能正常運行。
ZooKeeper :用來存放數據路由表和codis-proxy節(jié)點的元信息,codis-config發(fā)起的命令都會通過ZooKeeper同步到各個存活的codis-proxy
二、官方版安裝與實現
1、安裝與實現
redis-trib.rb的ruby腳本。redis-trib.rb是redis官方推出的管理redis集群的工具,集成在redis的源碼src目錄下(redis-xxx/src/)
redis-trib.rb是redis作者用ruby完成的。所以redis集群需要先安裝ruby環(huán)境。
[root@node112 redis]# redis-trib.rb --help
/usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- redis (LoadError)from /usr/share/rubygems/rubygems/core_ext/kernel_require.rb:55:in `require'from /usr/local/bin/redis-trib.rb:25:in `<main>' 需要安裝ruby的client # gem install redis //要求ruby版本2.2之上,假如版本低更新ruby版本如下 # yum install autoconf automake bison libffi-devel libtool readline-devel sqlite-devel libyaml-devel # curl -sSL https://rvm.io/mpapis.asc | gpg --import - # curl -L get.rvm.io | bash -s stable # source /etc/profile.d/rvm.sh //加載RVM環(huán)境。 # rvm reload # rvm requirements run # rvm install 2.4.2 //安裝ruby2.4 設置默認的ruby版本: # rvm list # rvm use 2.4.2 --default # ruby --version [root@node112 ~]# redis-trib.rb create --replicas 1 127.0.0.1:1201 127.0.0.1:1202 127.0.0.1:1203 127.0.0.1:1204 127.0.0.1:1205 127.0.0.1:1206 127.0.0.1:1207 127.0.0.1:1208 Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join....... >>> Performing Cluster Check (using node 127.0.0.1:1201) M: d392057bb258248b09db27e57da53e6277bfbb87 127.0.0.1:1201slots:0-4095 (4096 slots) master1 additional replica(s) M: 8ee780051bba2d62514c3adddb6ad050128309c2 127.0.0.1:1204slots:12288-16383 (4096 slots) master1 additional replica(s) S: 84fc85c03e8cd1d20b794a36091ccfd5235d3403 127.0.0.1:1207slots: (0 slots) slavereplicates 32d3f692b64dd438d5ff9c4729be53c55d3768bb S: e92354f4f158e5c45c508cf494dfd99892437bb4 127.0.0.1:1208slots: (0 slots) slavereplicates 8ee780051bba2d62514c3adddb6ad050128309c2 S: a23562b6abf030b912f950c0e1fe2f576d4e1b8d 127.0.0.1:1205slots: (0 slots) slavereplicates 8251d8d4bde7dd099acb462094d2132b94457481 M: 32d3f692b64dd438d5ff9c4729be53c55d3768bb 127.0.0.1:1203slots:8192-12287 (4096 slots) master1 additional replica(s) S: b31e1d76d83c050c5870caaaf7a6807b3b5c16a3 127.0.0.1:1206slots: (0 slots) slavereplicates d392057bb258248b09db27e57da53e6277bfbb87 M: 8251d8d4bde7dd099acb462094d2132b94457481 127.0.0.1:1202slots:4096-8191 (4096 slots) master1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [root@node112 ~]# [root@node112 ~]# netstat -tunlp |grep -i redis tcp 0 0 127.0.0.1:1201 0.0.0.0:* LISTEN 35965/redis-server tcp 0 0 127.0.0.1:1202 0.0.0.0:* LISTEN 35985/redis-server tcp 0 0 127.0.0.1:1203 0.0.0.0:* LISTEN 35990/redis-server tcp 0 0 127.0.0.1:1204 0.0.0.0:* LISTEN 35995/redis-server tcp 0 0 127.0.0.1:1205 0.0.0.0:* LISTEN 40784/redis-server tcp 0 0 127.0.0.1:1206 0.0.0.0:* LISTEN 40789/redis-server tcp 0 0 127.0.0.1:1207 0.0.0.0:* LISTEN 40794/redis-server tcp 0 0 127.0.0.1:1208 0.0.0.0:* LISTEN 40799/redis-server tcp 0 0 127.0.0.1:11201 0.0.0.0:* LISTEN 35965/redis-server tcp 0 0 127.0.0.1:11202 0.0.0.0:* LISTEN 35985/redis-server tcp 0 0 127.0.0.1:11203 0.0.0.0:* LISTEN 35990/redis-server tcp 0 0 127.0.0.1:11204 0.0.0.0:* LISTEN 35995/redis-server tcp 0 0 127.0.0.1:11205 0.0.0.0:* LISTEN 40784/redis-server tcp 0 0 127.0.0.1:11206 0.0.0.0:* LISTEN 40789/redis-server tcp 0 0 127.0.0.1:11207 0.0.0.0:* LISTEN 40794/redis-server tcp 0 0 127.0.0.1:11208 0.0.0.0:* LISTEN 40799/redis-serverRedis集群不僅需要開通redis客戶端連接的端口,而且需要開通集群總線端口,集群總線端口為redis客戶端連接的端口 + 10000
[root@node112 redis]# redis-cli -h 127.0.0.1 -p 1201 測試: 終端一:訂閱 [root@node112 ~]# redis-cli -h 127.0.0.1 -p 1206 127.0.0.1:1206> SUBSCRIBE mm 終端二:發(fā)布 [root@node112 redis]# redis-cli -h 127.0.0.1 -p 1201 127.0.0.1:1201> PUBLISH mm "test for messages" 在終端一上查看[root@node112 ~]# redis-cli -h 127.0.0.1 -p 1201 127.0.0.1:1201> set hello "hello worlld" OK [root@node112 ~]# redis-cli -h 127.0.0.1 -p 1205 -c //-c使用集群模式 127.0.0.1:1205> get hello -> Redirected to slot [866] located at 127.0.0.1:1201 "hello worlld"2、集群相關命令
redis-cluster把所有的物理節(jié)點映射到[0-16383]個slot(hash槽)上,由cluster負責維護node<->slot<->value
集群選舉容錯:節(jié)點fail選舉是過程是集群中所有master參與,半數以上master與被檢測node監(jiān)測超時(cluster-node-timeout),就認為當前master節(jié)點down
集群不可用:cluster:fail
1.集群任意master掛掉,且當前master沒有slave。集群進入fail狀態(tài),也可以理解成集群的slot映射[0-16838]不完整時進入fail狀態(tài)。
2.集群半數以上master掛掉,收到error clusterdown The Cluster is down的錯誤
集群優(yōu)缺點:
優(yōu)點:
在master節(jié)點下線后,slave節(jié)點會自動提升為master,保證集群可用性
fail node復活后,自動添加到集群中,變成slave節(jié)點。
缺點:
redis的復制使用異步復制機制,auto-fail-over的過程中。集群可能會丟失write命令。然而redis幾乎是同時執(zhí)行(將命令恢復發(fā)送給客戶端,以及將命令復制到slave節(jié)點)這兩個操作。所以實際操作中,丟失的可能性非常小
3、redis-trib.rb命令詳解
redis-trib.rb help //查看幫助信息
redis-trib.rb主要有兩個類:ClusterNode和RedisTrib。ClusterNode保存了每個節(jié)點的信息,RedisTrib則是redis-trib.rb各個功能的實現。
3.1、create:創(chuàng)建集群 //redis-trib.rb create --replicas 1 $host1:port1 $host2:port2 ...
1、首先為每個節(jié)點創(chuàng)建ClusterNode對象,包括連接每個節(jié)點。檢查每個節(jié)點是否為獨立且db為空的節(jié)點。執(zhí)行l(wèi)oad_info方法導入節(jié)點信息。
2、檢查傳入的master節(jié)點數量是否大于等于3個。只有大于3個節(jié)點才能組成集群。//master+slave大于6個
3、計算每個master需要分配的slot數量,以及給master分配slave。分配的算法大致如下:
先把節(jié)點按照host分類,這樣保證master節(jié)點能分配到更多的主機中。
不停遍歷遍歷host列表,從每個host列表中彈出一個節(jié)點,放入interleaved數組。直到所有的節(jié)點都彈出為止。
master節(jié)點列表就是interleaved前面的master數量的節(jié)點列表。保存在masters數組。
計算每個master節(jié)點負責的slot數量,保存在slots_per_node對象,用slot總數除以master數量取整即可。
遍歷masters數組,每個master分配slots_per_node個slot,最后一個master,分配到16384個slot為止。
接下來為master分配slave,分配算法會盡量保證master和slave節(jié)點不在同一臺主機上。對于分配完指定slave數量的節(jié)點,還有多余的節(jié)點,也會為這些節(jié)點尋找master。分配算法會遍歷兩次masters數組。
第一次遍歷masters數組,在余下的節(jié)點列表找到replicas數量個slave。每個slave為第一個和master節(jié)點host不一樣的節(jié)點,如果沒有不一樣的節(jié)點,則直接取出余下列表的第一個節(jié)點。
第二次遍歷是在對于節(jié)點數除以replicas不為整數,則會多余一部分節(jié)點。遍歷的方式跟第一次一樣,只是第一次會一次性給master分配replicas數量個slave,而第二次遍歷只分配一個,直到余下的節(jié)點被全部分配出去。
4、打印出分配信息,并提示用戶輸入“yes”確認是否按照打印出來的分配方式創(chuàng)建集群。
5、輸入“yes”后,會執(zhí)行flush_nodes_config操作,該操作執(zhí)行前面的分配結果,給master分配slot,讓slave復制master,對于還沒有握手(cluster meet)的節(jié)點,slave復制操作無法完成,不過沒關系,flush_nodes_config操作出現異常會很快返回,后續(xù)握手后會再次執(zhí)行flush_nodes_config。
6、給每個節(jié)點分配epoch,遍歷節(jié)點,每個節(jié)點分配的epoch比之前節(jié)點大1。
7、節(jié)點間開始相互握手,握手的方式為節(jié)點列表的其他節(jié)點跟第一個節(jié)點握手。
8、然后每隔1秒檢查一次各個節(jié)點是否已經消息同步完成,使用ClusterNode的get_config_signature方法,檢查的算法為獲取每個節(jié)點cluster nodes信息,排序每個節(jié)點,組裝成node_id1:slots|node_id2:slot2|...的字符串。如果每個節(jié)點獲得字符串都相同,即認為握手成功。
9、此后會再執(zhí)行一次flush_nodes_config,這次主要是為了完成slave復制操作。
10、最后再執(zhí)行check_cluster,全面檢查一次集群狀態(tài)。包括和前面握手時檢查一樣的方式再檢查一遍。確認沒有遷移的節(jié)點。確認所有的slot都被分配出去了。
11、至此完成了整個創(chuàng)建流程,返回[OK] All 16384 slots covered.。
3.2、check檢查集群狀態(tài)
[root@node112 redis]# redis-trib.rb check 127.0.0.1:1201
檢查集群狀態(tài),只需要選擇一個集群中的一個節(jié)點即可。
檢查前會先執(zhí)行l(wèi)oad_cluster_info_from_node方法,把所有節(jié)點數據load進來。load的方式為通過自己的cluster nodes發(fā)現其他節(jié)點,然后連接每個節(jié)點,并加入nodes數組。接著生成節(jié)點間的復制關系。
load完數據后,開始檢查數據,檢查的方式也是調用創(chuàng)建時候使用的check_cluster。
3.3、info查看集群信息
info命令也是先執(zhí)行l(wèi)oad_cluster_info_from_node獲取完整的集群信息。然后顯示ClusterNode的info_string結果
[root@node112 redis]# redis-trib.rb info 127.0.0.1:1201 127.0.0.1:1204 (9a60d0fc...) -> 0 keys | 4096 slots | 1 slaves. 127.0.0.1:1203 (31f66735...) -> 1 keys | 4096 slots | 1 slaves. 127.0.0.1:1209 (a4dc7dd4...) -> 0 keys | 4096 slots | 2 slaves. 127.0.0.1:1202 (56b03fec...) -> 0 keys | 4096 slots | 1 slaves. [OK] 1 keys in 4 masters. 0.00 keys per slot on average.3.4、fix修復集群
目前fix命令能修復兩種異常,一種是集群有處于遷移中的slot的節(jié)點,一種是slot未完全分配的異常。
fix_open_slot方法是修復集群有處于遷移中的slot的節(jié)點異常。
1、先檢查該slot是誰負責的,遷移的源節(jié)點如果沒完成遷移,owner還是該節(jié)點。沒有owner的slot無法完成修復功能。
2、遍歷每個節(jié)點,獲取哪些節(jié)點標記該slot為migrating狀態(tài),哪些節(jié)點標記該slot為importing狀態(tài)。對于owner不是該節(jié)點,但是通過cluster countkeysinslot獲取到該節(jié)點有數據的情況,也認為該節(jié)點為importing狀態(tài)。
3、如果migrating和importing狀態(tài)的節(jié)點均只有1個,這可能是遷移過程中redis-trib.rb被中斷所致,直接執(zhí)行move_slot繼續(xù)完成遷移任務即可。傳遞dots和fix為true。
4、如果migrating為空,importing狀態(tài)的節(jié)點大于0,那么這種情況執(zhí)行回滾流程,將importing狀態(tài)的節(jié)點數據通過move_slot方法導給slot的owner節(jié)點,傳遞dots、fix和cold為true。接著對importing的節(jié)點執(zhí)行cluster stable命令恢復穩(wěn)定。
5、如果importing狀態(tài)的節(jié)點為空,有一個migrating狀態(tài)的節(jié)點,而且該節(jié)點在當前slot沒有數據,那么可以直接把這個slot設為stable。
6、如果migrating和importing狀態(tài)不是上述情況,目前redis-trib.rb工具無法修復,上述的三種情況也已經覆蓋了通過redis-trib.rb工具遷移出現異常的各個方面,人為的異常情形太多,很難考慮完全。
fix_slots_coverage方法能修復slot未完全分配的異常。未分配的slot有三種狀態(tài)。
1、所有節(jié)點的該slot都沒有數據。該狀態(tài)redis-trib.rb工具直接采用隨機分配的方式,并沒有考慮節(jié)點的均衡。本人嘗試對沒有分配slot的集群通過fix修復集群,結果slot還是能比較平均的分配,但是沒有了連續(xù)性,打印的slot信息非常離散。
2、有一個節(jié)點的該slot有數據。該狀態(tài)下,直接把slot分配給該slot有數據的節(jié)點。
3、有多個節(jié)點的該slot有數據。此種情況目前還處于TODO狀態(tài),不過redis作者列出了修復的步驟,對這些節(jié)點,除第一個節(jié)點,執(zhí)行cluster migrating命令,然后把這些節(jié)點的數據遷移到第一個節(jié)點上。清除migrating狀態(tài),然后把slot分配給第一個節(jié)點。
3.5、reshard在線遷移slot
在線把集群的一些slot從集群原來slot負責節(jié)點遷移到新的節(jié)點,利用reshard可以完成集群的在線橫向擴容和縮容。
1、通過load_cluster_info_from_node方法裝載集群信息。
2、執(zhí)行check_cluster方法檢查集群是否健康。只有健康的集群才能進行遷移。
3、獲取需要遷移的slot數量,用戶沒傳遞--slots參數,則提示用戶手動輸入。
4、獲取遷移的目的節(jié)點,用戶沒傳遞--to參數,則提示用戶手動輸入。此處會檢查目的節(jié)點必須為master節(jié)點。
5、獲取遷移的源節(jié)點,用戶沒傳遞--from參數,則提示用戶手動輸入。此處會檢查源節(jié)點必須為master節(jié)點。--from all的話,源節(jié)點就是除了目的節(jié)點外的全部master節(jié)點。這里為了保證集群slot分配的平均,建議傳遞--from all。
6、執(zhí)行compute_reshard_table方法,計算需要遷移的slot數量如何分配到源節(jié)點列表,采用的算法是按照節(jié)點負責slot數量由多到少排序,計算每個節(jié)點需要遷移的slot的方法為:遷移slot數量 * (該源節(jié)點負責的slot數量 / 源節(jié)點列表負責的slot總數)。這樣算出的數量可能不為整數,這里代碼用了下面的方式處理:
這樣的處理方式會帶來最終分配的slot與請求遷移的slot數量不一致,這個BUG已經在github上提給作者,https://github.com/antirez/redis/issues/2990。
7、打印出reshard計劃,如果用戶沒傳--yes,就提示用戶確認計劃。
8、根據reshard計劃,一個個slot的遷移到新節(jié)點上,遷移使用move_slot方法,該方法被很多命令使用,具體可以參見下面的遷移流程。move_slot方法傳遞dots為true和pipeline數量。
9、至此,就完成了全部的遷移任務。
redis-trib.rb reshard --from all --to 80b661ecca260c89e3d8ea9b98f77edaeef43dcd --slots 11 10.180.157.199:6379
3.6、rebalance平衡集群節(jié)點slot數量
ruby redis-trib.rb rebalance --threshold 1 --weight b31e3a2e=5 --weight 60b8e3a1=5 --use-empty-masters --simulate 10.180.157.199:6379
host:port:這個是必傳參數,用來從一個節(jié)點獲取整個集群信息,相當于獲取集群信息的入口。 --weight <arg>:節(jié)點的權重,格式為node_id=weight,如果需要為多個節(jié)點分配權重的話,需要添加多個--weight <arg>參數,即--weight b31e3a2e=5 --weight 60b8e3a1=5,node_id可為節(jié)點名稱的前綴,只要保證前綴位數能唯一區(qū)分該節(jié)點即可。沒有傳遞–weight的節(jié)點的權重默認為1。 --auto-weights:這個參數在rebalance流程中并未用到。 --threshold <arg>:只有節(jié)點需要遷移的slot閾值超過threshold,才會執(zhí)行rebalance操作。具體計算方法可以參考下面的rebalance命令流程的第四步。 --use-empty-masters:rebalance是否考慮沒有節(jié)點的master,默認沒有分配slot節(jié)點的master是不參與rebalance的,設置--use-empty-masters可以讓沒有分配slot的節(jié)點參與rebalance。 --timeout <arg>:設置migrate命令的超時時間。 --simulate:設置該參數,可以模擬rebalance操作,提示用戶會遷移哪些slots,而不會真正執(zhí)行遷移操作。 --pipeline <arg>:與reshar的pipeline參數一樣,定義cluster getkeysinslot命令一次取出的key數量,不傳的話使用默認值為10。 rebalance命令流程如下:1、load_cluster_info_from_node方法先加載集群信息。 2、計算每個master的權重,根據參數--weight <arg>,為每個設置的節(jié)點分配權重,沒有設置的節(jié)點,則權重默認為1。 3、根據每個master的權重,以及總的權重,計算自己期望被分配多少個slot。計算的方式為:總slot數量 * (自己的權重 / 總權重)。 4、計算每個master期望分配的slot是否超過設置的閾值,即--threshold <arg>設置的閾值或者默認的閾值。計算的方式為:先計算期望移動節(jié)點的閾值,算法為:(100-(100.0*expected/n.slots.length)).abs,如果計算出的閾值沒有超出設置閾值,則不需要為該節(jié)點移動slot。只要有一個master的移動節(jié)點超過閾值,就會觸發(fā)rebalance操作。 5、如果觸發(fā)了rebalance操作。那么就開始執(zhí)行rebalance操作,先將每個節(jié)點當前分配的slots數量減去期望分配的slot數量獲得balance值。將每個節(jié)點的balance從小到大進行排序獲得sn數組。 6、用dst_idx和src_idx游標分別從sn數組的頭部和尾部開始遍歷。目的是為了把尾部節(jié)點的slot分配給頭部節(jié)點。sn數組保存的balance列表排序后,負數在前面,正數在后面。負數表示需要有slot遷入,所以使用dst_idx游標,正數表示需要有slot遷出,所以使用src_idx游標。理論上sn數組各節(jié)點的balance值加起來應該為0,不過由于在計算期望分配的slot的時候只是使用直接取整的方式,所以可能出現balance值之和不為0的情況,balance值之和不為0即為節(jié)點不平衡的slot數量,由于slot總數有16384個,不平衡數量相對于總數,基數很小,所以對rebalance流程影響不大。7、獲取sn[dst_idx]和sn[src_idx]的balance值較小的那個值,該值即為需要從sn[src_idx]節(jié)點遷移到sn[dst_idx]節(jié)點的slot數量。 8、接著通過compute_reshard_table方法計算源節(jié)點的slot如何分配到源節(jié)點列表。這個方法在reshard流程中也有調用,具體步驟可以參考reshard流程的第六步。 9、如果是simulate模式,則只是打印出遷移列表。 10、如果沒有設置simulate,則執(zhí)行move_slot操作,遷移slot,傳入的參數為:quiet=>true,:dots=>false,:update=>true。 11、遷移完成后更新sn[dst_idx]和sn[src_idx]的balance值。如果balance值為0后,游標向前進1。 12、直到dst_idx到達src_idx游標,完成整個rebalance操作。3.7、add-node將新節(jié)點加入集群
--slave:設置該參數,則新節(jié)點以slave的角色加入集群 --master-id:這個參數需要設置了--slave才能生效,--master-id用來指定新節(jié)點的master節(jié)點。如果不設置該參數,則會隨機為節(jié)點選擇master節(jié)點。 ruby redis-trib.rb add-node --slave --master-id dcb792b3e85726f012e83061bf237072dfc45f99 10.180.157.202:6379 10.180.157.199:6379add-node流程如下:
1、通過load_cluster_info_from_node方法轉載集群信息,check_cluster方法檢查集群是否健康。
2、如果設置了--slave,則需要為該節(jié)點尋找master節(jié)點。設置了--master-id,則以該節(jié)點作為新節(jié)點的master,如果沒有設置--master-id,則調用get_master_with_least_replicas方法,尋找slave數量最少的master節(jié)點。如果slave數量一致,則選取load_cluster_info_from_node順序發(fā)現的第一個節(jié)點。load_cluster_info_from_node順序的第一個節(jié)點是add-node設置的existing_host:existing_port節(jié)點,后面的順序根據在該節(jié)點執(zhí)行cluster nodes返回的結果返回的節(jié)點順序。
3、連接新的節(jié)點并與集群第一個節(jié)點握手。
4、如果沒設置–slave就直接返回ok,設置了–slave,則需要等待確認新節(jié)點加入集群,然后執(zhí)行cluster replicate命令復制master節(jié)點。
5、至此,完成了全部的增加節(jié)點的流程。
3.8、del-node從集群中刪除節(jié)點
del-node可以把某個節(jié)點從集群中刪除。del-node只能刪除沒有分配slot的節(jié)點。刪除命令傳遞兩個參數:
host:port:從該節(jié)點獲取集群信息。
node_id:需要刪除的節(jié)點id。
del-node執(zhí)行結果示例如下:
redis-trib.rb del-node 10.180.157.199:6379 d5f6d1d17426bd564a6e309f32d0f5b96962fe53
3.9、set-timeout設置集群節(jié)點間心跳連接的超時時間
set-timeout用來設置集群節(jié)點間心跳連接的超時時間,單位是毫秒,不得小于100毫秒,因為100毫秒對于心跳時間來說太短了。該命令修改是節(jié)點配置參數cluster-node-timeout,默認是15000毫秒。通過該命令,可以給每個節(jié)點設置超時時間,設置的方式使用config set命令動態(tài)設置,然后執(zhí)行config rewrite命令將配置持久化保存到硬盤。
ruby redis-trib.rb set-timeout 10.180.157.199:6379 30000
3.10、call在集群全部節(jié)點上執(zhí)行命令
call命令可以用來在集群的全部節(jié)點執(zhí)行相同的命令。call命令也是需要通過集群的一個節(jié)點地址,連上整個集群,然后在集群的每個節(jié)點執(zhí)行該命令。
redis-trib.rb call 10.180.157.199:6379 get key
3.11、import將外部redis數據導入集群
import命令可以把外部的redis節(jié)點數據導入集群。導入的流程如下:
1、通過load_cluster_info_from_node方法轉載集群信息,check_cluster方法檢查集群是否健康。
2、連接外部redis節(jié)點,如果外部節(jié)點開啟了cluster_enabled,則提示錯誤。
3、通過scan命令遍歷外部節(jié)點,一次獲取1000條數據。
4、遍歷這些key,計算出key對應的slot。
5、執(zhí)行migrate命令,源節(jié)點是外部節(jié)點,目的節(jié)點是集群slot對應的節(jié)點,如果設置了--copy參數,則傳遞copy參數,如果設置了--replace,則傳遞replace參數。
6、不停執(zhí)行scan命令,直到遍歷完全部的key。
7、至此完成整個遷移流程
這中間如果出現異常,程序就會停止。沒使用--copy模式,則可以重新執(zhí)行import命令,使用--copy的話,最好清空新的集群再導入一次。
import命令更適合離線的把外部redis數據導入,在線導入的話最好使用更專業(yè)的導入工具,以slave的方式連接redis節(jié)點去同步節(jié)點數據應該是更好的方式。
./redis-trib.rb import --from 10.0.10.1:6379 10.10.10.1:7000 //把 10.0.10.1:6379(redis 2.8)上的數據導入到 10.10.10.1:7000這個節(jié)點所在的集群四、redis分布式鎖
Redis為單進程單線程模式、采用隊列模式將并發(fā)訪問變成串行訪問。且多client對redis的鏈接并不存在競爭關系。其次Redis提供SETNX,GETSET方便實現分布式鎖
條件:
系統(tǒng)是一個分布式系統(tǒng)(關鍵是分布式,單機的可以使用ReentrantLock或者synchronized代碼塊來實現)
共享資源(各個系統(tǒng)訪問同一個資源,資源的載體可能是傳統(tǒng)關系型數據庫或者NoSQL)
同步訪問(即有很多個進程同事訪問同一個共享資源。沒有同步訪問,誰管你資源競爭不競爭)
分布式鎖多種方式實現,比如zookeeper、redis...。不管哪種方式,基本原理是不變的:用一個狀態(tài)值表示鎖,對鎖的占用和釋放通過狀態(tài)值來標識。
1、setNX實現分布式鎖
Redis為單進程單線程模式,采用隊列模式將并發(fā)訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關系。redis的SETNX命令可以方便的實現分布式鎖。
SETNX key value //若給定的 key 已經存在,則 SETNX 不做任何動作。
設置成功,返回 1 。 //exists key1 測試key1是否存在,為0表示不存在,1表示存在
設置失敗,返回 0 。
SETNX lock.foo <current Unix time + lock timeout + 1>
返回1:則該客戶端獲得鎖,把lock.foo的鍵值設置為時間值表示該鍵已被鎖定,該客戶端最后可以通過DEL lock.foo來釋放該鎖。
返回0:表明該鎖已被其他客戶端取得,這時我們可以先返回或進行重試等對方完成或等待鎖超時。
GETSET key value
將給定 key 的值設為 value,并返回 key 的舊值(old value)。
當 key 存在但不是字符串類型時,返回一個錯誤 //當 key 沒有舊值時,也即是,key 不存在時,返回 nil 。
127.0.0.1:1209> GETSET job "chanpinjingli" //返回舊指
"yunweigongchengshi"
127.0.0.1:1209> get job //已經修改
"chanpinjingli"
2、解決死鎖
刪除鎖的操作應該是鎖擁有這執(zhí)行的,只需要等它超時即可
當多個客戶端檢測到鎖超時后都會嘗試去釋放它,這里就可能出現一個競態(tài)條件,讓我們模擬一下這個場景:
C0操作超時了,但它還持有著鎖,C1和C2讀取lock.foo檢查時間戳,先后發(fā)現超時了。
C1 發(fā)送DEL lock.foo
C1 發(fā)送SETNX lock.foo 并且成功了。
C2 發(fā)送DEL lock.foo
C2 發(fā)送SETNX lock.foo 并且成功了。
這樣一來,C1,C2都拿到了鎖!問題大了!
解決方法:
C3發(fā)送SETNX lock.foo 想要獲得鎖,由于C0還持有鎖,所以Redis返回給C3一個0
C3發(fā)送GET lock.foo 以檢查鎖是否超時了,如果沒超時,則等待或重試。反之,如果已超時,C3通過下面的操作來嘗試獲得鎖:
GETSET lock.foo <current Unix time + lock timeout + 1>
通過GETSET,C3拿到的時間戳如果仍然是超時的,那就說明,C3如愿以償拿到鎖了。
如果在C3之前,有個叫C4的客戶端比C3快一步執(zhí)行了上面的操作,那么C3拿到的時間戳是個未超時的值,這時,C3沒有如期獲得鎖,需要再次等待或重試。留意一下,盡管C3沒拿到鎖,但它改寫了C4設置的鎖的超時值,不過這一點非常微小的誤差帶來的影響可以忽略不計。
為了讓分布式鎖的算法更穩(wěn)鍵些,持有鎖的客戶端在解鎖之前應該再檢查一次自己的鎖是否已經超時,再去做DEL操作,因為可能客戶端因為某個耗時的操作而掛起,操作完的時候鎖因為超時已經被別人獲得,這時就不必解鎖了
五、其他
1、Redis漏洞問題,參考
最近針對Redis的弱口令進行***的案例較多。處理方法大同小異,對比參考:
http://blog.jobbole.com/94518/ https://blog.csdn.net/kevin_pso/article/details/54844980 https://blog.51cto.com/simeon/21151842、redis集群相關配置指令
#cat conf/sentinel.conf sentinel monitor redis_cluster 127.0.0.1 16000 1 sentinel failover-timeout redis_cluster 15000 sentinel config-epoch redis_cluster 0 daemonize yes dir "/tmp/mongo/redis" logfile "/tmp/redis/redis-sentinel.log" port 16006解釋:
sentinel monitor mymaster 127.0.0.1 6379 2 //監(jiān)控的master的名字為mymaster。當集群中有2個sentinel認為master死了時,才能真正認為該master已經不可用了 sentinel down-after-milliseconds mymaster 60000 //如果master在“一定時間范圍”內不回應PONG 或者是回復了一個錯誤消息,那么這個sentinel會主觀地(單方面地)認為這個master已經不可用了(subjectively down, 也簡稱為SDOWN)。客觀down:ODOWN sentinel failover-timeout mymaster 180000 // sentinel parallel-syncs mymaster 1 //在發(fā)生failover主備切換時,這個選項指定了最多可以有多少個slave同時對新的master進行同步,這個數字越小,完成failover所需的時間就越長,但是如果這個數字越大,就意味著越多的slave因為replication而不可用。可以通過將這個值設為 1 來保證每次只有一個slave處于不能處理命令請求的狀態(tài)。//這些指令都可以通過SENTINEL SET command動態(tài)修改。
參考博客:
https://www.oschina.net/question/tag/twemproxy //twemproxy開源中國社區(qū)
https://www.cnblogs.com/lihaoyang/p/6906444.html //gem安裝redis官方集群版
https://redis.io/commands/cluster-nodes //官方redis集群相關命令
https://blog.csdn.net/huwei2003/article/details/50973967 //redis-trib.rb詳解
https://piaosanlang.gitbooks.io/redis/content/redisfen-bu-shi-suo-shi-xian.html
https://www.cnblogs.com/haoxinyue/p/redis.html //twitter版redis集群
https://blog.csdn.net/shmiluwei/article/details/51958359 //Codis安裝和配置
轉載于:https://blog.51cto.com/hmtk520/2115789
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Redis详解(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iBatis——执行原理
- 下一篇: 4.3/4.4 磁盘分区