Redis的複製功能以及Redis複製機制自己的優缺點以及集羣搭建問題

Redis複製流程概述

Redis的複製功能是徹底創建在以前咱們討論過的基於內存快照的持久化策略基礎上的,也就是說不管你的持久化策略選擇的是什麼,只要用到了 Redis的複製功能,就必定會有內存快照發生,那麼首先要注意你的系統內存容量規劃,緣由能夠參考我上一篇文章中提到的Redis磁盤IO問題。git

Redis複製流程在Slave和Master端各自是一套狀態機流轉,涉及的狀態信息是:github

Slave 端:算法

REDIS_REPL_NONE
REDIS_REPL_CONNECT
REDIS_REPL_CONNECTED

Master端:sql

REDIS_REPL_WAIT_BGSAVE_START
REDIS_REPL_WAIT_BGSAVE_END
REDIS_REPL_SEND_BULK
REDIS_REPL_ONLINE

整個狀態機流程過程以下:緩存

  1. Slave端在配置文件中添加了slave of指令,因而Slave啓動時讀取配置文件,初始狀態爲REDIS_REPL_CONNECT。
  2. Slave端在定時任務serverCron(Redis內部的定時器觸發事件)中鏈接Master,發送sync命令,而後阻塞等待master發送回其內存快照文件(最新版的Redis已經不須要讓Slave阻塞)。
  3. Master端收到sync命令簡單判斷是否有正在進行的內存快照子進程,沒有則當即開始內存快照,有則等待其結束,當快照完成後會將該文件發送給Slave端。
  4. Slave端接收Master發來的內存快照文件,保存到本地,待接收完成後,清空內存表,從新讀取Master發來的內存快照文件,重建整個內存表數據結構,並最終狀態置位爲 REDIS_REPL_CONNECTED狀態,Slave狀態機流轉完成。
  5. Master端在發送快照文件過程當中,接收的任何會改變數據集的命令都會暫時先保存在Slave網絡鏈接的發送緩存隊列裏(list數據結構),待快照完成後,依次發給Slave,以後收到的命令相同處理,並將狀態置位爲 REDIS_REPL_ONLINE。

整個複製過程完成,流程以下圖所示:服務器

Redis複製機制的缺陷

從上面的流程能夠看出,Slave從庫在鏈接Master主庫時,Master會進行內存快照,而後把整個快照文件發給Slave,也就是沒有象MySQL那樣有複製位置的概念,即無增量複製,這會給整個集羣搭建帶來很是多的問題。網絡

好比一臺線上正在運行的Master主庫配置了一臺從庫進行簡單讀寫分離,這時Slave因爲網絡或者其它緣由與Master斷開了鏈接,那麼當 Slave進行從新鏈接時,須要從新獲取整個Master的內存快照,Slave全部數據跟着所有清除,而後從新創建整個內存表,一方面Slave恢復的 時間會很是慢,另外一方面也會給主庫帶來壓力。數據結構

因此基於上述緣由,若是你的Redis集羣須要主從複製,那麼最好事先配置好全部的從庫,避免中途再去增長從庫。工具

Cache仍是Storage

在咱們分析過了Redis的複製與持久化功能後,咱們不可貴出一個結論,實際上Redis目前發佈的版本還都是一個單機版的思路,主要的問題集中在,持久化方式不夠成熟,複製機制存在比較大的缺陷,這時咱們又開始從新思考Redis的定位:Cache仍是Storage?性能

若是做爲Cache的話,彷佛除了有些很是特殊的業務場景,必需要使用Redis的某種數據結構以外,咱們使用Memcached可能更合適,畢竟Memcached不管客戶端包和服務器自己更久經考驗。

若是是做爲存儲Storage的話,咱們面臨的最大的問題是不管是持久化仍是複製都沒有辦法解決Redis單點問題,即一臺Redis掛掉了,沒有太好的辦法可以快速的恢復,一般幾十G的持久化數據,Redis重啓加載須要幾個小時的時間,而複製又有缺陷,如何解決呢?

Redis可擴展集羣搭建

1. 主動複製避開Redis複製缺陷。

既然Redis的複製功能有缺陷,那麼咱們不妨放棄Redis自己提供的複製功能,咱們能夠採用主動複製的方式來搭建咱們的集羣環境。

