1. 程式人生 > >redis cluster的介紹及搭建(6)

redis cluster的介紹及搭建(6)

redis cluster演算法演變

hash演算法 -> 一致性hash演算法(memcached) -> redis cluster,hash slot演算法

用不同的演算法,就決定了在多個master節點的時候,資料如何分佈到這些節點上去,解決這個問題

1、redis cluster介紹

redis cluster

(1)自動將資料進行分片,每個master上放一部分資料
(2)提供內建的高可用支援,部分master不可用時,還是可以繼續工作的

在redis cluster架構下,每個redis要放開兩個埠號,比如一個是6379,另外一個就是加10000的埠號,比如16379

16379埠號是用來進行節點間通訊的,也就是cluster bus的東西,叢集匯流排。cluster bus的通訊,用來進行故障檢測,配置更新,故障轉移授權

cluster bus用了另外一種二進位制的協議,主要用於節點間進行高效的資料交換,佔用更少的網路頻寬和處理時間

2、最老土的hash演算法和弊端(大量快取重建)

這裡寫圖片描述

3、一致性hash演算法(自動快取遷移)+虛擬節點(自動負載均衡)

一致性hash演算法的講解和優點
這裡寫圖片描述

一致性hash演算法的虛擬節點實現負載均衡
這裡寫圖片描述

4、redis cluster的hash slot演算法

redis cluster有固定的16384個hash slot,對每個key計算CRC16值,然後對16384取模,可以獲取key對應的hash slot

redis cluster中每個master都會持有部分slot,比如有3個master,那麼可能每個master持有5000多個hash slot

hash slot讓node的增加和移除很簡單,增加一個master,就將其他master的hash slot移動部分過去,減少一個master,就將它的hash slot移動到其他master上去

移動hash slot的成本是非常低的

客戶端的api,可以對指定的資料,讓他們走同一個hash slot,通過hash tag來實現
這裡寫圖片描述

5、redis cluster的重要配置

cluster-enabled

6、在三臺機器上啟動6個redis例項

(1)在eshop-cache03上部署目錄

/etc/redis(存放redis的配置檔案),/var/redis/6379(存放redis的持久化檔案)

(2)編寫配置檔案

redis cluster叢集,要求至少3個master,去組成一個高可用,健壯的分散式的叢集,每個master都建議至少給一個slave,3個master,3個slave,最少的要求

正式環境下,建議都是說在6臺機器上去搭建,至少3臺機器

保證,每個master都跟自己的slave不在同一臺機器上,如果是6臺自然更好,一個master+一個slave就死了

3臺機器去搭建6個redis例項的redis cluster

mkdir -p /etc/redis-cluster
mkdir -p /var/log/redis
mkdir -p /var/redis/7001

port 7001
cluster-enabled yes
cluster-config-file /etc/redis-cluster/node-7001.conf
cluster-node-timeout 15000
daemonize yes
pidfile /var/run/redis_7001.pid
dir /var/redis/7001
logfile /var/log/redis/7001.log
bind 192.168.31.187
appendonly yes

至少要用3個master節點啟動,每個master加一個slave節點,先選擇6個節點,啟動6個例項

將上面的配置檔案,在/etc/redis下放6個,分別為: 7001.conf,7002.conf,7003.conf,7004.conf,7005.conf,7006.conf

(3)準備生產環境的啟動指令碼

在/etc/init.d下,放6個啟動指令碼,分別為: redis_7001, redis_7002, redis_7003, redis_7004, redis_7005, redis_7006

每個啟動指令碼內,都修改對應的埠號

(4)分別在3臺機器上,啟動6個redis例項

將每個配置檔案中的slaveof給刪除

7、建立叢集

注意:

也許你執行”gem install redis” 會報redis requires Ruby version >= 2.2.2
這是Centos預設支援ruby到2.0.0,可gem 安裝redis需要最低是2.2.2
解決辦法是 先安裝rvm,再把ruby版本提升至2.3.3
具體解決方案:http://blog.csdn.net/qq1137623160/article/details/79178099

安裝ruby
yum install -y ruby
yum install -y rubygems
gem install redis

cp /usr/local/redis-3.2.8/src/redis-trib.rb /usr/local/bin

redis-trib.rb create –replicas 1 192.168.0.118:7001 192.168.0.118:7002 192.168.0.119:7003 192.168.0.119:7004 192.168.0.120:7005 192.168.0.120:7006

–replicas: 每個master有幾個slave

6臺機器,3個master,3個slave,儘量自己讓master和slave不在一臺機器上

yes

redis-trib.rb check 192.168.31.187:7001

8、讀寫分離+高可用+多master

讀寫分離:每個master都有一個slave
高可用:master宕機,slave自動被切換過去
多master:橫向擴容支援更大資料量

