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
來自龍果學院講師:中華石杉