使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗

redis哨兵+主從的問題

假設咱們在一臺主從機器上配置了200G內存,可是業務需求是須要500G的時候,主從結構+哨兵能夠實現高可用故障切換+冗餘備份,可是並不能解決數據容量的問題,用哨兵,redis每一個實例也是全量存儲,每一個redis存儲的內容都是完整的數據,浪費內存且有木桶效應html

爲了最大化利用內存,能夠採用cluster羣集,就是分佈式存儲。即每臺redis存儲不一樣的內容node

Redis 分佈式方案通常有兩種

1. 客戶端分區方案

優勢是分區邏輯可控,缺點是須要本身處理數據路由、高可用、故障轉移等問題,好比在redis2.8以前一般的作法是獲取某個key的hashcode,而後取餘分佈到不一樣節點,不過這種作法沒法很好的支持動態伸縮性需求,一旦節點的增或者刪操做,都會致使key沒法在redis中命中
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗redis

2. 代理方案

優勢是簡化客戶端分佈式邏輯和升級維護便利,缺點是加劇架構部署複雜度和性能損耗,好比twemproxy、Codis
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗算法

3. 官方爲咱們提供了專有的集羣方案:Redis Cluster

它很是優雅地解決了 Redis 集羣方面的問題,部署方便簡單,所以理解應用好 Redis Cluster 將極大地解放咱們使用分佈式 Redis 的工做量。docker

Redis Cluster

簡介

Redis Cluster 是 Redis 的分佈式解決方案,在3.0版本正式推出,有效地解決了 Redis 分佈式方面的需求。當遇到單機內存、併發、流量等瓶頸時,能夠採用 Cluster 架構方案達到負載均衡的目的
架構圖
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗
在這個圖中,每個藍色的圈都表明着一個redis的服務器節點。它們任何兩個節點之間都是相互連通的。客戶端能夠與任何一個節點相鏈接,而後就能夠訪問集羣中的任何一個節點,對其進行存取和其餘操做數據庫

Redis 集羣提供瞭如下兩個好處:centos

  1. 將數據自動切分到多個節點的能力
  2. 當集羣中的一部分節點失效或者沒法進行通信時, 仍然能夠繼續處理命令請求的能力,擁有自動故障轉移的能力緩存

    redis cluster vs. replication + sentinal如何選擇

    若是你的數據量不多,主要是承載高併發高性能的場景,好比你的緩存通常就幾個G,單機足夠了
    Replication:一個mater,多個slave,要幾個slave跟你的要求的讀吞吐量有關係,結合sentinal集羣,去保證redis主從架構的高可用性,就能夠了
    redis cluster:主要是針對海量數據+高併發+高可用的場景,海量數據,若是你的數據量很大,那麼建議就用redis clusteruby

數據分佈是如何進行的

什麼是數據分佈?數據分佈有兩種方式,順序分區和哈希分區

分佈式數據庫首先要解決把整個數據集按照分區規則映射到多個節點的問題,即把數據集劃分到多個節點上,每一個節點負責總體數據的一個子集bash

順序分佈

順序分佈就是把一整塊數據分散到不少機器中,以下圖所示
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗
順序分佈通常都是平均分配的

哈希分區

以下圖所示,1~100這整塊數字,經過 hash 的函數,取餘產生的數。這樣能夠保證這串數字充分的打散,也保證了均勻的分配到各臺機器上

使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗
哈希分佈和順序分佈只是場景上的適用。哈希分佈不能順序訪問,好比你想訪問1~100,哈希分佈只能遍歷所有數據,同時哈希分佈由於作了 hash 後致使與業務數據無關了

使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗

數據傾斜與數據遷移跟節點伸縮

順序分佈是會致使數據傾斜的,主要是訪問的傾斜。每次點擊會重點訪問某臺機器,這就致使最後數據都到這臺機器上了,這就是順序分佈最大的缺點

但哈希分佈實際上是有個問題的,當咱們要擴容機器的時候,專業上稱之爲「節點伸縮」,這個時候,由於是哈希算法,會致使數據遷移

哈希分區方式

由於redis-cluster使用的就是哈希分區規則因此分析下幾種分區形式
 

