redis系列:叢集

雲梟發表於2018-08-09

1 簡介

Redis 叢集是Redis 的一個分散式實現,它是一個網狀結構,每個節點都通過 TCP 連線跟其他每個節點連線。現在來看看Redis叢集實現了哪些目標?

  • 在1000個節點的時候仍能表現得很好並且可擴充套件性(scalability)是線性的。叢集之間使用非同步複製,並且沒有合併的操作。
  • 可接受的寫入安全(Write safety)級別:那些與大多數節點相連的客戶端所做的寫入操作,系統嘗試全部都儲存下來。不過還是會有小部分寫入會丟失。
  • 可用性(Availability):在絕大多數的主節點(master node)是可達的,並且對於每一個不可達的主節點都至少有一個它的從節點(slave)可達的情況下,Redis 叢集仍能進行分割槽(partitions)操作。

那麼Redis叢集環境與非分散式Redis環境在功能上有沒有什麼不同的呢?

  • 叢集的資料庫只有0,且不支援SELECT
  • 由於叢集將鍵分佈在不同的槽(slot)中,所以涉及到多鍵值的複製操作也是不支援的,像set裡的並集(unions)和交集(intersections)操作

2 概念

在進入叢集環境搭建時,先介紹叢集的一些基本概念,例如節點、槽等等。

2.1 節點

節點(node)可以說是構成叢集的基本元素.通常一個Redis叢集中包含了多個節點,每個節點都有一個唯一的名字。

節點名字是一個十六進位制表示的160 bit 隨機數,這個隨機數是節點第一次啟動時獲得的(通常是用 /dev/urandom)。 節點會把它的ID儲存在配置檔案裡,以後永遠使用這個ID.如果想要更換ID有如下兩種方式:

  1. 刪除掉節點配置檔案。
  2. 執行CLUSTER RESET命令。

那麼這個節點ID有什麼用處呢?

節點ID是用於每個節點。通過節點ID可以檢測到節點 IP 或埠的變化。

如果節點發生了 IP 或埠變化時,其他節點是如何得知的呢?

叢集會使用gossip 協議來發布廣播訊息,通知配置變更。

節點與節點之間知道對方的哪些資訊?

  • 節點的 IP 地址和 TCP 埠號。

  • 各種標識。

  • 節點使用的雜湊槽。

  • 最近一次用叢集連線傳送 ping 包的時間。

  • 最近一次在回覆中收到一個 pong 包的時間。

  • 最近一次標識節點失效的時間。

  • 該節點的從節點個數。

  • 如果該節點是從節點,會有主節點ID資訊。(如果它是個主節點則該資訊置為0000000…)

我們可以通過使用CLUSTER NODES命令可以獲得以上的一些資訊,如下

redis系列:叢集

2.2 槽

Redis叢集通過分片的方式來儲存資料庫中的鍵值對:整個鍵空間被分割為 16384 槽(slot),每個鍵都將存放在 16384 槽(slot)的其中一個位置上。

那麼是什麼決定一個鍵所存放的位置呢?

下方就是計算鍵存放的位置的演算法。

HASH_SLOT = CRC16(key) mod 16384
複製程式碼

通過CRC16演算法獲取鍵的16位輸出結果,然後再對 16384 取餘,結果就是鍵所在的位置。

既然整個鍵空間被分割為 16384 槽(slot),那麼是如何將這些槽分配給不同的節點的?

可以通過以下命令將槽分片

CLUSTER ADDSLOTS slot1 [slot2] … [slotN]
複製程式碼

3 搭建Redis叢集

3.1 叢集環境介紹

本文搭建的叢集環境有3個主節點,每個主節點都有兩個從節點,架構圖如下

redis系列:叢集

3.2 修改配置檔案

關於叢集的配置如下

################################ REDIS CLUSTER ###############################

cluster-enabled yes

cluster-config-file nodes-6379.conf

cluster-node-timeout 5000
複製程式碼

cluster-enabled表示是否開啟叢集模式

