Redis集羣介紹和搭建

什麼是Redis集羣

簡介

Redis是一個快速高效的NoSQL型數據庫,由於其基於內存存儲、單線程、多路IO複用的特性,其QPS可以達到驚人的100000+(官方數據),但是即使有這麼高的速度,在中國這麼大的網民基數環境下,也存在着性能瓶頸。

首先拋開服務器故障不談,Redis集羣首先可以使Redis性能得到線性提高,這是毋庸置疑的,其次Redis集羣除了解決了效率問題,還可以解決服務器宕機造成的數據丟失問題,當某個Redis節點宕機,剩下的節點會繼續工作,並不會影響整體集羣的使用,從而實現高可用

Redis單機模式有什麼問題

單機故障

在單機模式下的Redis,我們的應用中所有需要緩存的數據都依賴一臺Redis服務器,應用的流量小可能看不出什麼問題,但是隨着應用越來越大,流量越來越大,如果出現服務器宕機或者斷電的狀況,那麼我們的應用整個一個緩存層在一段時間內(重啓)都將不復存在。

先不談基於Redis的分佈式Session可能造成的問題,如果恰好遇上流量高峯,這些流量直接打在數據庫上,我們知道數據庫的IO效率遠不及Redis,這將大大提高應用負載,容易出現數據庫服務器的宕機,從而造成應用的宕機。

由此看來,單機版Redis如果出現故障,將有可能引起一系列的連鎖反應,造成不可逆的損失。

容量瓶頸

我們知道Redis是基於內存存儲的一個NoSQL數據庫,基於內存也是其高速高效的原因之一。雖然容量瓶頸在實際生產中並不常見(通常有意識地將搭載Redis的機器內存容量加高),但是不排除在某些極端條件下Redis會將一臺機器的內存耗盡,造成數據丟失,甚至服務器宕機。

性能瓶頸

簡介中提到,雖然Redis在官方文檔中提到可以達到約100000+QPS,但是首先在日常環境的測試中,我們可能並達不到文檔中宣稱的QPS,換言之,這可能也就是一種理論值,就像是4G的理論網速在10-100Mbps,摺合下載速度1.5M/s-10M/s。

但在日常生活中我們極少甚至從來沒有達到過這個速度過,一樣的道理。其次,在中國巨大的網民基數下,單機Redis滿足日常需求尚且捉襟見肘,如果碰上像雙十一、雙十二、春運這些特殊的環境,單臺Redis顯然會有性能不足的現象發生。

Redis集羣的三種模式

主從模式

主從模式是最簡單的一種Redis集羣模式,首先其思想就是一臺Redis服務器作爲主服務器(Master),一臺或多臺服務器作爲從服務器(Slave)。當以此種方式部署集羣時,集羣有如下特點:

  • Master可以進行讀寫操作,當寫操作導致數據發生變化時,將自動同步給Slave,Slave通常是隻讀的,並且接受從Master同步過來的數據。

  • 一臺Master可以有多臺Slave,但每臺Slave只能有一個Master。

  • 某臺Slave宕機不影響其他Slave和Master的讀寫,重新啓動後會將數據重新從Master同步過來。

  • Master宕機後不影響Slave的讀,但該集羣不再提供對Redis的寫入功能。

  • Master宕機後不會從Slave中選舉主節點。

在此種模式下,我們可以對Redis集羣做容災備份和讀寫分離,但是要注意,容災備份並不能拯救你的誤操作,因爲無論增刪改,Redis都將其作爲寫,同步到每個Slave節點上,所以容災,是指不可預知的錯誤導致數據丟失,這種情況下可以從Slave節點中找到原數據的備份,從而進行數據恢復。

而讀寫分離就比較好理解了,上文中提到,Master節點可以讀寫,而Slave節點通常只進行讀操作,索性直接將所有的讀操作都轉移到Slave節點上,這樣可以減輕Master節點的IO壓力。

主從模式的工作原理(全量同步):

Redis全量同步一般發生在Slave初始化階段,但其實在任何時候Slave都可以向Master發起全量同步的請求,這時Slave需要將Master上的所有數據都複製一份。

  • Slave連接主服務器,發送SYNC命令。

  • Master接收到SYNC命令後,開始執行BGSAVE命令生成RDB文件並使用緩衝區記錄此後執行的所有寫命令。

  • Master執行完BGSAVE後,向所有從服務器發送RDB文件,並在發送期間繼續記錄被執行的寫命令。

  • Slave收到RDB文件後丟棄所有舊數據,載入收到的RDB。

  • Master快照發送完畢後開始向Slave發送緩衝區中的寫命令。

  • Slave完成對RDB的載入,開始接收命令請求,並執行來自Master緩衝區的寫命令。