一、節點取餘分區

 
使用特定的數據(包括redis的鍵或用戶ID),再根據節點數量N,使用公式:hash(key)%N計算出一個0~(N-1)值,用來決定數據映射到哪個節點上。即哈希值對節點總數取餘。餘數x,表示這條數據存放在第(x+1)個節點上。

缺點:當節點數量N變化時(擴容或者收縮),數據和節點之間的映射關係須要從新計算,這樣的話,按照新的規則映射,要麼以前存儲的數據找不到,要麼以前數據被從新映射到新的節點(致使之前存儲的數據發生數據遷移)

實踐:經常使用於數據庫的分庫分表規則,通常採用預分區的方式,提早根據數據量規劃好分區數,好比劃分爲512或1024張表,保證可支撐將來一段時間的數據量,再根據負載狀況將表遷移到其餘數據庫中
 

二、一致性哈希

 
一致性哈希分區(Distributed Hash Table)實現思路是爲系統中每一個節點分配一個 token,範圍通常在0~232,這些 token 構成一個哈希環。數據讀寫執行節點查找操做時,先根據 key 計算 hash 值,而後順時針找到第一個大於等於該哈希值的 token 節點
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗

上圖就是一個一致性哈希的原理解析。
 
假設咱們有 n1~n4 這四臺機器,咱們對每一臺機器分配一個惟一 token,每次有數據(圖中×××表明數據),一致性哈希算法規定每次都順時針漂移數據,也就是圖中×××的數 據都指向 n3。
 
這個時候咱們須要增長一個節點 n5,在 n2 和 n3 之間,數據仍是會發生漂移(會偏移到大於等於的節點),可是這個時候你是否注意到,其實只有 n2~n3 這部分的數據被漂移,其餘的數據都是不會變的,這種方式相比節點取餘最大的好處在於加入和刪除節點隻影響哈希環中相鄰的節點,對其餘節點無影響
 
缺點:每一個節點的負載不相同,由於每一個節點的hash是根據key計算出來的,換句話說就是假設key足夠多,被hash算法打散得很是均勻,可是節點過少,致使每一個節點處理的key個數不太同樣,甚至相差很大,這就會致使某些節點壓力很大
 
實踐:加減節點會形成哈希環中部分數據沒法命中,須要手動處理或者忽略這部分數據,所以一致性哈希經常使用於緩存場景。
 

3.虛擬槽分區

 
虛擬槽分區巧妙地使用了哈希空間,使用分散度良好的哈希函數把全部數據映射到一個固定範圍的整數集合中,整數定義爲槽(slot)。這個範圍通常遠遠大於節點數,好比 Redis Cluster 槽範圍是0~16383。槽是集羣內數據管理和遷移的基本單位。採用大範圍槽的主要目的是爲了方便數據拆分和集羣擴展。每一個節點會負責必定數量的槽,下圖所示。

當前集羣有5個節點,每一個節點平均大約負責3276個槽。因爲採用高質量的哈希算法,每一個槽所映射的數據一般比較均勻,將數據平均劃分到5個節點進行數據分區。Redis Cluster 就是採用虛擬槽分區,下面就介紹 Redis 數據分區方法。

使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗

每當 key 訪問過來,Redis Cluster 會計算哈希值是否在這個區間裏。它們彼此都知道對應的槽在哪臺機器上,這樣就能作到平均分配了
 

搭建集羣

手動

 
介紹完 Redis 集羣分區規則以後,下面咱們開始搭建 Redis 集羣。搭建集羣工做須要如下三個步驟:
 

  1. 準備節點
  2. 節點握手
  3. 分配槽
     
    節點規劃
容器名稱 容器 IP 地址 映射端口號 服務運行模式
redis-master10 172.50.0.10 6400->6400、16400->16400 master
redis-master11 172.50.0.11 6401->640一、16401->16401 master
redis-master12 172.50.0.12 6402->6402 master
redis-slave10 172.30.0.10 6403->6403 slave
redis-slave11 172.30.0.11 6404->6404 slave
redis-slave12 172.30.0.12 6405->6405 slave

 