cluster-conf-file 表示儲存節點配置檔案的路徑

cluster-node-timeout表示節點超時時間

完整的配置檔案在github.com/rainbowda/l… ,有需要的可以去下載

3.3 節點配置及啟動

由於採用一個伺服器執行三個Redis例項,所以每個節點的配置有些許不同,像埠號、檔案位置、檔名稱等等。這裡就不將每個配置檔案貼出來了,Github上有主節點和兩個從節點的配置檔案。

我在redis資料夾下建立一個cluster資料夾,然後在cluster資料夾下建立一個master檔案,存放主節點的配置檔案master.conf和一些其他檔案;再然後建立兩個從節點檔案7001和7002,也是存放配置檔案等。

mkdir cluster
cd cluster
mkdir master 7001 7002
複製程式碼

將配置檔案拷貝到相應資料夾後,根據配置檔案啟動Redis,這裡就不在說明了。

3.4 使用叢集命令列工具redis-trib

我們已經有九個正在執行中的 Redis 例項 ,接下來需要使用這些例項來建立叢集 。Redis中提供叢集命令列工具 redis-trib 來簡化叢集操作

在執行redis-trib.rb檔案之前需要安裝ruby環境,嫌麻煩可以直接執行下面命令

yum install centos-release-scl-rh
yum install rh-ruby23 -y
scl enable rh-ruby23 bash
gem install redis
複製程式碼

注:如果直接執行yum install ruby命令時,再執行gem install redis會顯示redis requires Ruby version >= 2.2.2錯誤

執行命令如下

./redis-trib.rb create --replicas 2 192.168.17.101:6379 192.168.17.102:6379 192.168.17.103:6379 192.168.17.101:7001 192.168.17.101:7002 192.168.17.102:7001 192.168.17.102:7002 192.168.17.103:7001 192.168.17.103:7002
複製程式碼

命令中的create表示建立叢集 ,引數 replicas表示需要一個主節點有幾個從節點 ,後面就是節點ip和埠號。

命令執行後,會輸出已下內容,內容裡面包括主從節點的資訊

接下來需要使用者輸入yes確認

redis系列:叢集

輸入yes後, redis-trib 就會將這份配置應用到叢集當中,讓各個節點開始互相通訊,最後可以得到如下資訊:

redis系列:叢集

當輸出已下內容時,表示叢集環境已經搭建好了

[OK] All 16384 slots covered
複製程式碼

接下來輸入cluster info命令看看叢集狀態,輸出的結果如下

redis系列:叢集

3.5 叢集搭建過程

在使用redis-trib建立叢集時,我們並不知道其內部發生了什麼,接下來我將簡單介紹下其過程。

  1. 接收到redis-trib建立叢集命令後,檢查傳入的master節點數量是否大於等於3個。只有大於3個節點才能組成叢集 。
  2. 計算每個主節點需要分配的槽數量,以及給主節點分配從節點。
  3. 輸出分配的資訊,等待使用者輸入yes確認分配方案。
  4. 使用者輸入yes之後,便開始執行分配方案。
  5. 給主節點分配槽。
  6. 從節點複製主節點。
  7. 讓節點加入到同一個叢集中。(傳送cluster meet 命令)

大致的過程如上,接下來介紹下cluster meet 命令

3.6 CLUSTER MEET 命令

在沒有使用 CLUSTER MEET 命令時,每個節點都是相互獨立的, 它們都處於一個只包含自己的叢集當中,通過使用 CLUSTER MEET 命令可以將各個獨立的節點連線起來, 構成一個包含多個節點的叢集 。使用 CLUSTER MEET 命令的格式如下

CLUSTER MEET <ip> <port>
複製程式碼

看看這個命令的實現

3.7 CLUSTER MEET 命令實現

