1. 玩轉Redis的高可用(主從、哨兵、集群)
所謂的高可用,也叫 HA(High Availability),是分布式系統架構設計中必須考慮的因素之一,它是保證系統SLA的重要指標。Redis 高可用的主要有三種模式: 主從模式 , 哨兵模式和集群模式 。
Redis 提供了 Redis 提供了復制(replication)功能,當一台 redis 資料庫中的數據發生了變化,這個變化會被自動地同步到其答鍵念他的 redis 機器上去。
Redis 多機器部署時,這些機器節點會被分成兩類,一類是主節點(master 節點),一類是從節點(slave 節點)。一般 主節點可以進行讀、寫操作 ,而 從節點只能進行讀操作 。一個主節點可以有多個從節點,但是一個從節點只會有一個主節點,也就是所謂的 一主多從結構 。
· 支持主從復制,主機會自動將數據同步到從機,可以進行讀寫分離;
· Master 是以非阻塞的方式為主 Slaves 提供服務。所以在 Master-Slave 同步期間,客戶端仍然可以提交查詢或修改請求;
· Slave 同樣是以非阻塞的方式完成數據同步。在同步期間,如果有客戶端提交查詢請求,Redis 則返回同步之前的數據。
· Redis 不具備自動容錯和恢復功能,主機從機的宕機都會導致前端部分讀寫請求失敗,需要等待機器重啟或者手動切換前端的 IP 才能恢復;
· 主機宕機,宕機前有部分數據未能及時同步到從機,切換 IP 後面還會引入數據不一致的問題,降低了系統的可用性;
· Redis 較難支持在線擴容,在集群容量達到上限時在線擴容會變得很復雜;
· Redis 的主節點和亮塵從節點中的數據是一樣的,降低的內存的可用性
實際生產中,我們優先考慮哨兵模式。這種模式下,master 宕機,哨兵會自動選舉 master 並將其他的 slave 指向新的 master。
在主從模式下,redis 同時提供了哨兵命令 redis-sentinel ,哨兵是一個獨立的進程,作為進程,它會獨立運行。其原理是哨兵進程向所有的 redis 機器人發送命令,等待 Redis 伺服器響應,從而監控運行的多個 Redis 實例。一般為了便於決策選舉,使用 奇數個哨兵 。多個哨兵構成一個哨兵集群,哨兵直接也會相互通信,檢查哨兵是否正常運行,同時發現 master 戰機哨兵之間會進行決策選舉新的 master
哨兵模式的作用:
· 通過發送命令,讓 Redis 伺服器返回監控其運行狀態,包括主伺服器和從伺服器;
· 然而一個哨兵進程對 Redis 伺服器進行監控,也可能會清困出現問題,為此,我們可以使用多個哨兵進行監控。各個哨兵之間還會進行監控,這樣就形成了多種哨兵模式。
哨兵很像 kafka 集群中的 zookeeper 的功能。
· 哨兵模式是基於主從模式的,所有主從的優點,哨兵模式都具有。
· 主從可以自動切換,系統更健壯,可用性更高。
· 具有主從模式的缺點,每台機器上的數據是一樣的,內存的可用性較低。
· Redis 較難支持在線擴容,在集群容量達到上限時在線擴容會變得很復雜。
Redis 集群模式本身沒有使用一致性 hash 演算法,而是使用 slots 插槽 。
Redis 哨兵模式基本已經可以實現高可用,讀寫分離 ,但是在這種模式下每台 Redis 伺服器都存儲相同的數據,很浪費內存,所以在 redis3.0 上加入了 Cluster 集群模式,實現了 Redis 的分布式存儲,對數據進行分片,也就是說每台 Redis 節點上存儲不同的內容;每個節點都會通過集群匯流排(cluster bus),與其他的節點進行通信。 通訊時使用特殊的埠號,即對外服務埠號加 10000。例如如果某個 node 的埠號是 6379,那麼它與其它 nodes 通信的埠號是 16379。nodes 之間的通信採用特殊的二進制協議。
對客戶端來說,整個 cluster 被看做是一個整體,客戶端可以連接任意一個 node 進行操作,就像操作單一 Redis 實例一樣, 當客戶端操作的時候 key 沒有分配到該 node 上時,Redis 會返回轉向指令,指向正確的 node,這有點兒像瀏覽器頁面的 302 redirect 跳轉。
根據官方推薦,集群部署至少要 3 台以上的 master 節點,最好使用 3 主 3 從六個節點的模式。
在 Redis 的每一個節點上,都有這么兩個東西, 一個是插槽(slot),它的的取值范圍是:0-16383, 可以從上面 redis-trib.rb 執行的結果看到這 16383 個 slot 在三個 master 上的分布。還有一個就是 cluster,可以理解為是一個集群管理的插件,類似的哨兵。
當我們的存取的 Key 到達的時候,Redis 會根據 crc16 的演算法對計算後得出一個結果,然後把結果和 16384 求余數,這樣每個 key 都會對應一個編號在 0-16383 之間的哈希槽,通過這個值,去找到對應的插槽所對應的節點,然後直接自動跳轉到這個對應的節點上進行存取操作。
為了保證高可用, redis-cluster 集群引入了主從模式 ,一個主節點對應一個或者多個從節點。當其它主節點 ping 主節點 master 1 時,如果半數以上的主節點與 master 1 通信超時,那麼認為 master 1 宕機了,就會啟用 master 1 的從節點 slave 1,將 slave 1 變成主節點繼續提供服務。
如果 master 1 和它的從節點 slave 1 都宕機了,整個集群就會進入 fail 狀態,因為集群的 slot 映射不完整。 如果集群超過半數以上的 master 掛掉,無論是否有 slave,集群都會進入 fail 狀態。
redis-cluster 採用去中心化的思想 ,沒有中心節點的說法,客戶端與 Redis 節點直連,不需要中間代理層,客戶端不需要連接集群所有節點,連接集群中任何一個可用節點即可。
對 redis 集群的擴容就是向集群中添加機器,縮容就是從集群中刪除機器,並重新將 16383 個 slots 分配到集群中的節點上(數據遷移)。
擴縮容也是使用集群管理工具 redis-tri.rb。
擴容時,先使用 redis-tri.rb add-node 將新的機器加到集群中,這是新機器雖然已經在集群中了,但是沒有分配 slots,依然是不起做用的。在使用 redis-tri.rb reshard 進行分片重哈希(數據遷移),將舊節點上的 slots 分配到新節點上後,新節點才能起作用。
縮容時,先要使用 redis-tri.rb reshard 移除的機器上的 slots,然後使用 redis-tri.rb add-del 移除機器。
採用去中心化思想,數據按照 slot 存儲分布在多個節點,節點間數據共享,可動態調整數據分布;
可擴展性:可線性擴展到 1000 多個節點,節點可動態添加或刪除;
高可用性:部分節點不可用時,集群仍可用。通過增加 Slave 做 standby 數據副本,能夠實現故障自動 failover,節點之間通過 gossip 協議交換狀態信息,用投票機制完成 Slave 到 Master 的角色提升;
降低運維成本,提高系統的擴展性和可用性。
1.Redis Cluster 是無中心節點的集群架構,依靠 Goss 協議(謠言傳播)協同自動化修復集群的狀態。但 GosSIp 有消息延時和消息冗餘的問題,在集群節點數量過多的時候,節點之間需要不斷進行 PING/PANG 通訊,不必須要的流量佔用了大量的網路資源。雖然 Reds4.0 對此進行了優化,但這個問題仍然存在。
2.數據遷移問題
Redis Cluster 可以進行節點的動態擴容縮容,這一過程,在目前實現中,還處於半自動狀態,需要人工介入。在擴縮容的時候,需要進行數據遷移。
而 Redis 為了保證遷移的一致性,遷移所有操作都是同步操作 ,執行遷移時,兩端的 Redis 均會進入時長不等的阻塞狀態,對於小 Key,該時間可以忽略不計,但如果一旦 Key 的內存使用過大,嚴重的時候會接觸發集群內的故障轉移,造成不必要的切換。
主從模式:master 節點掛掉後,需要手動指定新的 master,可用性不高,基本不用。
哨兵模式:master 節點掛掉後,哨兵進程會主動選舉新的 master,可用性高,但是每個節點存儲的數據是一樣的,浪費內存空間。數據量不是很多,集群規模不是很大,需要自動容錯容災的時候使用。
集群模式:數據量比較大,QPS 要求較高的時候使用。 Redis Cluster 是 Redis 3.0 以後才正式推出,時間較晚,目前能證明在大規模生產環境下成功的案例還不是很多,需要時間檢驗。
2. Redis怎麼做集群
為什麼集群?
通常,為了提高網站響應速度,總是把熱點數據保存在內存中而不是直接從後端資料庫中讀取。Redis是一個很好的Cache工具。大型網站應用,熱點數據量往往巨大,幾十G上百G是很正常的事兒,在這種情況下,如何正確架構Redis呢?
首先,無論我們是使用自己的物理主機,還是使用雲服務主機,內存資源往往是有限制的,scale up不是一個好辦法,我們需要scale out橫向可伸縮擴展,這需要由多台主機協同提供服務,即分布式多個Redis實例協同運行。
其次,目前硬體資源成本降低,多核CPU,幾十G內存的主機很普遍,對於主進程是單線程工作的Redis,只運行一個實例就顯得有些浪費。同時,管理一個巨大內存不如管理相對較小的內存高效。因此,實際使用中,通常一台機器上同時跑多個Redis實例。
方案
1.Redis官方集群方案 Redis Cluster
Redis Cluster是一種伺服器Sharding技術,3.0版本開始正式提供。
Redis Cluster中,Sharding採用slot(槽)的概念,一共分成16384個槽,這有點兒類pre sharding思路。對於每個進入Redis的鍵值對,根據key進行散列,分配到這16384個slot中的某一個中。使用的hash演算法也比較簡單,就是CRC16後16384取模。
Redis集群中的每個node(節點)負責分攤這16384個slot中的一部分,也就是說,每個slot都對應一個node負責處理。當動態添加或減少node節點時,需要將16384個槽做個再分配,槽中的鍵值也要遷移。當然,這一過程,在目前實現中,還處於半自動狀態,需要人工介入。
Redis集群,要保證16384個槽對應的node都正常工作,如果某個node發生故障,那它負責的slots也就失效,整個集群將不能工作。
為了增加集群的可訪問性,官方推薦的方案是將node配置成主從結構,即一個master主節點,掛n個slave從節點。這時,如果主節點失效,Redis Cluster會根據選舉演算法從slave節點中選擇一個上升為主節點,整個集群繼續對外提供服務。這非常類似前篇文章提到的Redis Sharding場景下伺服器節點通過Sentinel監控架構成主從結構,只是Redis Cluster本身提供了故障轉移容錯的能力。
Redis Cluster的新節點識別能力、故障判斷及故障轉移能力是通過集群中的每個node都在和其它nodes進行通信,這被稱為集群匯流排(cluster bus)。它們使用特殊的埠號,即對外服務埠號加10000。例如如果某個node的埠號是6379,那麼它與其它nodes通信的埠號是16379。nodes之間的通信採用特殊的二進制協議。
對客戶端來說,整個cluster被看做是一個整體,客戶端可以連接任意一個node進行操作,就像操作單一Redis實例一樣,當客戶端操作的key沒有分配到該node上時,Redis會返回轉向指令,指向正確的node,這有點兒像瀏覽器頁面的302 redirect跳轉。
Redis Cluster是Redis 3.0以後才正式推出,時間較晚,目前能證明在大規模生產環境下成功的案例還不是很多,需要時間檢驗。
2.Redis Sharding集群
Redis 3正式推出了官方集群技術,解決了多Redis實例協同服務問題。Redis Cluster可以說是服務端Sharding分片技術的體現,即將鍵值按照一定演算法合理分配到各個實例分片上,同時各個實例節點協調溝通,共同對外承擔一致服務。
多Redis實例服務,比單Redis實例要復雜的多,這涉及到定位、協同、容錯、擴容等技術難題。這里,我們介紹一種輕量級的客戶端Redis Sharding技術。
Redis Sharding可以說是Redis Cluster出來之前,業界普遍使用的多Redis實例集群方法。其主要思想是採用哈希演算法將Redis數據的key進行散列,通過hash函數,特定的key會映射到特定的Redis節點上。這樣,客戶端就知道該向哪個Redis節點操作數據。
慶幸的是,java redis客戶端驅動jedis,已支持Redis Sharding功能,即ShardedJedis以及結合緩存池的ShardedJedisPool。
Jedis的Redis Sharding實現具有如下特點:
1. 採用一致性哈希演算法(consistent hashing),將key和節點name同時hashing,然後進行映射匹配,採用的演算法是MURMUR_HASH。採用一致性哈希而不是採用簡單類似哈希求模映射的主要原因是當增加或減少節點時,不會產生由於重新匹配造成的rehashing。一致性哈希隻影響相鄰節點key分配,影響量小。
2.為了避免一致性哈希隻影響相鄰節點造成節點分配壓力,ShardedJedis會對每個Redis節點根據名字(沒有,Jedis會賦予預設名字)會虛擬化出160個虛擬節點進行散列。根據權重weight,也可虛擬化出160倍數的虛擬節點。用虛擬節點做映射匹配,可以在增加或減少Redis節點時,key在各Redis節點移動再分配更均勻,而不是只有相鄰節點受影響。
3.ShardedJedis支持keyTagPattern模式,即抽取key的一部分keyTag做sharding,這樣通過合理命名key,可以將一組相關聯的key放入同一個Redis節點,這在避免跨節點訪問相關數據時很重要。