主從模式的工作原理(增量同步):

Redis增量同步一般發生在Slave已經初始化完成,開始正常連接Master的階段

  • Master接收到寫請求,將寫命令發送到Slave。

  • Slave執行接收到的些命令。

注:如果多個Slave同時宕機重啓,那麼就會同時向Master發送SYNC命令,那麼有可能會造成Master節點的IO劇增,有可能會引起宕機。

哨兵(Sentinel)模式

上文中介紹了Redis主從複製模式下的集羣策略,當Master宕機後,不會從Slave節點中選舉出Master,所以該集羣喪失了寫的能力,我們只能人工去將Slave節點晉升爲Master節點,同時要通知應用方更新Master節點的IP地址,對於這種故障處理的方式在現在的環境下通常是不可接受的。所以從Redis2.8開始,Redis正式提供了哨兵模式的架構(故障轉移),來解決這個問題。

哨兵模式的工作特點:

  • 哨兵模式是建立在主從模式的基礎上,當Master節點宕機之後,哨兵會從Slave節點中選擇一個節點作爲Master,並修改它們的配置文件,使其他的Slave指向新的Master。

  • 當原先宕機的Master節點重新啓動時,他將不再是Master,而是作爲新Master的一個Slave節點存在。

  • 哨兵節點是一個特殊的Redis節點(不存儲數據),本質上也是一個進程,所以也有掛掉的可能,所以哨兵也存在集羣模式。

哨兵模式工作原理:

  • 每隔10秒,每個哨兵節點會向Master和Slave節點發送info命令獲取最新的拓撲結構。

  • 每隔1秒,每個哨兵節點會向Master和Slave節點還有其它哨兵節點發送ping命令做心跳檢測,看看是否存在不可達的節點。

  • 主觀下線,如果某個哨兵向一個節點發出的心跳檢測沒有得到響應,那麼該哨兵認爲該節點已經下線。

  • 客觀下線,當哨兵主觀下線的節點是主節點時,哨兵會向其他的哨兵詢問對主節點的判斷,當下線判斷超過一定個數時,那麼哨兵會認爲主節點確實已經下線,那麼會對主節點進行客觀下線的判定。

  • 故障轉移,當Master節點客觀下線時,哨兵會從Slave節點中選擇一個節點作爲Master節點,選擇規則是選擇與主節點複製相似度最高的節點,選擇完成後會將其餘的Slave節點指向新的Master節點,並監控原來的Master節點,當它回覆後作爲新Master節點的Slave存在,並且同步新Master節點的數據。

  • 選舉領導者哨兵節點:當主節點被判斷客觀下線以後,各個哨兵節點會進行協商,選舉出一個領導者哨兵節點,並由該領導者節點對其進行故障轉移操作。

  • 當使用sentinel模式的時候,客戶端不用直接連接Redis,而是連接哨兵的ip和port,由哨兵來提供具體的可提供服務的Redis實現,這樣當master節點掛掉以後,哨兵就會感知並將新的master節點提供給使用者。

     

     

 

Cluster模式

在上文的哨兵模式中,哨兵引入了主節點的自動故障轉移,進一步提高了Redis的高可用性。但是哨兵的缺陷同樣很明顯:哨兵無法對Slave進行自動故障轉移,在讀寫分離場景下,Slave故障會導致讀服務不可用,需要我們對Slave做額外的監控、切換操作。

此外,哨兵仍然沒有解決寫操作無法負載均衡、及存儲能力受到單機限制的問題。

Redis Cluster模式是Redis3.0之後推薦的一種解決方案,其是由多個主節點羣組成的分佈式服務器羣,它具有複製、高可用和分片的特性。另外,Redis Cluster集羣不需要哨兵也能完成節點移除和故障轉移的功能。需要將每個節點設置爲集羣模式,這種集羣模式沒有中心節點,可水平擴展,且集羣配置非常簡單。

Cluster集羣模式工作特點:

  • 多個Redis節點互聯,數據共享。

  • 所有的節點都是主從模式,其中Slave不提供服務,只提供備用。

  • 不支持同時處理多個Key,因爲需要分發到多個節點上。

  • 支持在線增加、刪除節點。

  • 客戶端可以連接任何一個Master節點進行讀寫。

Cluster集羣模式工作原理:

