redis的叢集搭建
Redis 是一個開源的 key-value 儲存系統,由於出眾的效能,大部分網際網路企業都用來做伺服器端快取。Redis 在3.0版本前只支援單例項模式,雖然支援主從模式、哨兵模式部署來解決單點故障,但是現在網際網路企業動輒大幾百G的資料,可完全是沒法滿足業務的需求,所以,Redis 在 3.0 版本以後就推出了叢集模式。
Redis 叢集採用了P2P的模式,完全去中心化。Redis 把所有的 Key 分成了 16384 個 slot,每個 Redis 例項負責其中一部分 slot 。叢集中的所有資訊(節點、埠、slot等),都通過節點之間定期的資料交換而更新。
Redis 客戶端可以在任意一個 Redis 例項發出請求,如果所需資料不在該例項中,通過重定向命令引導客戶端訪問所需的例項。
安裝環境
OS: centos 7.2.1511
redis : 最新版 v4.0.1
單臺redis安裝
下載
wget http://download.redis.io/releases/redis-4.0.10.tar.gz tar zxvf redis-4.0.10.tar.gz mv redis-4.0.10 /usr/local/redis/
如果是離線安裝,則需要從官網下載指定版本,然後上傳到生產環境。這裡安裝的是最新版redis,指定安裝路徑 /usr/local/redis/
編譯安裝
cd /usr/local/redis make make install安裝完成,這時候會在/usr/local/bin/目錄下看到redis-server、redis-cli等可執行指令碼,進入看一下,如果沒有,就要去解壓目錄複製進去了。
配置redis.conf
redis.conf在預設在安裝目錄下$ vim /usr/local/redis/redis.conf這裡要修改兩個地方,一個bind和daemonize就行。要注意的點:
- bind這裡配置要注意,預設是隻有一個127.0.0.1,這個時候只能自己連線,其他區域網內是連線不上的。所以,需要配置多個 IP ,這樣就可以區域網內進行連線了。我設定的0.0.0.0,雖然這不太安全,方便我在其它地方連線。
- daemonize是設定是否後臺啟動 Redis,預設no,正常都需要以服務形式啟動 Redis,所以這裡設定為yes。
- 可以設定密碼 requirepass mypassord
- redis v3.0+版本增加了保護機制,預設protected-mode yes, 這樣目標機器呼叫可能會報錯,需要設定為no
啟動redis
cd /usr/local/bin/
redis-server /usr/local/redis/redis.conf
測試redis服務
埠已開啟:netstat -anp | grep 6379
命令列連線:
➜ redis redis-cli 127.0.0.1:6379> keys * 1) "city" 2) "usage" 3) "idc" 4) "region" 127.0.0.1:6379> set name jzhou OK 127.0.0.1:6379> get name "jzhou" 127.0.0.1:6379>
關閉服務
可以通過殺程序的方式暴力關閉服務,也可以通過命令:
redis-cli shutdown通過 netstat 可以看出來埠已經是TIME_WAIT狀態了
以上是centos7單機部署redis的過程,下面叢集搭建和上述類似,不過在目錄結構和配置檔案不同,當然,遇到的坑也多,主要配置叢集依賴一些外部包,但說實話光搭建個叢集是沒有啥技術含量但。。
redis叢集搭建
簡介
redis-cluster架構設計
架構細節:
(1)所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位制協議優化傳輸速度和頻寬.
(2)節點的fail是通過叢集中超過半數的節點檢測失效時才生效.
(3)客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連線叢集所有節點,連線叢集中任何一個可用節點即可
(4)redis-cluster把所有的物理節點對映到[0-16383]slot上,cluster 負責維護node<->slot<->key
(5)Redis叢集預分好16384個桶,當需要在 Redis 叢集中放置一個 key-value 時,根據 CRC16(key) mod 16384的值,決定將一個key放到哪個桶中。
環境準備
Redis叢集中要求奇數節點,所以至少要有三個節點,並且每個節點至少有一備份節點,所以至少需要6個redis服務例項。
這裡演示的是我搭的生產環境redis叢集,3臺伺服器,每臺起3個服務,共9個節點,生產環境搭建的很順利,但測試環境一臺伺服器開6個埠遇到了很多問題,各種依賴包問題,後續會說明。
三臺伺服器
172.28.37.29 172.28.37.30 172.18.38.219 (每臺伺服器redis三個埠號 7000~7002)
按照上面搭建單臺redis服務的方式在三臺伺服器分別安裝redis,從配置檔案開始會有所不同。下面會說明搭建過程。
依賴包安裝
注意,下述均安裝最新版本,否則會和redis版本不匹配
lib
zlib
ruby
rubugems
擼起袖子叢集搭建
1. 準備目錄結構
三臺機器一樣,建立如下目錄結構:
$ mkdir -p /usr/local/redis/redis-cluster/{7000,7001,7002}
分別進入每個埠目錄建立配置檔案:
cd /usr/local/redis/redis-cluster/7000 && touch redis.conf
2.redis.conf內容及解釋:
port 7000 # 埠7000,7001,7002,與目錄對應 bind 172.28.37.29 #預設ip為127.0.0.1,需要改為其他節點機器可訪問的ip,否則建立叢集時無法訪問對應的埠,無法建立叢集 daemonize yes #redis後臺執行 cluster-enabled yes #開啟叢集 cluster-config-file nodes_7000.conf #叢集的配置,配置檔案首次啟動自動生成 7000,7001,7002 cluster-node-timeout 8000 #請求超時,預設15秒,可自行設定 appendonly yes #開啟aof持久化模式,每次寫操作請求都追加到appendonly.aof檔案中 appendfsync always #每次有寫操作的時候都同步 logfile "/data/redis/logs/redis.log" #redis服務日誌 pidfile /var/run/redis_7000.pid #pidfile檔案對應7000,7001,7002
注意,上述有些配置項要對應服務和目錄,三個目錄按照上述配置好後,啟動服務
3.啟動/關閉叢集服務
可以在每個伺服器上寫一個啟動指令碼start-redis.sh:
for((i=0;i<3;i++)); do /usr/local/bin/redis-server /usr/local/redis/redis-cluster/700$i/redis.conf; done
關閉服務類似:
for((i=0;i<=2;i++)); do /usr/local/bin/redis-cli -c -h $IP -p 700$i shutdown; done
$IP分別為三臺伺服器IP。
這時只是啟動了9個單獨的redis服務,它們還不是一個叢集,下面就說明建立叢集
4.建立叢集
注意:在任意一臺上執行 不要在每臺機器上都執行,一臺就夠了
Redis 官方提供了 redis-trib.rb
這個工具,就在解壓目錄的 src 目錄中
在其中一臺執行:
$ cd /root/redis-4.0.10/src $ ./redis-trib.rb create --replicas 1 172.28.37.29:7000 172.28.37.29:7001 172.28.37.29:7002 172.28.37.30:7000 172.28.37.30:7001 172.28.37.30:7002 172.18.38.219:7000 172.18.38.219:7001 172.18.38.219:7002
敲完這個命令後會提示是否按照預設的推薦方式配置叢集主從,一般選yes就行了
截圖中看出,推薦了4個masters,5個從節點>>> Creating cluster >>> Performing hash slots allocation on 9 nodes... Using 4 masters: 172.28.37.29:7000 172.28.37.30:7000 172.18.38.219:7000 172.28.37.29:7001 Adding replica 172.18.38.219:7001 to 172.28.37.29:7000 Adding replica 172.28.37.29:7002 to 172.28.37.30:7000 Adding replica 172.28.37.30:7002 to 172.18.38.219:7000 Adding replica 172.18.38.219:7002 to 172.28.37.29:7001 Adding replica 172.28.37.30:7001 to 172.28.37.29:7000
下面這個顯示了叢集和slot分配結果
5.叢集驗證
引數 -C 可連線到叢集,因為 redis.conf 將 bind 改為了ip地址,所以 -h 引數不可以省略,-p 引數為埠號[[email protected]172-28-37-29 src]# redis-cli -c -p 7000 -h 172.28.37.29 172.28.37.29:7000> set name zhoujie -> Redirected to slot [5798] located at 172.28.37.30:7000 OK 172.28.37.30:7000> get name "zhoujie" 172.28.37.30:7000>
可以看到在29的7000上設定了name,重定向到了30的7000節點。
到此為止叢集搭建成功!
友情提示:
當出現叢集無法啟動時,刪除叢集配置檔案,再次重新啟動每一個redis服務,然後重新構件叢集環境。
--------------------------------------------華麗的分割線----------------------------------------------------
redis-trib.rb命令常見用法
1)列出叢集節點-cluster nodes
[[email protected]172-28-37-30 src]# redis-cli -h 172.28.37.30 -c -p 7000 172.28.37.30:7000> cluster nodes 0d260c47f10ecbbfd9c3c1707da82a3dd7951313 172.18.38.219:7001@17001 slave f85a9a80aca5e4c4a1437c7b58abd5895ee66855 0 1531497002061 8 connected 6d3db545319a8d41f4ad0666885856257fc2ab5f 172.18.38.219:7002@17002 slave 0cdeba26690238582ad7705abac6de71d1817c9e 0 1531497002062 9 connected 3f6c1449e60fe0d868e0bdc655165419ed7cd193 172.18.38.219:7000@17000 master - 0 1531497003000 7 connected 8192-12287 4004ad507da2e7cbae465d7f01864d53972f595c 172.28.37.30:7002@17002 slave 3f6c1449e60fe0d868e0bdc655165419ed7cd193 0 1531497003062 7 connected f85a9a80aca5e4c4a1437c7b58abd5895ee66855 172.28.37.29:7000@17000 master - 0 1531497004064 1 connected 0-4095 7fc5072406e47cd58822f17f5e1ce3b15328352e 172.28.37.30:7000@17000 myself,master - 0 1531497002000 4 connected 4096-8191 fb4554a4fa2bd4af1c213b90ec32d3f7fb12e87b 172.28.37.30:7001@17001 slave f85a9a80aca5e4c4a1437c7b58abd5895ee66855 0 1531497005066 5 connected 967243e807a1c0c32ad56db47007c54a2d1d6e2e 172.28.37.29:7002@17002 slave 7fc5072406e47cd58822f17f5e1ce3b15328352e 0 1531497004064 4 connected 0cdeba26690238582ad7705abac6de71d1817c9e 172.28.37.29:7001@17001 master - 0 1531497004064 2 connected 12288-16383 172.28.37.30:7000>
2)檢視叢集資訊- cluster info
172.28.37.30:7000> cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:9 cluster_size:4 cluster_current_epoch:9 cluster_my_epoch:4 cluster_stats_messages_ping_sent:2243335 cluster_stats_messages_pong_sent:2220197 cluster_stats_messages_meet_sent:4 cluster_stats_messages_sent:4463536 cluster_stats_messages_ping_received:2220192 cluster_stats_messages_pong_received:2243339 cluster_stats_messages_meet_received:5 cluster_stats_messages_received:4463536 172.28.37.30:7000>
3)檢查叢集狀態-check
下面內容是測試環境搭建的,一臺伺服器6個節點
[[email protected]192-168-151-110 redis]# /root/redis-4.0.10/src/redis-trib.rb check 192.168.151.110:7000 >>> Performing Cluster Check (using node 192.168.151.110:7000) M: 7bf58adaafbfee1643785ea7b5da723d6595bbaf 192.168.151.110:7000 slots:0-5460 (5461 slots) master 1 additional replica(s) S: 90072487e6227544c079b2d8214ef5fd050575b5 192.168.151.110:7004 slots: (0 slots) slave replicates f7374d1b48562c9e54523948915c99bb00a12ba7 M: c6cf17370fc94aabc88e862fa72229ebd9b94166 192.168.151.110:7002 slots:10923-16383 (5461 slots) master 1 additional replica(s) S: 59d25294cbfb63f2d4d75410e66ce58899c65469 192.168.151.110:7003 slots: (0 slots) slave replicates 7bf58adaafbfee1643785ea7b5da723d6595bbaf S: 6256051389337640691e4b44d7de2b7b8c8fa25f 192.168.151.110:7005 slots: (0 slots) slave replicates c6cf17370fc94aabc88e862fa72229ebd9b94166 M: f7374d1b48562c9e54523948915c99bb00a12ba7 192.168.151.110:7001 slots:5461-10922 (5462 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. [[email protected]192-168-151-110 redis]#
4)其它常用命令
- create:建立一個叢集環境host1:port1 ... hostN:portN(叢集中的主從節點比例)
- call:可以執行redis命令
- add-node:將一個節點新增到叢集裡,第一個引數為新節點的ip:port,第二個引數為叢集中任意一個已經存在的節點的ip:port
- del-node:移除一個節點
- reshard:重新分片
叢集操作
//叢集(cluster) CLUSTER INFO 列印叢集的資訊 CLUSTER NODES 列出叢集當前已知的所有節點(node),以及這些節點的相關資訊。 //節點(node) CLUSTER MEET <ip> <port> 將 ip 和 port 所指定的節點新增到叢集當中,讓它成為叢集的一份子。 CLUSTER FORGET <node_id> 從叢集中移除 node_id 指定的節點。 CLUSTER REPLICATE <node_id> 將當前節點設定為 node_id 指定的節點的從節點。 CLUSTER SAVECONFIG 將節點的配置檔案儲存到硬盤裡面。 //槽(slot) CLUSTER ADDSLOTS <slot> [slot ...] 將一個或多個槽(slot)指派(assign)給當前節點。 CLUSTER DELSLOTS <slot> [slot ...] 移除一個或多個槽對當前節點的指派。 CLUSTER FLUSHSLOTS 移除指派給當前節點的所有槽,讓當前節點變成一個沒有指派任何槽的節點。 CLUSTER SETSLOT <slot> NODE <node_id> 將槽 slot 指派給 node_id 指定的節點,如果槽已經指派給另一個節點,那麼先讓另一個節點刪除該槽>,然後再進行指派。 CLUSTER SETSLOT <slot> MIGRATING <node_id> 將本節點的槽 slot 遷移到 node_id 指定的節點中。 CLUSTER SETSLOT <slot> IMPORTING <node_id> 從 node_id 指定的節點中匯入槽 slot 到本節點。 CLUSTER SETSLOT <slot> STABLE 取消對槽 slot 的匯入(import)或者遷移(migrate)。 //鍵 (key) CLUSTER KEYSLOT <key> 計算鍵 key 應該被放置在哪個槽上。 CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的鍵值對數量。 CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 個 slot 槽中的鍵。
向叢集中新增節點或刪除節點
我們新建兩個服務,按照之前搭建的叢集方式新增倆個節點:(一主一從 master、slave)。
Mater:7007 slave:7008
建立7007/7008資料夾。拷貝redis.conf檔案到對於的7007,7008目錄下 ,再進行修改配置檔案。
啟動7007和7008倆個服務並檢視服務狀態。
1. 叢集中新增一個主節點
步驟一:使用add-node命令:綠色為新增節點,紅色為已知存在節點
[[email protected] local]# /usr/local/redis/src/redis-trib.rb add-node 192.168.151.110:7007 192.168.151.110:7001
注意:當新增節點成功以後,新增的節點不會有任何資料,因為他沒有分配任何slot。需要為新節點手動分配slot。
步驟二:reshard命令,分配slot:
[[email protected] local]# /usr/local/redis-3.0.0/src/redis-trib.rb reshard 192.168.151.110:7007
(提示一)
How many slots do you want to move (from 1 to 16384)? 2000
(提示二)
What is the receiving node ID? 382634a4025778c040b7213453fd42a709f79e28
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:all
(提示三)
Do you want to proceed with the proposed reshard plan (yes/no)? yes
- 提示一:是希望你需要多少個槽移動到新的節點上,可以自己設定,比如200個槽。
- 提示二:是你需要把這200個slot槽移動到那個節點上去(需要指定節點id),並且下個提示是輸入all為從所有主節點(7001 7002 7003)中分別抽取相應的槽數(一共為200個槽到指定的新節點中!,並且會列印執行分片的計劃。)
- 提示三:輸入yes確認開始執行分片任務
2. 叢集中新增一個從節點
步驟一:使用add-node命令:綠色為新增節點,紅色為已知存在節點
[[email protected] local]# /usr/local/redis/src/redis-trib.rb add-node 192.168.151.110:7008 192.168.151.110:7001
步驟二:首先需要登入到7008節點的客戶端,然後使用叢集命令,執行replicate命令來指定當前節點的主節點id為哪一個。把當前的7008(slave)節點指定到一個主節點下(這裡使用之前建立的7007主節點,綠色表示節點id)。
[[email protected] ~]# /usr/local/redis/bin/redis-cli -c -h 192.168.151.110 -p 7008
192.168.1.124:7008> cluster replicate 4d4cb840519eef342a5730168b6c7e14dd811542
(7007的id)
OK
3.叢集中刪除一個主節點
如果主節點有從節點,將從節點轉移到其他主節點。如果主節點有slot,先將主節點裡的slot分配到其他可用節點中,然後再刪除節點才行,否則會有資料的丟失。
步驟一:刪除7007(master)節點之前,我們需要先把其全部的資料(slot槽)移動到其他節點上去(目前只能把master的資料遷移到一個節點上,暫時做不了平均分配功能)。
[[email protected] ~]# /usr/local/redis/src/redis-trib.rb reshard 192.168.151.110:7007
How many slots do you want to move (from 1 to 16384)? 1999
(註釋:這裡不會是正好200個槽)
What is the receiving node ID? 614d0def75663f2620b6402a017014b57c912dad
(註釋:這裡是需要把資料移動到哪?7001的主節點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:4d4cb840519eef342a5730168b6c7e14dd811542
(註釋:這裡是需要資料來源,也就是我們的7007節點id)
Source node #2:done
(註釋:這裡直接輸入done 開始生成遷移計劃)
Do you want to proceed with the proposed reshard plan (yes/no)? yes
(註釋:這裡輸入yes開始遷移)
步驟二:最後我們直接使用del-node命令刪除7007主節點即可(藍色表示7007的節點id)。
[[email protected] ~]# /usr/local/redis-3.0.0/src/redis-trib.rb del-node 192.168.151.110:7007 4d4cb840519eef342a5730168b6c7e14dd811542
4.叢集中刪除一個從節點
步驟一:刪除從節點7008,輸入del-node命令,指定刪除節點ip和埠,以及節點id(藍色為7008節點id),移除了7008 slave節點,前節點的服務程序也會隨之銷燬。
[[email protected] ~]# /usr/local/redis-3.0.0/src/redis-trib.rb del-node 192.168.151.110:7008 a78c8a41f6430b51a7eca1fdb50092c463a8f1ac
nodejs連線redis叢集示例
目前用得最多的 Node.js Redis 庫是 node redis,不過這個庫基本已經不再維護了,存在很多 bug(在生產環境中碰到過),也缺失了很多功能(如 pipeling 和指令碼優化)。而 ioredis 不僅支援了 Cluster 和 Sentinel,還在 API 層面和 node redis 保持了相容。
const Redis = require('ioredis'); let redis_members = [{ port: 7000, host: '172.28.37.29' }, { port: 7001, host: '172.28.37.29' }, { port: 7002, host: '172.28.37.29' }, { port: 7000, host: '172.28.37.30' }, { port: 7001, host: '172.28.37.30' }, { port: 7002, host: '172.28.37.30' }, { port: 7000, host: '172.18.38.219' }, { port: 7001, host: '172.18.38.219' }, { port: 7002, host: '172.18.38.219' }] var cluster = new Redis.Cluster(redis_members); cluster.set('foo', 'bar'); cluster.get('foo', function (err, res) { console.log('=====',res) //===== bar });
-------------------------------------------------華麗的分割線------------------------------------------------
你以為結束了?其實還沒有!
叢集搭建過程的各種奇葩錯誤這裡彙總,當然下面錯誤不是一定會遇到,但不保證一定不會不遇到。生產環境我搭的挺順利,但是測試環境幾乎所有能遇到的問題全都遇到了,最後都解決了,特此記錄!