1. 程式人生 > >redis的叢集搭建

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
這裡要修改兩個地方,一個binddaemonize就行。要注意的點:
  • 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三個埠號 70007002

 按照上面搭建單臺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  #叢集的配置,配置檔案首次啟動自動生成 700070017002  
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,70017002
複製程式碼

注意,上述有些配置項要對應服務和目錄,三個目錄按照上述配置好後,啟動服務

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
});
複製程式碼

-------------------------------------------------華麗的分割線------------------------------------------------

你以為結束了?其實還沒有!

叢集搭建過程的各種奇葩錯誤這裡彙總,當然下面錯誤不是一定會遇到,但不保證一定不會不遇到。生產環境我搭的挺順利,但是測試環境幾乎所有能遇到的問題全都遇到了,最後都解決了,特此記錄!