1. 準備節點

 
Redis 集羣通常由多個節點組成,節點數量至少爲6個才能保證組成完整高可用的集羣。每一個節點須要開啓配置 cluster-enabled yes,讓 Redis 運行在集羣模式下,上面的配置都相應的給到redis的配置文件當中並啓動。

其餘配置和單機模式一致便可,配置文件命名規則 redis-{port}.conf,準備好配置後啓動全部節點,第一次啓動時若是沒有集羣配置文件,它會自動建立一份,文件名稱採用 cluster-config-file 參數項控制,建議採用 node-{port}.conf 格式定義,也就是說會有兩份配置文件
 
當集羣內節點信息發生變化,如添加節點、節點下線、故障轉移等。節點會自動保存集羣狀態到配置文件中。須要注意的是,Redis 自動維護集羣配置文件,不要手動修改,防止節點重啓時產生集羣信息錯亂

配置文件信息以下:
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗

文件內容記錄了集羣初始狀態,這裏最重要的是節點 ID,它是一個40位16進制字符串,用於惟一標識集羣內一個節點,節點 ID 在集羣初始化時只建立一次,節點重啓時會加載集羣配置文件進行重用,結合作相應的集羣操做,而 Redis 的運行 ID 每次重啓都會變化

構建鏡像用到的 Dockerfile 信息

FROM centos:latest
MAINTAINER peter "ttone2@vip.qq.com"
RUN groupadd -r redis && useradd  -r -g redis redis
RUN  yum -y update &&  yum -y install epel-release \
&&   yum -y install redis  && yum -y install wget \
&&   yum -y install net-tools \
&&   yum -y install  ruby && yum  -y install  rubygems
RUN wget https://rubygems.org/downloads/redis-3.2.1.gem  &&  gem install -l ./redis-3.2.1.gem \
&&  rm -f redis-3.2.1.gem
COPY  ./config/redis-trib.rb  /usr/bin
COPY  ./config/redis.sh       /usr/bin
RUN  mkdir -p /config  && chmod  775  /usr/bin/redis.sh && chmod 775 /usr/bin/redis-trib.rb

建立單獨的網絡

主節點網卡
docker  network create  -d  bridge  --subnet    172.50.0.0/16 redis-cluster_redis-master

從節點網卡
docker  network create  -d  bridge  --subnet    172.30.0.0/16 redis-cluster_redis-salve

依次執行新建容器命令:

docker run -itd --name redis-master10 -v /home/work/redis-cluster/config/:/config --workdir /config --net redis-cluster_redis-master -e PORT=6400 -p 6400:6400 -p 16400:16400 --ip 172.50.0.10 redis-cluster
docker run -itd --name redis-master11 -v /home/work/redis-cluster/config/:/config --workdir /config --net redis-cluster_redis-master -e PORT=6401 -p 6401:6401 -p 16401:16401 --ip 172.50.0.11 redis-cluster
...
docker run -itd --name redis-slave11 -v /home/work/redis-cluster/config/:/config --workdir /config --net redis-cluster_redis-slave -e PORT=6404 -p 6404:6404 -p 16404:16404 --ip 172.30.0.11 redis-cluster
docker run -itd --name redis-slave12 -v /home/work/redis-cluster/config/:/config --workdir /config --net redis-cluster_redis-slave -e PORT=6405 -p 6405:6405 -p 16405:16405 --ip 172.30.0.12 redis-cluster

因爲使用的命令行方式建立的,entrypoint和CMD命令沒辦法獲得執行,因此須要進入到每臺機器裏去開啓 Redis 服務.

[root@izuf64gdegum84eku07pljz config]# docker exec -it redis-master11 bash
[root@c101670ac2f4 config]# ./redis.sh &
2.節點握手

節點握手是指一批運行在集羣模式下的節點經過 Gossip 協議彼此通訊,達到感知對方的過程。節點握手是集羣彼此通訊的第一步,由客戶端發起命令:cluster meet{ip}{port}

關於Gossip能夠看看文章的介紹

