redis系列:集羣

1 簡介

Redis 集羣是Redis 的一個分佈式實現,它是一個網狀結構,每一個節點都經過 TCP 鏈接跟其餘每一個節點鏈接。如今來看看Redis集羣實現了哪些目標?node

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

那麼Redis集羣環境與非分佈式Redis環境在功能上有沒有什麼不一樣的呢?git

  • 集羣的數據庫只有0,且不支持SELECT
  • 因爲集羣將鍵分佈在不一樣的槽(slot)中,因此涉及到多鍵值的複製操做也是不支持的,像set裏的並集(unions)和交集(intersections)操做

2 概念

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

2.1 節點

節點(node)能夠說是構成集羣的基本元素.一般一個Redis集羣中包含了多個節點,每一個節點都有一個惟一的名字。redis

節點名字是一個十六進制表示的160 bit 隨機數,這個隨機數是節點第一次啓動時得到的(一般是用 /dev/urandom)。 節點會把它的ID保存在配置文件裏,之後永遠使用這個ID.若是想要更換ID有以下兩種方式:算法

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

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

節點ID是用於每一個節點。經過節點ID能夠檢測到節點 IP 或端口的變化。centos

若是節點發生了 IP 或端口變化時,其餘節點是如何得知的呢?安全

集羣會使用gossip 協議來發布廣播消息,通知配置變動。ruby

節點與節點之間知道對方的哪些信息?bash

  • 節點的 IP 地址和 TCP 端口號。
  • 各類標識。
  • 節點使用的哈希槽。
  • 最近一次用集羣鏈接發送 ping 包的時間。
  • 最近一次在回覆中收到一個 pong 包的時間。
  • 最近一次標識節點失效的時間。
  • 該節點的從節點個數。
  • 若是該節點是從節點,會有主節點ID信息。(若是它是個主節點則該信息置爲0000000…)

咱們能夠經過使用CLUSTER NODES 命令能夠得到以上的一些信息,以下

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個主節點,每一個主節點都有兩個從節點,架構圖以下

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表示節點超時時間

完整的配置文件在https://github.com/rainbowda/learnWay/tree/master/learnRedis/cluster ,有須要的能夠去下載

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確認

輸入yes後, redis-trib 就會將這份配置應用到集羣當中,讓各個節點開始互相通信,最後能夠獲得以下信息:

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

[OK] All 16384 slots covered

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

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消息時,握手完成。

節點的握手過程以下

4 故障檢測和轉移

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

在主節點101上執行DEBUG SEGFAULT命令以後,到103客戶端上查看節點狀態

能夠從上圖中看出101主節點多了個fail狀態,並且103的一個從節點7001變成了主節點。如今再讓101從新上線,再次查看狀態

能夠看到以前的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官網:https://redis.io

Redis中文網:http://www.redis.cn

本篇的集羣配置文件:https://github.com/rainbowda/learnWay/tree/master/learnRedis/cluster

相關文章
相關標籤/搜索