1、實驗多master寫入 -> 海量資料的分散式儲存

你在redis cluster寫入資料的時候,其實是你可以將請求傳送到任意一個master上去執行

但是,每個master都會計算這個key對應的CRC16值,然後對16384個hashslot取模,找到key對應的hashslot,找到hashslot對應的master

如果對應的master就在自己本地的話,set mykey1 v1,mykey1這個key對應的hashslot就在自己本地,那麼自己就處理掉了

但是如果計算出來的hashslot在其他master上,那麼就會給客戶端返回一個moved error,告訴你,你得到哪個master上去執行這條寫入的命令

什麼叫做多master的寫入,就是每條資料只能存在於一個master上,不同的master負責儲存不同的資料,分散式的資料儲存

100w條資料,5個master,每個master就負責儲存20w條資料,分散式資料儲存

高併發、高效能、每日上億流量,redis持久化 -> 災難的時候,做資料恢復,複製 -> 讀寫分離,擴容slave,支撐更高的讀吞吐,redis怎麼支撐讀QPS超過10萬,幾十萬; 哨兵,在redis主從,一主多從,怎麼保證99.99%可用性; redis cluster,海量資料

天下武功,都出自一脈,研究過各種大資料的系統,redis cluster講解了很多原理,跟elasticsearch,很多底層的分散式原理,都是類似的

redis AOF,fsync

elasticsearch建立索引的時候,先寫記憶體快取,每秒鐘把資料刷入os cache,接下來再每隔一定時間fsync到磁碟上去

redis cluster,寫可以到任意master,任意master計算key的hashslot以後,告訴client,重定向,路由到其他mater去執行,分散式儲存的一個經典的做法

elasticsearch,建立索引的時候,也會根據doc id/routing value,做路由,路由到某個其他節點,重定向到其他節點去執行

分散式的一些,hadoop,spark,storm裡面很多核心的思想都是類似的

9、實驗不同master各自的slave讀取 -> 讀寫分離

在這個redis cluster中,如果你要在slave讀取資料,那麼需要帶上readonly指令,get mykey1

redis-cli -c啟動,就會自動進行各種底層的重定向的操作

實驗redis cluster的讀寫分離的時候,會發現有一定的限制性,預設情況下,redis cluster的核心的理念,主要是用slave做高可用的,每個master掛一兩個slave,主要是做資料的熱備,還有master故障時的主備切換,實現高可用的

redis cluster預設是不支援slave節點讀或者寫的,跟我們手動基於replication搭建的主從架構不一樣的

slave node,readonly,get,這個時候才能在slave node進行讀取

redis cluster,主從架構是出來,讀寫分離,複雜了點,也可以做,jedis客戶端,對redis cluster的讀寫分離支援不太好的

預設的話就是讀和寫都到master上去執行的

如果你要讓最流行的jedis做redis cluster的讀寫分離的訪問,那可能還得自己修改一點jedis的原始碼,成本比較高

要不然你就是自己基於jedis,封裝一下,自己做一個redis cluster的讀寫分離的訪問api

核心的思路,就是說,redis cluster的時候,就沒有所謂的讀寫分離的概念了

讀寫分離,是為了什麼,主要是因為要建立一主多從的架構,才能橫向任意擴充套件slave node去支撐更大的讀吞吐量

redis cluster的架構下,實際上本身master就是可以任意擴充套件的,你如果要支撐更大的讀吞吐量,或者寫吞吐量,或者資料量,都可以直接對master進行橫向擴充套件就可以了

也可以實現支撐更高的讀吞吐的效果

不會去跟大家直接講解的,很多東西都要帶著一些疑問,未知,實際經過一些實驗和操作之後,讓你體會的更加深刻一些

redis cluster,主從架構,讀寫分離,沒說錯,沒有撒謊

redis cluster,不太好,server層面,jedis client層面,對master做擴容,所以說擴容master,跟之前擴容slave,效果是一樣的

9、實驗自動故障切換 -> 高可用性

redis-trib.rb check 192.168.31.187:7001

比如把master1,187:7001,殺掉,看看它對應的19:7004能不能自動切換成master,可以自動切換

切換成master後的19:7004,可以直接讀取資料

再試著把187:7001給重新啟動,恢復過來,自動作為slave掛載到了19:7004上面去

10、redis水平擴容

redis cluster模式下,不建議做物理的讀寫分離了

我們建議通過master的水平擴容,來橫向擴充套件讀寫吞吐量,還有支撐更多的海量資料

redis單機,讀吞吐是5w/s,寫吞吐2w/s

擴充套件redis更多master,那麼如果有5臺master,不就讀吞吐可以達到總量25/s QPS,寫可以達到10w/s QPS

redis單機,記憶體,6G,8G,fork類操作的時候很耗時,會導致請求延時的問題