經過命令 cluster meet 127.0.0.1 6380讓節點6379和6380節點進行握手通訊。cluster meet 命令是一個異步命令,執行以後馬上返回。內部發起與目標節點進行握手通訊
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗
1)節點6379本地建立6380節點信息對象,併發送 meet 消息
2)節點6380接受到 meet 消息後,保存6379節點信息並回復 pong 消息
3)以後節點6379和6380彼此按期經過 ping/pong 消息進行正常的節點通訊

[root@63d7fd48dc81 config]# redis-cli -p 6400
127.0.0.1:6400> cluster meet 47.101.139.0 6401
OK
127.0.0.1:6400> cluster meet 47.101.139.0 6402
OK
127.0.0.1:6400> cluster meet 47.101.139.0 6403
OK
127.0.0.1:6400> cluster meet 47.101.139.0 6404
OK
127.0.0.1:6400> cluster meet 47.101.139.0 6405
OK
127.0.0.1:6400>

經過cluster nodes 命令確認6個節點都彼此感知並組成集羣
使用Docker 手動&redis-trib.rb方式建立 Redis-Cluster 實驗

注意:
一、每一個Redis Cluster節點會佔用兩個TCP端口,一個監聽客戶端的請求,默認是6379,另一個在前一個端口加上10000,好比16379,來監聽數據的請求,節點和節點之間會監聽第二個端口,用一套二進制協議來通訊。
節點之間會經過套協議來進行失敗檢測,配置更新,failover認證等等。
爲了保證節點之間正常的訪問,須要注意防火牆的配置。
二、節點創建握手以後集羣還不能正常工做,這時集羣處於下線狀態,全部的數據讀寫都被禁止

3.設置從節點

做爲一個完整的集羣,須要主從節點,保證當它出現故障時能夠自動進行故障轉移。集羣模式下,Reids 節點角色分爲主節點和從節點。
首次啓動的節點和被分配槽的節點都是主節點,從節點負責複製主節點槽信息和相關的數據。
使用 cluster replicate {nodeId} 命令讓一個節點成爲從節點。其中命令執行必須在對應的從節點上執行,將當前節點設置爲 node_id 指定的節點的從節點

[root@izuf64gdegum84eku07pljz config]# redis-cli -h 47.101.139.0 -p 6403 cluster replicate 1a30f25427cf9ca0a8362a50e6a313b933fe7df2
[root@izuf64gdegum84eku07pljz config]# redis-cli -h 47.101.139.0 -p 6404 cluster replicate b99ed66e154468f0d2fc21ff4d7113bf0424262b
[root@izuf64gdegum84eku07pljz config]# redis-cli -h 47.101.139.0 -p 6405 cluster replicate ba4547686e34ab121a8226291eb5ab019c53c5f8

結果:

127.0.0.1:6401> cluster nodes
056b42451d574aa383464a4cb1b50b1511d6ebe5 47.101.139.0:6404 slave b99ed66e154468f0d2fc21ff4d7113bf0424262b 0 1555686394258 4 connected
ba4547686e34ab121a8226291eb5ab019c53c5f8 47.101.139.0:6402 master - 0 1555686390247 0 connected
2a2676234dcd3b6991dd4491e61feebbdb689c69 47.101.139.0:6403 slave 1a30f25427cf9ca0a8362a50e6a313b933fe7df2 0 1555686394759 3 connected
1a30f25427cf9ca0a8362a50e6a313b933fe7df2 47.101.139.0:6400 master - 0 1555686395260 1 connected
0dc1306db8bde9868846dacc631c80f6abe85ff3 47.101.139.0:6405 slave ba4547686e34ab121a8226291eb5ab019c53c5f8 0 1555686393255 5 connected
b99ed66e154468f0d2fc21ff4d7113bf0424262b 172.50.0.11:6401 myself,master - 0 0 2 connected
4. 分配槽

Redis 集羣把全部的數據映射到16384個槽中。每一個 key 會映射爲一個固定的槽,只有當節點分配了槽,才能響應和這些槽關聯的鍵命令。經過 cluster addslots 命令爲節點分配槽

利用 bash 特性批量設置槽(slots),命令以下:

