天天看點

如何建構 Redis 高可用架構?

1 、題記

Redis 是一個開源的使用 ANSI C 語言編寫、支援網絡、可基于記憶體亦可持久化的日志型、Key-Value 資料庫,并提供多種語言的 API。

如今,網際網路業務的資料正以更快的速度在增長,資料類型越來越豐富,這對資料處理的速度和能力提出了更高要求。Redis 是一種開源的記憶體非關系型資料庫,給開發人員帶來的體驗是颠覆性的。在自始至終的設計過程中,都充分考慮高性能,這使得 Redis 成為當今速度最快的 NoSQL 資料庫。

考慮高性能的同時,高可用也是很重要的考慮因素。網際網路 7x24 無間斷服務,在故障期間以最快的速度 Failover,能給企業帶來最小的損失。

那麼,在實際應用中,都有哪些高可用架構呢?架構之間有何優劣?我們應該怎麼取舍?有哪些最佳實踐?

二、Sentinel (哨兵)原理

在講解 Redis 高可用方案之前,我們先來看看 Redis Sentinel 原理(https://redis.io/topics/sentinel)是怎麼樣的。

Sentinel 叢集通過給定的配置檔案發現 master,啟動時會監控 master。通過向 master 發送 info 資訊獲得該伺服器下面的所有從伺服器。

Sentinel 叢集通過指令連接配接向被監視的主從伺服器發送 hello 資訊 (每秒一次),該資訊包括 Sentinel 本身的 IP、端口、id 等内容,以此來向其他 Sentinel 宣告自己的存在。

Sentinel 叢集通過訂閱連接配接接收其他 Sentinel 發送的 hello 資訊,以此來發現監視同一個主伺服器的其他 Sentinel;叢集之間會互相建立指令連接配接用于通信,因為已經有主從伺服器作為發送和接收 hello 資訊的中介,Sentinel 之間不會建立訂閱連接配接。

Sentinel 叢集使用 ping 指令來檢測執行個體的狀态,如果在指定的時間内(down-after-milliseconds)沒有回複或則傳回錯誤的回複,那麼該執行個體被判為下線。

當 failover 主備切換被觸發後,failover 并不會馬上進行,還需要 Sentinel 中的大多數 Sentinel 授權後才可以進行 failover,即進行 failover 的 Sentinel 會去獲得指定 quorum 個的 Sentinel 的授權,成功後進入 ODOWN 狀态。如在 5 個 Sentinel 中配置了 2 個 quorum,等到 2 個 Sentinel 認為 master 死了就執行 failover。

Sentinel 向選為 master 的 slave 發送 SLAVEOF NO ONE 指令,選擇 slave 的條件是 Sentinel 首先會根據 slaves 的優先級來進行排序,優先級越小排名越靠前。如果優先級相同,則檢視複制的下标,哪個從 master 接收的複制資料多,哪個就靠前。如果優先級和下标都相同,就選擇程序 ID 較小的。

Sentinel 被授權後,它将會獲得宕掉的 master 的一份最新配置版本号 (config-epoch),當 failover 執行結束以後,這個版本号将會被用于最新的配置,通過廣播形式通知其它 Sentinel,其它的 Sentinel 則更新對應 master 的配置。

1 到 3 是自動發現機制:

以 10 秒一次的頻率,向被監視的 master 發送 info 指令,根據回複擷取 master 目前資訊。

以 1 秒一次的頻率,向所有 redis 伺服器、包含 Sentinel 在内發送 PING 指令,通過回複判斷伺服器是否線上。

以 2 秒一次的頻率,通過向所有被監視的 master,slave 伺服器發送目前 Sentinel master 資訊的消息。

4 是檢測機制,5 和 6 是 failover 機制,7 是更新配置機制。[1]

三、Redis 高可用架構

講解完 Redis Sentinel 原理之後,接下來講解常用的 Redis高可用架構。

Redis Sentinel 叢集 + 内網 DNS + 自定義腳本

Redis Sentinel 叢集 + VIP + 自定義腳本

封裝用戶端直連 Redis Sentinel 端口

JedisSentinelPool,适合 Java

PHP 基于 phpredis 自行封裝

Redis Sentinel 叢集 + Keepalived/Haproxy

Redis M/S + Keepalived

Redis Cluster

Twemproxy

Codis

接下來配合圖文逐個講解。

1、Redis Sentinel 叢集 + 内網 DNS + 自定義腳本

上圖是已經線上上環境應用的方案。底層是 Redis Sentinel 叢集,代理着 Redis 主從,Web 端連接配接内網 DNS 提供服務。内網 DNS 按照一定的規則配置設定,比如 xxxx.redis.cache/queue.port.xxx.xxx,第一個段表示業務簡寫,第二個段表示這是 Redis 内網域名,第三個段表示 Redis 類型,cache 表示緩存,queue 表示隊列,第四個段表示 Redis 端口,第五、第六個段表示内網主域名。

當主節點發生故障,比如機器故障、Redis 節點故障或者網絡不可達,Sentinel 叢集會調用 client-reconfig-script 配置的腳本,修改對應端口的内網域名。對應端口的内網域名指向新的 Redis 主節點。

優點:

秒級切換,在 10s 内完成整個切換操作

腳本自定義,架構可控

對應用透明,前端不用擔心後端發生什麼變化

缺點:

維護成本略高,Redis Sentinel 叢集建議投入 3 台機器以上

依賴 DNS,存在解析延時

Sentinel 模式存在短時間的服務不可用

服務通過外網通路不可采用此方案

2、Redis Sentinel 叢集 + VIP + 自定義腳本

此方案和上一個方案相比,略有不同。第一個方案使用了内網 DNS,第二個方案把内網 DNS 換成了虛拟 IP。底層是 Redis Sentinel 叢集,代理着 Redis 主從,Web 端通過 VIP 提供服務。在部署 Redis 主從的時候,需要将虛拟 IP 綁定到目前的 Redis 主節點。當主節點發生故障,比如機器故障、Redis 節點故障或者網絡不可達,Sentinel 叢集會調用 client-reconfig-script 配置的腳本,将 VIP 漂移到新的主節點上。

秒級切換,在 5s 内完成整個切換操作

使用 VIP 增加維護成本,存在 IP 混亂風險

3.3 封裝用戶端直連 Redis Sentinel 端口

3、封裝用戶端直連 Redis Sentinel 端口

部分業務隻能通過外網通路 Redis,上述兩種方案均不可用,于是衍生出了這種方案。Web 使用用戶端連接配接其中一台 Redis Sentinel 叢集中的一台機器的某個端口,然後通過這個端口擷取到目前的主節點,然後再連接配接到真實的 Redis 主節點進行相應的業務員操作。需要注意的是,Redis Sentinel 端口和 Redis 主節點均需要開放通路權限。如果前端業務使用 Java,有 JedisSentinelPool 可以複用;如果前端業務使用 PHP,可以在 phpredis 的基礎上做二次封裝。

服務探測故障及時

DBA 維護成本低

依賴用戶端支援 Sentinel

Sentinel 伺服器和 Redis 節點需要開放通路權限

對應用有侵入性

4、Redis Sentinel 叢集 + Keepalived/Haproxy

底層是 Redis Sentinel 叢集,代理着 Redis 主從,Web 端通過 VIP 提供服務。當主節點發生故障,比如機器故障、Redis 節點故障或者網絡不可達,Redis 之間的切換通過 Redis Sentinel 内部機制保障,VIP 切換通過 Keepalived 保障。

秒級切換

對應用透明

維護成本高

存在腦裂

5、Redis M/S + Keepalived

此方案沒有使用到 Redis Sentinel。此方案使用了原生的主從和 Keepalived,VIP 切換通過 Keepalived 保障,Redis 主從之間的切換需要自定義腳本實作。

部署簡單,維護成本低

需要腳本實作切換功能

6、Redis Cluster

From: http://intro2libsys.com/focused-redis-topics/day-one/intro-redis-cluster

Redis 3.0.0 在 2015 年 4 月 2 日正式釋出,距今已有兩年多的時間。Redis 叢集采用 P2P 模式,無中心化。把 key 分成 16384 個 slot,每個執行個體負責一部分 slot。用戶端請求對應的資料,若該執行個體 slot 沒有對應的資料,該執行個體會轉發給對應的執行個體。另外,Redis 叢集通過 Gossip 協定同步節點資訊。

元件 all-in-box,部署簡單,節約機器資源

性能比 proxy 模式好

自動故障轉移、Slot 遷移中資料可用

官方原生叢集方案,更新與支援有保障

架構比較新,最佳實踐較少

多鍵操作支援有限(驅動可以曲線救國)

為了性能提升,用戶端需要緩存路由表資訊

節點發現、reshard 操作不夠自動化

7、Twemproxy

From: http://engineering.bloomreach.com/the-evolution-of-fault-tolerant-redis-cluster

多個同構 Twemproxy(配置相同)同時工作,接受用戶端的請求,根據 hash 算法,轉發給對應的 Redis。

Twemproxy 方案比較成熟了,之前我們團隊長期使用此方案,但是效果并不是很理想。一方面是定位問題比較困難,另一方面是它對自動剔除節點的支援不是很友好。

開發簡單,對應用幾乎透明

曆史悠久,方案成熟

代理影響性能

LVS 和 Twemproxy 會有節點性能瓶頸

Redis 擴容非常麻煩

Twitter 内部已放棄使用該方案,新使用的架構未開源

8、Codis

From: https://github.com/CodisLabs/codis

Codis 是由豌豆莢開源的産品,涉及元件衆多,其中 ZooKeeper 存放路由表和代理節點中繼資料、分發 Codis-Config 的指令;Codis-Config 是內建管理工具,有 Web 界面供使用;Codis-Proxy 是一個相容 Redis 協定的無狀态代理;Codis-Redis 基于 Redis 2.8 版本二次開發,加入 slot 支援,友善遷移資料。

性能比 Twemproxy 好

有圖形化界面,擴容容易,運維友善

代理依舊影響性能

元件過多,需要很多機器資源

修改了 Redis 代碼,導緻和官方無法同步,新特性跟進緩慢

開發團隊準備主推基于 Redis 改造的 reborndb

四、最佳實踐

所謂的最佳實踐,都是最适合具體場景的實踐。

主推以下方案:

以下是實戰過程中總結出的最佳實踐:

Redis Sentinel 叢集建議使用 >= 5 台機器

不同的大業務可以使用一套 Redis Sentinel 叢集,代理該業務下的所有端口

根據不同的業務劃分好 Redis 端口範圍

自定義腳本建議采用 Python 實作,擴充便利

自定義腳本需要注意判斷目前的 Sentinel 角色

自定義腳本傳入參數:<service_name> <role> <comment> <from_ip> <from_port> <to_ip> <to_port>

自定義腳本需要遠端 ssh 操作機器,建議使用 paramiko 庫,避免重複建立 SSH 連接配接,消耗時間

加速 SSH 連接配接,建議關閉以下兩個參數

UseDNS no

GSSAPIAuthentication no

微信或者郵件告警,建議 fork 一個程序,避免主程序阻塞

自動切換和故障切換,所有操作建議在 15s 以内完成

歡迎工作一到五年的Java工程師朋友們加入Java填坑之路:860113481

群内提供免費的Java架構學習資料(裡面有高可用、高并發、高性能及分布式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!