Redis Cluster有固定的16384個hash slot(槽),對每個key計算CRC16值,然後對16384取模,可以獲取key對應的hash slot。每個master都會持有部分slot,比如有3個master,那麼可能每個master持有5000多個hash slot,在redis cluster寫入數據的時候。

其實是你可以將請求發送到任意一個master上去執行。但是,每個master都會計算這個key對應的CRC16值,然後對16384個hashslot取模,找到key對應的hashslot,找到hashslot對應的master。

主觀下線(pfail):集羣中的每個節點都會定期向其他節點發送ping消息,如果在一段時間內一直通信失敗,則發送節點方認爲接收節點存在故障,把接收節點標爲主觀下線(pfail)狀態。

客觀下線(fail):當某個節點判斷另一個節點主觀下線後,相應的節點狀態就會在集羣中進行傳播,如果集羣中所有節點都將它標爲主觀下線,那麼該節點爲客觀下線,並通知該節點的Slave進行故障轉移操作。

故障轉移:在某個節點客觀下線後,該節點的從節點開始故障轉移流程,首先進行資格檢查,每個從節點檢查與主節點的斷開時間,超過一定時間的取消選舉資格,然後同樣在所有從節點中尋找複製偏移量最大的節點先開始進行選舉,只有持有槽的主節點纔有投票權,當從節點收集到過半的票數時,即晉升爲Master,隨即通知Slave當前Master變爲自己。

搭建Redis集羣

上文中說了三種Redis搭建的模式,分別是主從模式、哨兵模式、Cluster模式,關於前兩種網上有着非常多的教程,這裏就不再重新演示了,這裏着重演示一下如何去搭建一個Redis Cluster集羣。

環境準備

CentOS 7,Redis5.0.4

場景描述

本次會啓動三臺CentOS 7服務器,每臺服務器上搭載三個Redis實例,一主二從,一共三個Master實例,六個Slave實例。

清單如下:

Master 1:IP:192.168.43.101 Port:7001

Master 2:IP:192.168.43.102 Port:7002

Master 3:IP:192.168.43.103 Port:7003

Slave 1:IP:192.168.43.101 Port:6001

Slave 2:IP:192.168.43.102 Port:6002

Slave 3:IP:192.168.43.103 Port:6003

Slave 4:IP:192.168.43.101 Port:6004

Slave 5:IP:192.168.43.102 Port:6005

Slave 6:IP:192.168.43.103 Port:6006

修改配置文件

熟悉Redis的應該明白,所謂Redis實例,實際上就是一個又一個的配置文件。要在服務器上啓動多臺不同Redis,實際上就是使用不同的配置文件來啓動Redis,所以第一步我們要先對集羣中的每一個Redis實例配置不一樣的配置文件。

綁定Redis地址

下列三臺主機上的配置文件均爲Master節點配置文件(修改bind屬性)

修改端口號

將端口號修改爲自定義的端口號,默認爲6379,修改爲我們自定義的端口號。

開啓集羣模式並設置集羣配置文件

將cluster-enabled 設置爲yes,並將cluster-config-file設置爲自定義的文件。

這裏定義爲nodes-端口號.conf

修改集羣RDB快照和AOF文件的存放位置

修改dir屬性,這裏定義爲/home/redis-cluster/redis-master/

修改集羣密碼

修改masterauth屬性爲Redis(RequirePass)密碼。

開啓AOF持久化

修改appendonly屬性

appendonly yes

對六臺Slave節點進行同樣的修改配置操作

注意:上述指定的文件夾和文件名原則上對於每個redis實例都應該是唯一的,便於區分。

啓動Redis實例

運行命令:

#第一臺主機
/usr/local/bin/redis-server /home/redis-cluster/redis-master/redis-master-7001.conf
/usr/local/bin/redis-server /home/redis-cluster/redis-slave/redis-slave-6001.conf
/usr/local/bin/redis-server /home/redis-cluster/redis-slave/redis-slave-6004.conf

#第二臺主機
/usr/local/bin/redis-server /home/redis-cluster/redis-master/redis-master-7002.conf
/usr/local/bin/redis-server /home/redis-cluster/redis-slave/redis-slave-6002.conf
/usr/local/bin/redis-server /home/redis-cluster/redis-slave/redis-slave-6005.conf

#第三臺主機
/usr/local/bin/redis-server /home/redis-cluster/redis-master/redis-master-7003.conf
/usr/local/bin/redis-server /home/redis-cluster/redis-slave/redis-slave-6003.conf
/usr/local/bin/redis-server /home/redis-cluster/redis-slave/redis-slave-6006.conf