[root@izuf64gdegum84eku07pljz config]# redis-cli -h 47.101.139.0 -p 6400 cluster addslots {0..5461}
[root@izuf64gdegum84eku07pljz config]# redis-cli -h 47.101.139.0 -p 6401 cluster addslots {5462..10922}
[root@izuf64gdegum84eku07pljz config]# redis-cli -h 47.101.139.0 -p 6402 cluster addslots {10923..16383}

主節點的槽信息:

127.0.0.1:6401> cluster nodes
056b42451d574aa383464a4cb1b50b1511d6ebe5 47.101.139.0:6404 slave b99ed66e154468f0d2fc21ff4d7113bf0424262b 0 1555687118050 4 connected
ba4547686e34ab121a8226291eb5ab019c53c5f8 47.101.139.0:6402 master - 0 1555687118550 0 connected 10923-16383
2a2676234dcd3b6991dd4491e61feebbdb689c69 47.101.139.0:6403 slave 1a30f25427cf9ca0a8362a50e6a313b933fe7df2 0 1555687115546 3 connected
1a30f25427cf9ca0a8362a50e6a313b933fe7df2 47.101.139.0:6400 master - 0 1555687117549 1 connected 0-5461
0dc1306db8bde9868846dacc631c80f6abe85ff3 47.101.139.0:6405 slave ba4547686e34ab121a8226291eb5ab019c53c5f8 0 1555687116547 5 connected
b99ed66e154468f0d2fc21ff4d7113bf0424262b 172.50.0.11:6401 myself,master - 0 0 2 connected 5462-10922

咱們依照 Redis 協議手動創建一個集羣。它由6個節點構成,3個主節點負責處理槽和相關數據,3個從節點負責故障轉移。手動搭建集羣便於理解集羣創建的流程和細節,可是咱們從中發現集羣搭建須要不少步驟,當集羣節點衆多時,必然會加大搭建集羣的複雜度和運維成本。所以 Redis 官方提供了 redis-trib.rb 工具方便咱們快速搭建集羣

5. 操做集羣

-c 集羣模式

[root@izuf64gdegum84eku07pljz config]# redis-cli -c -h 47.101.139.0 -p 6400 set mayun alibaba-996
[root@izuf64gdegum84eku07pljz config]# redis-cli -c -h 47.101.139.0 -p 6400 get mayun
"alibaba-996"

若是沒有指定集羣模式,那麼會出現以下錯誤

[root@izuf64gdegum84eku07pljz config]# redis-cli -h 47.101.139.0 -p 6400 set mayun alibaba-996
(error) MOVED 11165 47.101.139.0:6402

全部命令:

CLUSTER info:打印集羣的信息
CLUSTER nodes:列出集羣當前已知的全部節點(node)的相關信息
CLUSTER meet <ip> <port>:將ip和port所指定的節點添加到集羣當中
CLUSTER addslots <slot> [slot ...]:將一個或多個槽(slot)指派(assign)給當前節點
CLUSTER delslots <slot> [slot ...]:移除一個或多個槽對當前節點的指派
CLUSTER slots:列出槽位、節點信息
CLUSTER slaves <node_id>:列出指定節點下面的從節點信息
CLUSTER replicate <node_id>:將當前節點設置爲指定節點的從節點
CLUSTER saveconfig:手動執行命令保存保存集羣的配置文件,集羣默認在配置修改的時候會自動保存配置文件
CLUSTER keyslot <key>:列出key被放置在哪一個槽上
CLUSTER flushslots:移除指派給當前節點的全部槽,讓當前節點變成一個沒有指派任何槽的節點
CLUSTER countkeysinslot <slot>:返回槽目前包含的鍵值對數量
CLUSTER getkeysinslot <slot> <count>:返回count個槽中的鍵
CLUSTER setslot <slot> node <node_id> 將槽指派給指定的節點,若是槽已經指派給另外一個節點,那麼先讓另外一個節點刪除該槽,而後再進行指派
CLUSTER setslot <slot> migrating <node_id> 將本節點的槽遷移到指定的節點中
CLUSTER setslot <slot> importing <node_id> 從 node_id 指定的節點中導入槽 slot 到本節點
CLUSTER setslot <slot> stable 取消對槽 slot 的導入(import)或者遷移(migrate)
CLUSTER failover:手動進行故障轉移
CLUSTER forget <node_id>:從集羣中移除指定的節點,這樣就沒法完成握手,過時時爲60s,60s後兩節點又會繼續完成握手
CLUSTER reset [HARD|SOFT]:重置集羣信息,soft是清空其餘節點的信息,但不修改本身的id,hard還會修改本身的id,不傳該參數則使用soft方式
CLUSTER count-failure-reports <node_id>:列出某個節點的故障報告的長度
CLUSTER SET-CONFIG-EPOCH:設置節點epoch,只有在節點加入集羣前才能設置