擴容到5臺master,能支撐的總的快取資料量就是30G,40G -> 100臺,600G,800G,甚至1T+,海量資料

redis是怎麼擴容的

11、加入新master

mkdir -p /var/redis/7007

port 7007
cluster-enabled yes
cluster-config-file /etc/redis-cluster/node-7007.conf
cluster-node-timeout 15000
daemonize yes
pidfile /var/run/redis_7007.pid
dir /var/redis/7007
logfile /var/log/redis/7007.log
bind 192.168.31.227
appendonly yes

搞一個7007.conf,再搞一個redis_7007啟動指令碼

手動啟動一個新的redis例項,在7007埠上

redis-trib.rb add-node 192.168.31.227:7007 192.168.31.187:7001

redis-trib.rb check 192.168.31.187:7001

連線到新的redis例項上,cluster nodes,確認自己是否加入了叢集,作為了一個新的master

12、reshard一些資料過去

resharding的意思就是把一部分hash slot從一些node上遷移到另外一些node上

redis-trib.rb reshard 192.168.31.187:7001

要把之前3個master上,總共4096個hashslot遷移到新的第四個master上去

How many slots do you want to move (from 1 to 16384)?

1000

13、新增node作為slave

eshop-cache03

mkdir -p /var/redis/7008

port 7008
cluster-enabled yes
cluster-config-file /etc/redis-cluster/node-7008.conf
cluster-node-timeout 15000
daemonize yes
pidfile /var/run/redis_7008.pid
dir /var/redis/7008
logfile /var/log/redis/7008.log
bind 192.168.31.227
appendonly yes

redis-trib.rb add-node –slave –master-id 28927912ea0d59f6b790a50cf606602a5ee48108 192.168.31.227:7008 192.168.31.187:7001

14、刪除node

先用resharding將資料都移除到其他節點,確保node為空之後,才能執行remove操作

redis-trib.rb del-node 192.168.31.187:7001 bd5a40a6ddccbd46a0f4a2208eb25d2453c2a8db

2個是1365,1個是1366

當你清空了一個master的hashslot時,redis cluster就會自動將其slave掛載到其他master上去

這個時候就只要刪除掉master就可以了

15、slave的自動遷移

比如現在有10個master,每個有1個slave,然後新增了3個slave作為冗餘,有的master就有2個slave了,有的master出現了salve冗餘

如果某個master的slave掛了,那麼redis cluster會自動遷移一個冗餘的slave給那個master

只要多加一些冗餘的slave就可以了

為了避免的場景,就是說,如果你每個master只有一個slave,萬一說一個slave死了,然後很快,master也死了,那可用性還是降低了

但是如果你給整個叢集掛載了一些冗餘slave,那麼某個master的slave死了,冗餘的slave會被自動遷移過去,作為master的新slave,此時即使那個master也死了

還是有一個slave會切換成master的

之前有一個master是有冗餘slave的,直接讓其他master其中的一個slave死掉,然後看有冗餘slave會不會自動掛載到那個master

16、節點間的內部通訊機制

1、基礎通訊原理

(1)redis cluster節點間採取gossip協議進行通訊

跟集中式不同,不是將叢集元資料(節點資訊,故障,等等)集中儲存在某個節點上,而是互相之間不斷通訊,保持整個叢集所有節點的資料是完整的

維護叢集的元資料用得,集中式,一種叫做gossip

集中式:好處在於,元資料的更新和讀取,時效性非常好,一旦元資料出現了變更,立即就更新到集中式的儲存中,其他節點讀取的時候立即就可以感知到; 不好在於,所有的元資料的跟新壓力全部集中在一個地方,可能會導致元資料的儲存有壓力
集中式的叢集元資料儲存和維護
這裡寫圖片描述

gossip協議維護叢集元資料
這裡寫圖片描述

gossip:好處在於,元資料的更新比較分散,不是集中在一個地方,更新請求會陸陸續續,打到所有節點上去更新,有一定的延時,降低了壓力; 缺點,元資料更新有延時,可能導致叢集的一些操作會有一些滯後

我們剛才做reshard,去做另外一個操作,會發現說,configuration error,達成一致

(2)10000埠

每個節點都有一個專門用於節點間通訊的埠,就是自己提供服務的埠號+10000,比如7001,那麼用於節點間通訊的就是17001埠

每隔節點每隔一段時間都會往另外幾個節點發送ping訊息,同時其他幾點接收到ping之後返回pong

(3)交換的資訊

故障資訊,節點的增加和移除,hash slot資訊,等等

2、gossip協議

gossip協議包含多種訊息,包括ping,pong,meet,fail,等等

meet: 某個節點發送meet給新加入的節點,讓新節點加入叢集中,然後新節點就會開始與其他節點進行通訊