場景:有A(192.168.17.101)和B(192.168.17.102)兩個節點,在B節點的客戶端輸入CLUSTER MEET 192.168.17.101 6379,表示B加入到A所在的叢集中。A收到命令後便開始執行以下步驟

  1. 節點A為B建立一個節點結構,並新增到自己的節點字典中。
  2. 節點A向節點B傳送一條MEET訊息(訊息後面會說明)
  3. 當節點B收到節點A的MEET訊息時,節點B也會為A建立一個節點結構,並新增到自己的節點字典中。
  4. 節點B向節點A回覆一條PONG訊息。
  5. 當節點A收到節點B回覆的PONG的訊息時,表示節點B已經成功收到自己傳送的MEET訊息。
  6. 這時節點A會向節點B返回PING訊息。
  7. 當節點B收到節點A返回的PING訊息時,握手完成。

節點的握手過程如下

redis系列:叢集

4 故障檢測和轉移

這裡將模擬一個主節點故障,通過向主節點傳送DEBUG SEGFAULT 命令來實現主節點故障效果。

在主節點101上執行DEBUG SEGFAULT命令之後,到103客戶端上檢視節點狀態

redis系列:叢集

可以從上圖中看出101主節點多了個fail狀態,而且103的一個從節點7001變成了主節點。現在再讓101重新上線,再次檢視狀態

redis系列:叢集

可以看到之前的101主節點變成了從節點。好了接下來看看叢集是怎麼發現故障故障如何轉移的。

4.1 故障檢測

叢集是通過什麼方式來發現某個節點出現故障?

答:可以分為如下幾個步驟.

定義:傳送PING訊息的節點-->節點A;接受PING訊息的節點-->節點B

  1. 叢集中的A節點向B節點傳送PING訊息
  2. 如果節點B沒有在NODE_TIMEOUT時間內返回PONG訊息,那麼節點A會將B節點標記為PFAIL** (疑似下線狀態)
  3. 節點 A 通過 gossip 欄位收集到叢集中大部分主節點標識的節點 B 的狀態資訊。
  4. 如果大部分主節點標記節點 B 為 PFAIL 狀態,或者在 NODE_TIMEOUT *FAIL_REPORT_VALIDITY_MULT 這個時間內是處於 PFAIL 狀態。那麼節點A會標記節點 B 為 FAIL (已下線狀態)。
  5. 節點A向所有可達節點傳送一個節點 B的 FAIL 訊息 。

4.2 故障轉移

故障轉移步驟:

  1. 下線主節點的所有從節點,會有一個從節點被選中。

  2. 被選中的從節點會執行SLAVEOF no one命令,成為新的主節點

  3. 新的主節點會撤銷所有對已下線主節點的槽指派,並將這些槽全部指派給自己。

  4. 新的主節點向叢集廣播一條PONG訊息,這條訊息可以讓叢集中的其他節點立即知道這個節點已經由從節點變成了主節點,並且這個主節點已經接管了原本由已下線節點負責處理的槽。

  5. 新的主節點開始接收和自己負責處理的槽有關的命令請求,故障轉移完成。

以下就是發生故障出現日誌內容

2169:M 31 Jul 21:06:20.873 * Marking node 3c29beb7984b40a8c19b580362a0daf29dc349fb as failing (quorum reached).
2169:M 31 Jul 21:06:20.873 # Cluster state changed: fail
2169:M 31 Jul 21:06:21.546 # Failover auth granted to aba761321b40112c0b8de29d810767a40c59d27b for epoch 10
2169:M 31 Jul 21:06:21.586 # Cluster state changed: ok
2169:M 31 Jul 21:13:05.849 * Clear FAIL state for node 3c29beb7984b40a8c19b580362a0daf29dc349fb: master without slots is reachable again.
複製程式碼

這篇文章主要介紹叢集搭建和故障檢測轉移,當然叢集中還有其他知識點像MOVED 重定向、ASK 重定向和重新分片等功能,這些功能官方文件都有相應的資料。

Redis官網:redis.io

Redis中文網:www.redis.cn

本篇的叢集配置檔案:github.com/rainbowda/l…

相關文章