所謂主動複製是指由業務端或者經過代理中間件對Redis存儲的數據進行雙寫或多寫,經過數據的多份存儲來達到與複製相同的目的,主動複製不只限於 用在Redis集羣上,目前不少公司採用主動複製的技術來解決MySQL主從之間複製的延遲問題,好比Twitter還專門開發了用於複製和分區的中間件 gizzard(https://github.com/twitter/gizzard) 。

主動複製雖然解決了被動複製的延遲問題,但也帶來了新的問題,就是數據的一致性問題,數據寫2次或屢次,如何保證多份數據的一致性呢?若是你的應用 對數據一致性要求不高,容許最終一致性的話,那麼一般簡單的解決方案是能夠經過時間戳或者vector clock等方式,讓客戶端同時取到多份數據並進行校驗,若是你的應用對數據一致性要求很是高,那麼就須要引入一些複雜的一致性算法好比Paxos來保證 數據的一致性,可是寫入性能也會相應降低不少。

經過主動複製,數據多份存儲咱們也就再也不擔憂Redis單點故障的問題了,若是一組Redis集羣掛掉,咱們可讓業務快速切換到另外一組Redis上,下降業務風險。

2. 經過presharding進行Redis在線擴容。

經過主動複製咱們解決了Redis單點故障問題,那麼還有一個重要的問題須要解決:容量規劃與在線擴容問題。

咱們前面分析過Redis的適用場景是所有數據存儲在內存中,而內存容量有限,那麼首先須要根據業務數據量進行初步的容量規劃,好比你的業務數據需 要100G存儲空間,假設服務器內存是48G,那麼根據上一篇咱們討論的Redis磁盤IO的問題,咱們大約須要3~4臺服務器來存儲。這個實際是對現有 業務狀況所作的一個容量規劃,假如業務增加很快,很快就會發現當前的容量已經不夠了,Redis裏面存儲的數據很快就會超過物理內存大小,那麼如何進行 Redis的在線擴容呢?

Redis的做者提出了一種叫作presharding的方案來解決動態擴容和數據分區的問題,實際就是在同一臺機器上部署多個Redis實例的方式,當容量不夠時將多個實例拆分到不一樣的機器上,這樣實際就達到了擴容的效果。

拆分過程以下:

  1. 在新機器上啓動好對應端口的Redis實例。
  2. 配置新端口爲待遷移端口的從庫。
  3. 待複製完成,與主庫完成同步後,切換全部客戶端配置到新的從庫的端口。
  4. 配置從庫爲新的主庫。
  5. 移除老的端口實例。
  6. 重複上述過程遷移好全部的端口到指定服務器上。

以上拆分流程是Redis做者提出的一個平滑遷移的過程,不過該拆分方法仍是很依賴Redis自己的複製功能的,若是主庫快照數據文件過大,這個複製的過程也會好久,同時會給主庫帶來壓力。因此作這個拆分的過程最好選擇爲業務訪問低峯時段進行。

Redis複製的改進思路

咱們線上的系統使用了咱們本身改進版的Redis,主要解決了Redis沒有增量複製的缺陷,可以完成相似Mysql Binlog那樣能夠經過從庫請求日誌位置進行增量複製。

咱們的持久化方案是首先寫Redis的AOF文件,並對這個AOF文件按文件大小進行自動分割滾動,同時關閉Redis的Rewrite命令,而後 會在業務低峯時間進行內存快照存儲,並把當前的AOF文件位置一塊兒寫入到快照文件中,這樣咱們可使快照文件與AOF文件的位置保持一致性,這樣咱們獲得 了系統某一時刻的內存快照,而且同時也能知道這一時刻對應的AOF文件的位置,那麼當從庫發送同步命令時,咱們首先會把快照文件發送給從庫,而後從庫會取 出該快照文件中存儲的AOF文件位置,並將該位置發給主庫,主庫會隨後發送該位置以後的全部命令,之後的複製就都是這個位置以後的增量信息了。

Redis與MySQL的結合

目前大部分互聯網公司使用MySQL做爲數據的主要持久化存儲,那麼如何讓Redis與MySQL很好的結合在一塊兒呢?咱們主要使用了一種基於MySQL做爲主庫,Redis做爲高速數據查詢從庫的異構讀寫分離的方案。

爲此咱們專門開發了本身的MySQL複製工具,能夠方便的實時同步MySQL中的數據到Redis上。

(MySQL-Redis 異構讀寫分離)

總結:

  1. Redis的複製功能沒有增量複製,每次重連都會把主庫整個內存快照發給從庫,因此須要避免向在線服務的壓力較大的主庫上增長從庫。
  2. Redis 的複製因爲會使用快照持久化方式,因此若是你的Redis持久化方式選擇的是日誌追加方式(aof),那麼系統有可能在同一時刻既作aof日誌文件的同步 刷寫磁盤,又作快照寫磁盤操做,這個時候Redis的響應能力會受到影響。因此若是選用aof持久化,則加從庫須要更加謹慎。
  3. 可使用主動複製和presharding方法進行Redis集羣搭建與在線擴容。

本文加上以前的2篇文章基本將Redis的最經常使用功能和使用場景與優化進行了分析和討論,實際Redis還有不少其它輔助的一些功能,Redis的做者也在不斷嘗試新的思路,這裏就不一一列舉了,有興趣的朋友能夠研究下,也很歡迎一塊兒討論

相關文章
相關標籤/搜索