redis-trib.rb add-node

其實內部就是傳送了一個gossip meet訊息,給新加入的節點,通知那個節點去加入我們的叢集

ping: 每個節點都會頻繁給其他節點發送ping,其中包含自己的狀態還有自己維護的叢集元資料,互相通過ping交換元資料

每個節點每秒都會頻繁傳送ping給其他的叢集,ping,頻繁的互相之間交換資料,互相進行元資料的更新

pong: 返回ping和meet,包含自己的狀態和其他資訊,也可以用於資訊廣播和更新

fail: 某個節點判斷另一個節點fail之後,就傳送fail給其他節點,通知其他節點,指定的節點宕機了

3、ping訊息深入

ping很頻繁,而且要攜帶一些元資料,所以可能會加重網路負擔

每個節點每秒會執行10次ping,每次會選擇5個最久沒有通訊的其他節點

當然如果發現某個節點通訊延時達到了cluster_node_timeout / 2,那麼立即傳送ping,避免資料交換延時過長,落後的時間太長了

比如說,兩個節點之間都10分鐘沒有交換資料了,那麼整個叢集處於嚴重的元資料不一致的情況,就會有問題

所以cluster_node_timeout可以調節,如果調節比較大,那麼會降低傳送的頻率

每次ping,一個是帶上自己節點的資訊,還有就是帶上1/10其他節點的資訊,傳送出去,進行資料交換

至少包含3個其他節點的資訊,最多包含總節點-2個其他節點的資訊

17、redis在實際生產中出現的一些問題及處理方法

1、fork耗時導致高併發請求延時

RDB和AOF的時候,其實會有生成RDB快照,AOF rewrite,耗費磁碟IO的過程,主程序fork子程序

fork的時候,子程序是需要拷貝父程序的空間記憶體頁表的,也是會耗費一定的時間的

一般來說,如果父程序記憶體有1個G的資料,那麼fork可能會耗費在20ms左右,如果是10G~30G,那麼就會耗費20 * 10,甚至20 * 30,也就是幾百毫秒的時間

info stats中的latest_fork_usec,可以看到最近一次form的時長

redis單機QPS一般在幾萬,fork可能一下子就會拖慢幾萬條操作的請求時長,從幾毫秒變成1秒

優化思路

fork耗時跟redis主程序的記憶體有關係,一般控制redis的記憶體在10GB以內,slave -> master,全量複製

2、AOF的阻塞問題

redis將資料寫入AOF緩衝區,單獨開一個現場做fsync操作,每秒一次

但是redis主執行緒會檢查兩次fsync的時間,如果距離上次fsync時間超過了2秒,那麼寫請求就會阻塞

everysec,最多丟失2秒的資料

一旦fsync超過2秒的延時,整個redis就被拖慢

優化思路

優化硬碟寫入速度,建議採用SSD,不要用普通的機械硬碟,SSD,大幅度提升磁碟讀寫的速度

3、主從複製延遲問題

主從複製可能會超時嚴重,這個時候需要良好的監控和報警機制

在info replication中,可以看到master和slave複製的offset,做一個差值就可以看到對應的延遲量

如果延遲過多,那麼就進行報警

4、主從複製風暴問題

如果一下子讓多個slave從master去執行全量複製,一份大的rdb同時傳送到多個slave,會導致網路頻寬被嚴重佔用

如果一個master真的要掛載多個slave,那儘量用樹狀結構,不要用星型結構
避免複製風暴
這裡寫圖片描述

5、vm.overcommit_memory

0: 檢查有沒有足夠記憶體,沒有的話申請記憶體失敗
1: 允許使用記憶體直到用完為止
2: 記憶體地址空間不能超過swap + 50%

如果是0的話,可能導致類似fork等操作執行失敗,申請不到足夠的記憶體空間

cat /proc/sys/vm/overcommit_memory
echo “vm.overcommit_memory=1” >> /etc/sysctl.conf
sysctl vm.overcommit_memory=1

6、swapiness

cat /proc/version,檢視linux核心版本

如果linux核心版本<3.5,那麼swapiness設定為0,這樣系統寧願swap也不會oom killer(殺掉程序)
如果linux核心版本>=3.5,那麼swapiness設定為1,這樣系統寧願swap也不會oom killer

保證redis不會被殺掉

echo 0 > /proc/sys/vm/swappiness
echo vm.swapiness=0 >> /etc/sysctl.conf

7、最大開啟檔案控制代碼

ulimit -n 10032 10032

自己去上網搜一下,不同的作業系統,版本,設定的方式都不太一樣

8、tcp backlog

cat /proc/sys/net/core/somaxconn
echo 511 > /proc/sys/net/core/somaxconn

來自龍果學院講師:中華石杉