Redis復制流程概述Redis的復制功能是完全建立在之前我們討論過的基于內(nèi)存快照的持久化策略基礎上的,也就是說無論你的持久化策略選擇的是什么,只要用到了Redis的復制功能,就一定會有內(nèi)存快照發(fā)生,那么首先要注意你的系統(tǒng)內(nèi)存容量規(guī)劃,原因可以參考我上一篇文章中提到的Redis磁盤IO問題。 Redis復制流程在Slave和Master端各自是一套狀態(tài)機流轉,涉及的狀態(tài)信息是: Slave 端: REDIS_REPL_NONE REDIS_REPL_CONNECT REDIS_REPL_CONNECTED Master端: REDIS_REPL_WAIT_BGSAVE_START REDIS_REPL_WAIT_BGSAVE_END REDIS_REPL_SEND_BULK REDIS_REPL_ONLINE 整個狀態(tài)機流程過程如下:
整個復制過程完成,流程如下圖所示:
Redis復制機制的缺陷從上面的流程可以看出,Slave從庫在連接Master主庫時,Master會進行內(nèi)存快照,然后把整個快照文件發(fā)給Slave,也就是沒有象MySQL那樣有復制位置的概念,即無增量復制,這會給整個集群搭建帶來非常多的問題。 比如一臺線上正在運行的Master主庫配置了一臺從庫進行簡單讀寫分離,這時Slave由于網(wǎng)絡或者其它原因與Master斷開了連接,那么當Slave進行重新連接時,需要重新獲取整個Master的內(nèi)存快照,Slave所有數(shù)據(jù)跟著全部清除,然后重新建立整個內(nèi)存表,一方面Slave恢復的時間會非常慢,另一方面也會給主庫帶來壓力。 所以基于上述原因,如果你的Redis集群需要主從復制,那么最好事先配置好所有的從庫,避免中途再去增加從庫。 Cache還是Storage在我們分析過了Redis的復制與持久化功能后,我們不難得出一個結論,實際上Redis目前發(fā)布的版本還都是一個單機版的思路,主要的問題集中在,持久化方式不夠成熟,復制機制存在比較大的缺陷,這時我們又開始重新思考Redis的定位:Cache還是Storage? 如果作為Cache的話,似乎除了有些非常特殊的業(yè)務場景,必須要使用Redis的某種數(shù)據(jù)結構之外,我們使用Memcached可能更合適,畢竟Memcached無論客戶端包和服務器本身更久經(jīng)考驗。 如果是作為存儲Storage的話,我們面臨的最大的問題是無論是持久化還是復制都沒有辦法解決Redis單點問題,即一臺Redis掛掉了,沒有太好的辦法能夠快速的恢復,通常幾十G的持久化數(shù)據(jù),Redis重啟加載需要幾個小時的時間,而復制又有缺陷,如何解決呢? Redis可擴展集群搭建1. 主動復制避開Redis復制缺陷。既然Redis的復制功能有缺陷,那么我們不妨放棄Redis本身提供的復制功能,我們可以采用主動復制的方式來搭建我們的集群環(huán)境。 所謂主動復制是指由業(yè)務端或者通過代理中間件對Redis存儲的數(shù)據(jù)進行雙寫或多寫,通過數(shù)據(jù)的多份存儲來達到與復制相同的目的,主動復制不僅限于用在Redis集群上,目前很多公司采用主動復制的技術來解決MySQL主從之間復制的延遲問題,比如Twitter還專門開發(fā)了用于復制和分區(qū)的中間件gizzard(https://github.com/twitter/gizzard) 。 主動復制雖然解決了被動復制的延遲問題,但也帶來了新的問題,就是數(shù)據(jù)的一致性問題,數(shù)據(jù)寫2次或多次,如何保證多份數(shù)據(jù)的一致性呢?如果你的應用對數(shù)據(jù)一致性要求不高,允許最終一致性的話,那么通常簡單的解決方案是可以通過時間戳或者vector clock等方式,讓客戶端同時取到多份數(shù)據(jù)并進行校驗,如果你的應用對數(shù)據(jù)一致性要求非常高,那么就需要引入一些復雜的一致性算法比如Paxos來保證數(shù)據(jù)的一致性,但是寫入性能也會相應下降很多。 通過主動復制,數(shù)據(jù)多份存儲我們也就不再擔心Redis單點故障的問題了,如果一組Redis集群掛掉,我們可以讓業(yè)務快速切換到另一組Redis上,降低業(yè)務風險。 2. 通過presharding進行Redis在線擴容。通過主動復制我們解決了Redis單點故障問題,那么還有一個重要的問題需要解決:容量規(guī)劃與在線擴容問題。 我們前面分析過Redis的適用場景是全部數(shù)據(jù)存儲在內(nèi)存中,而內(nèi)存容量有限,那么首先需要根據(jù)業(yè)務數(shù)據(jù)量進行初步的容量規(guī)劃,比如你的業(yè)務數(shù)據(jù)需要100G存儲空間,假設服務器內(nèi)存是48G,那么根據(jù)上一篇我們討論的Redis磁盤IO的問題,我們大約需要3~4臺服務器來存儲。這個實際是對現(xiàn)有業(yè)務情況所做的一個容量規(guī)劃,假如業(yè)務增長很快,很快就會發(fā)現(xiàn)當前的容量已經(jīng)不夠了,Redis里面存儲的數(shù)據(jù)很快就會超過物理內(nèi)存大小,那么如何進行Redis的在線擴容呢? Redis的作者提出了一種叫做presharding的方案來解決動態(tài)擴容和數(shù)據(jù)分區(qū)的問題,實際就是在同一臺機器上部署多個Redis實例的方式,當容量不夠時將多個實例拆分到不同的機器上,這樣實際就達到了擴容的效果。 拆分過程如下:
以上拆分流程是Redis作者提出的一個平滑遷移的過程,不過該拆分方法還是很依賴Redis本身的復制功能的,如果主庫快照數(shù)據(jù)文件過大,這個復制的過程也會很久,同時會給主庫帶來壓力。所以做這個拆分的過程最好選擇為業(yè)務訪問低峰時段進行。 Redis復制的改進思路我們線上的系統(tǒng)使用了我們自己改進版的Redis,主要解決了Redis沒有增量復制的缺陷,能夠完成類似Mysql Binlog那樣可以通過從庫請求日志位置進行增量復制。 我們的持久化方案是首先寫Redis的AOF文件,并對這個AOF文件按文件大小進行自動分割滾動,同時關閉Redis的Rewrite命令,然后會在業(yè)務低峰時間進行內(nèi)存快照存儲,并把當前的AOF文件位置一起寫入到快照文件中,這樣我們可以使快照文件與AOF文件的位置保持一致性,這樣我們得到了系統(tǒng)某一時刻的內(nèi)存快照,并且同時也能知道這一時刻對應的AOF文件的位置,那么當從庫發(fā)送同步命令時,我們首先會把快照文件發(fā)送給從庫,然后從庫會取出該快照文件中存儲的AOF文件位置,并將該位置發(fā)給主庫,主庫會隨后發(fā)送該位置之后的所有命令,以后的復制就都是這個位置之后的增量信息了。
Redis與MySQL的結合目前大部分互聯(lián)網(wǎng)公司使用MySQL作為數(shù)據(jù)的主要持久化存儲,那么如何讓Redis與MySQL很好的結合在一起呢?我們主要使用了一種基于MySQL作為主庫,Redis作為高速數(shù)據(jù)查詢從庫的異構讀寫分離的方案。 為此我們專門開發(fā)了自己的MySQL復制工具,可以方便的實時同步MySQL中的數(shù)據(jù)到Redis上。
(MySQL-Redis 異構讀寫分離) 總結:
本文加上之前的2篇文章基本將Redis的最常用功能和使用場景與優(yōu)化進行了分析和討論,實際Redis還有很多其它輔助的一些功能,Redis的作者也在不斷嘗試新的思路,這里就不一一列舉了,有興趣的朋友可以研究下,也很歡迎一起討論,我的微博(http://weibo.com/bachmozart ) @搖擺巴赫。 |
|
|