redis-trib.rb 搭建集羣

redis-trib.rb 是採用 Ruby 實現的 Redis 集羣管理工具。內部經過 Cluster 相關命令幫咱們簡化集羣建立、檢查、槽遷移和均衡等常見運維操做,使用以前須要安裝 Ruby 依賴環境。下面介紹搭建集羣的詳細步驟

內部經過 Cluster 相關命令幫咱們簡化集羣建立、檢查、槽遷移和均衡等常見運維操做,使用以前須要安裝 Ruby 依賴環境,相關擴展這個在dockerfile文件當中已經寫了指令,查看下理解意思就能夠了

啓動好6個節點以後,使用 redis-trib.rb create 命令完成節點握手和槽分配過程

redis-trib.rb create --replicas 1 47.101.139.0:6400  47.101.139.0:6401  47.101.139.0:6402 47.101.139.0:6403 47.101.139.0:6404 47.101.139.0:6405

--replicas 參數指定集羣中每一個主節點配備幾個從節點,這裏設置爲1,redis-trib.rb 會盡量保證主從節點不分配在同一機器下,所以會從新排序節點列表順序。節點列表順序用於肯定主從角色,先主節點以後是從節點。建立過程當中首先會給出主從節點角色分配的計劃,而且會生成報告

命令說明:
 redis-trib.rb help
Usage: redis-trib <command> <options> <arguments ...>

#建立集羣
create          host1:port1 ... hostN:portN  
                  --replicas <arg> #帶上該參數表示是否有從,arg表示從的數量
#檢查集羣
check           host:port
#查看集羣信息
info            host:port
#修復集羣
fix             host:port
                  --timeout <arg>
#在線遷移slot  
reshard         host:port       #個是必傳參數,用來從一個節點獲取整個集羣信息,至關於獲取集羣信息的入口
                  --from <arg>  #須要從哪些源節點上遷移slot,可從多個源節點完成遷移,以逗號隔開,傳遞的是節點的node id,還能夠直接傳遞--from all,這樣源節點就是集羣的全部節點,不傳遞該參數的話,則會在遷移過程當中提示用戶輸入
                  --to <arg>    #slot須要遷移的目的節點的node id,目的節點只能填寫一個,不傳遞該參數的話,則會在遷移過程當中提示用戶輸入。
                  --slots <arg> #須要遷移的slot數量,不傳遞該參數的話,則會在遷移過程當中提示用戶輸入。
                  --yes         #設置該參數,能夠在打印執行reshard計劃的時候,提示用戶輸入yes確認後再執行reshard
                  --timeout <arg>  #設置migrate命令的超時時間。
                  --pipeline <arg> #定義cluster getkeysinslot命令一次取出的key數量,不傳的話使用默認值爲10。
#平衡集羣節點slot數量  
rebalance       host:port
                  --weight <arg>
                  --auto-weights
                  --use-empty-masters
                  --timeout <arg>
                  --simulate
                  --pipeline <arg>
                  --threshold <arg>
#將新節點加入集羣 
add-node        new_host:new_port existing_host:existing_port
                  --slave
                  --master-id <arg>
#從集羣中刪除節點
del-node        host:port node_id
#設置集羣節點間心跳鏈接的超時時間
set-timeout     host:port milliseconds
#在集羣所有節點上執行命令
call            host:port command arg arg .. arg
#將外部redis數據導入集羣
import          host:port
                  --from <arg>
                  --copy
                  --replace
相關文章
相關標籤/搜索