查看進程 ps -ef | grep redis:

可以看到現在啓動的redis實例已經是集羣模式的了。

搭建集羣

輸入命令:

/usr/local/bin/redis-cli -a Object 
--cluster create --cluster-replicas 2 192.168.43.101:7001 
192.168.43.102:7002 192.168.43.103:7003 192.168.43.101:6001 
192.168.43.102:6002 192.168.43.103:6003 192.168.43.101:6004 
192.168.43.102:6005 192.168.43.103:6006

其中 --cluster-replicas 2代表每個Master攜帶2個Slave,那麼就是三個Master,每個Master攜帶兩個Slave。

示意圖如下:

我們可以看到,Redis將三臺機器連成了一個整體,Master7001的Slave指向了其它兩臺服務器上的Slave,而其它兩臺服務器的Master也同樣跨服務器指向了,這就是RedisCluster高可用的策略,假設有一臺服務器完整地宕機了,由於自己的Slave節點存在於別的服務器上,數據也能重新通過選舉選舉的方式恢復,不易引起數據的丟失。

另外我們可以看到,我們在上文說過,Cluster集羣模式將集羣分爲16384個槽,這裏體現爲0-16383,分佈到了每一個Master節點上,這對我們之前的理論部分做了驗證。

測試

測試環節通過客戶端測試和Java程序測試,來模擬集羣模式下Redis的存儲策略。

客戶端測試

開啓客戶端,隨意連接一個master節點

/usr/local/bin/redis-cli -c -a 密碼 -h IP -p 端口

我們可以看到,當我們set一個鍵值對的時候,Redis會自動爲我們的key計算CRC16值,然後對16384取模,獲取key對應的hash slot,然後通過判斷該槽被那個Master所佔用,幫我們重定向到那個Master節點,將鍵值對存入。

程序測試

在測試之前先把Redis中的數據清空,對三個Master節點分別執行flushall命令。

啓動程序:

1.正常存入數據時關閉某Master節點(模擬宕機):

程序打印正在選舉…

2.選舉結束後繼續IO

代碼:

public class JedisDemo {
    public static void main(String[] args) {

         JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); 
         Set<HostAndPort> nodes = new HashSet<>();
         nodes.add(new HostAndPort("192.168.43.101",7001));
         nodes.add(new HostAndPort("192.168.43.102",7002)); 
         nodes.add(new HostAndPort("192.168.43.103",7003)); 
         nodes.add(new HostAndPort("192.168.43.101",6001)); 
         nodes.add(new HostAndPort("192.168.43.102",6002)); 
         nodes.add(new HostAndPort("192.168.43.103",6003)); 
         nodes.add(new HostAndPort("192.168.43.101",6004)); 
         nodes.add(new HostAndPort("192.168.43.102",6005)); 
         nodes.add(new HostAndPort("192.168.43.103",6006)); 
         JedisCluster jedisCluster = new JedisCluster(nodes,100,1000,100,"Object","rediscluster",jedisPoolConfig,false);
         for(int i = 0;i<2000;i++) {
             try {
            System.out.println("存入數據:"+i);
             jedisCluster.set(String.valueOf(i),String.valueOf(i));
                 try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
            e.printStackTrace();
                }
                 String res = jedisCluster.get(String.valueOf(i)); 
                 System.out.println("取出數據:"+res);
             }catch(Exception e) {
                 //出現節點宕機
                 System.out.println("正在選舉...");
                 System.out.println(new Date());
                 continue;
             }
         }

         jedisCluster.close();
    }
}

注意事項

在搭建集羣的過程中有可能會遇到一直等待連接,但是集羣無法連接成功的狀況,這是因爲我們在搭建集羣的時候防火牆沒有開啓對應的端口號導致的,我們不光要開啓我們對外連接的端口號,如7001、7002、7003,還要開啓對外連接端口號+10000的端口,用於集羣內部相互通信,如節點端口爲7001、7002、7003,那麼我們還應該開啓17001、17002、17003這些端口。

如果遇到搭建失敗的情況,重新搭建的時候一定要到dir指向的文件夾中將快照和AOF還有node.conf文件刪乾淨,否則無法重新搭建。

搭建完畢

至此我們已經完成了Redis集羣的搭建,在私下實踐的時候可以試試使用Redis客戶端直接操作集羣時手動關閉某個Master,會出現什麼樣的狀況,這個是文章中沒有提到的內容。

發佈了52 篇原創文章 · 獲贊 8 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章