參考文檔:node
http://geek.csdn.net/news/detail/200023git
redis主從複製:https://blog.csdn.net/imxiangzi/article/details/52400877github
設計原則和初衷
redis
redis-cluster設計算法
Redis-Cluster採用無中心結構,每一個節點保存數據和整個集羣狀態,每一個節點都和其餘全部節點鏈接。數據庫
其結構設計:緩存
redis cluster 數據分片安全
Redis Cluster在設計中沒有使用一致性哈希(Consistency Hashing),而是使用數據分片(Sharding)引入哈希槽(hash slot)來實現;一個 Redis Cluster包含16384(0~16383)個哈希槽,存儲在Redis Cluster中的全部鍵都會被映射到這些slot中,集羣中的每一個鍵都屬於這16384個哈希槽中的一個,集羣使用公式slot=CRC16(key)/16384來計算key屬於哪一個槽,其中CRC16(key)語句用於計算key的CRC16 校驗和。
集羣中的每一個主節點(Master)都負責處理16384個哈希槽中的一部分,當集羣處於穩定狀態時,每一個哈希槽都只由一個主節點進行處理,每一個主節點能夠有一個到N個從節點(Slave),當主節點出現宕機或網絡斷線等不可用時,從節點能自動提高爲主節點進行處理。 服務器
如今咱們是三個主節點分別是:A, B, C 三個節點,它們能夠是一臺機器上的三個端口,也能夠是三臺不一樣的服務器。那麼,採用哈希槽 (hash slot)的方式來分配16384個slot 的話,它們三個節點分別承擔的slot 區間是:
節點A覆蓋0-5460;
節點B覆蓋5461-10922;
節點C覆蓋10923-16383.
獲取數據:
若是存入一個值,按照redis cluster哈希槽的算法: CRC16('key')384 = 6782。 那麼就會把這個key 的存儲分配到 B 上了。一樣,當我鏈接(A,B,C)任何一個節點想獲取'key'這個key時,也會這樣的算法,而後內部跳轉到B節點上獲取數據 ,如圖:網絡
新增一個主節點:
新增一個節點D,redis cluster的這種作法是從各個節點的前面各拿取一部分slot到D上,我會在接下來的實踐中實驗。大體就會變成這樣:
節點A覆蓋1365-5460
節點B覆蓋6827-10922
節點C覆蓋12288-16383
節點D覆蓋0-1364,5461-6826,10923-12287
一樣刪除一個節點也是相似,移動完成後就能夠刪除這個節點了。
優點
不足
Redis 主從模式
redis cluster 爲了保證數據的高可用性,加入了主從模式,一個主節點對應一個或多個從節點,主節點提供數據存取,從節點則是從主節點拉取數據備份,當這個主節點掛掉後,就會有這個從節點選取一個來充當主節點,從而保證集羣不會掛掉。
上面那個例子裏, 集羣有ABC三個主節點, 若是這3個節點都沒有加入從節點,若是B掛掉了,咱們就沒法訪問整個集羣了。A和C的slot也沒法訪問。因此咱們在集羣創建的時候,必定要爲每一個主節點都添加了從節點, 好比像這樣, 集羣包含主節點A、B、C, 以及從節點A一、B一、C1, 那麼即便B掛掉系統也能夠繼續正確工做。B1節點替代了B節點,因此Redis集羣將會選擇B1節點做爲新的主節點,集羣將會繼續正確地提供服務。 當B從新開啓後,它就會變成B1的從節點。不過須要注意,若是節點B和B1同時掛了,Redis集羣就沒法繼續正確地提供服務了
redis主從複製的一些特色:
1)master能夠有多個slave
2)除了多個slave連到相同的master外,slave也能夠鏈接其餘slave造成圖狀結構
3)主從複製不會阻塞master。也就是說當一個或多個slave與master進行初次同步數據時,master能夠繼續處理client發來的請求。相反slave在初次同步數據時則會阻塞不能處理client的請求
4)主從複製能夠用來提升系統的可伸縮性,咱們能夠用多個slave專門用於client的讀請求,好比sort操做可使用slave來處理。也能夠用來作簡單的數據冗餘
5)能夠在master禁用數據持久化,只須要註釋掉master配置文件中的全部save配置,而後只在slave上配置數據持久化
redis的主從複製分爲兩個階段:
1)同步操做:將從服務器的數據庫狀態更新至主服務器當前所處的數據庫狀態
2)命令傳播:在主服務器的數據庫狀態被修改,致使主從服務器的數據庫狀態出現不一致時,主服務器會將本身執行的寫命令送給從服務器執行
同步操做的過程(2.8版本之後):
1)設置主服務器地址和端口,經過調用SAVEOF <master_ip> <master_port>命令
2)創建套接字鏈接
3)發送PING命令,檢查主從服務器是否可以正常處理命令
4)身份驗證,從服務器設置了masterauth而且主服務器設置了requirepass是須要進行身份驗證。這兩個選項要麼都設置要麼都不設置,若是隻設置了一個從服務器向主服務器發送命令時會報錯
5)發送端口信息,經過執行命令REPLCONF listening-port <port-number>,向主服務器發送從服務器的監聽端口號
6)同步,從服務器向主服務器發送PSYNC命令
7)命令傳播,完成同步以後主服務器會把以後執行的寫命令傳播到從服務器保證主從服務器的狀態一致
2.8版本以前 同步操做SYNC。只有全量同步,效率比較低
SYNC同步過程:
1)從服務器向主服務器發送 SYNC 命令
2)收到 SYNC 命令的主服務器執行 BGSAVE 命令, 在後臺生成一個 RDB 文件, 並使用一個緩衝區記錄從如今開始執行的全部寫命令
3)當主服務器的 BGSAVE 命令執行完畢時, 主服務器會將 BGSAVE 命令生成的 RDB 文件發送給從服務器, 從服務器接收並載入這個 RDB 文件, 將本身的數據庫狀態更新至主服務器執行 BGSAVE 命令時的數據庫狀態。
4)主服務器將記錄在緩衝區裏面的全部寫命令發送給從服務器, 從服務器執行這些寫命令, 將本身的數據庫狀態更新至主服務器數據庫當前所處的狀態
2.8版本以後 同步操做PSYNC。自行判斷 是全量同步 仍是 增量同步 效率比較高
部分重同步功能由下面幾個部分構成:
主服務器的複製偏移量和從服務器的複製偏移量:當主服務器在向從服務器進行命令同步時,主服務器和從服務器會各自記錄一個複製偏移量,當主從服務器的數據庫狀態一致時這兩個複製偏移量是相同的,若是這兩個偏移量不一致說明當前主從服務器的狀態不一致
主服務器的複製積壓緩衝區:複製積壓緩衝區是一個固定大小的FIFO隊列,當隊列已滿時會彈出最先插入的數據,在主服務器進行命令傳播時會同時把命令放到緩衝區中,緩衝區包含兩部分數據,偏移量和字節。在進行復制時從服務器會將偏移量上報到主服務器,主服務檢查當前偏移量是否還存在緩衝區中,若是存在進行部分重同步,若是不存在進行完整重同步。由於這個積壓緩衝區是一個固定大小的隊列,因此當從服務器長時間斷線時,從服務器的複製偏移量極可能已再也不緩衝區中,這時候只能進行完整重同步
服務器的運行ID:初次同步時主服務器會把ID發給從服務器,從服務器保存主服務器ID,當斷線重連後,會把以前保存的主服務器ID上報給主服務器,主服務器檢查從服務器以前複製的主服務器ID是否和本身的ID相同,若是相同,執行部分重同步,若是不一樣說明從服務器以前記錄的狀態不是當前主服務器,這時候須要執行完整重同步
PSYNC命令實現
1)初始複製或者以前執行過SLAVEOF no one命令,執行完整重同步:發送PSYNC ? -1命令到主服務器
2)若是從服務器已經複製過某個主服務器,在開始新複製時向主服務器發送PSYNC <runid> <offset>命令,runid是上次複製的主服務器id,offset是從服務器的複製偏移量
3)主服務器會根據這個兩個參數來決定作哪一種同步,判斷服務器id是否和本機相同,複製偏移量是否在緩衝區中,主服務器有三種回覆:
回覆+FULLRESYNC <runid> <offset>執行完整重同步,從服務器把offset當作初始複製偏移量
回覆+CONTINUE,表示執行部分重同步,從服務器等待主服務器發送缺乏的數據
回覆-ERR,表示主服務器版本低於2.8,不支持PSYNC命令
心跳檢測
在命令傳播階段,從服務器默認每秒一次的頻率向主服務器發送命令:REPLCONF ACK <replication_offset>,replication_offset是從服務器的複製偏移量,該命令有三個做用:
1)檢測從服務器的網絡鏈接狀態,檢測主從服務器鏈接是否正常,若是主服務器超過必定時間沒有收到從服務器的REPLCONF ACK 命令,那麼它們的鏈接可能出了問題
2)輔助實現min-slaves選項,min-slaves-to-write和min-slaves-max-lag兩個選項能夠防止主服務器在不安全的狀況下執行寫命令,min-slaves-to-write 3 min-slaves-max-lag 10 表示若是從服務器少於3個,或者3個從服務器的延遲都大於10秒時,主服務器拒絕寫命令
3)檢測命令丟失,主服務器接收到從服務器的REPLCONF ACK 命令以後會檢查從服務器的偏移量是否和主服務器的一致,若是不一致會把積壓緩衝區中的從服務器偏移量後面的命令發送到從服務器
關閉主服務器持久化時,複製功能的數據安全
當配置Redis複製功能時,強烈建議打開主服務器的持久化功能。 不然的話,因爲延遲等問題,部署的服務應該要避免自動拉起。爲了幫助理解主服務器關閉持久化時自動拉起的危險性,參考一下如下會致使主從服務器數據所有丟失的例子:
假設節點A爲主服務器,而且關閉了持久化。 而且節點B和節點C從節點A複製數據
節點A崩潰,而後由自動拉起服務重啓了節點A. 因爲節點A的持久化被關閉了,因此重啓以後沒有任何數據
節點B和節點C將從節點A複製數據,可是A的數據是空的, 因而就把自身保存的數據副本刪除。
在關閉主服務器上的持久化,並同時開啓自動拉起進程的狀況下,即使使用Sentinel來實現Redis的高可用性,也是很是危險的。 由於主服務器可能拉起得很是快,以致於Sentinel在配置的心跳時間間隔內沒有檢測到主服務器已被重啓,而後仍是會執行上面的數據丟失的流程。不管什麼時候,數據安全都是極其重要的,因此應該禁止主服務器關閉持久化的同時自動拉起
主服務器只在有至少 N 個從服務器的狀況下,才執行寫操做
從 Redis 2.8 開始, 爲了保證數據的安全性, 能夠經過配置, 讓主服務器只在有至少 N 個當前已鏈接從服務器的狀況下, 才執行寫命令。不過, 由於 Redis 使用異步複製, 因此主服務器發送的寫數據並不必定會被從服務器接收到, 所以, 數據丟失的可能性仍然是存在的。如下是這個特性的運做原理:
從服務器以每秒一次的頻率 PING 主服務器一次, 並報告複製流的處理狀況。
主服務器會記錄各個從服務器最後一次向它發送 PING 的時間。
用戶能夠經過配置, 指定網絡延遲的最大值 min-slaves-max-lag , 以及執行寫操做所需的至少從服務器數量 min-slaves-to-write
若是至少有 min-slaves-to-write 個從服務器, 而且這些服務器的延遲值都少於 min-slaves-max-lag 秒, 那麼主服務器就會執行客戶端請求的寫操做。你能夠將這個特性看做 CAP 理論中的 C 的條件放寬版本: 儘管不能保證寫操做的持久性, 但起碼丟失數據的窗口會被嚴格限制在指定的秒數中。
若是條件達不到 min-slaves-to-write 和 min-slaves-max-lag 所指定的條件, 那麼寫操做就不會被執行, 主服務器會向請求執行寫操做的客戶端返回一個錯誤
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,至少須要3~4臺服務器來存儲。這個實際是對現有 業務狀況所作的一個容量規劃,假如業務增加很快,很快就會發現當前的容量已經不夠了,Redis裏面存儲的數據很快就會超過物理內存大小,如何進行 Redis的在線擴容呢?Redis的做者提出了一種叫作presharding的方案來解決動態擴容和數據分區的問題,實際就是在同一臺機器上部署多個Redis實例的方式,當容量不夠時將多個實例拆分到不一樣的機器上,這樣實際就達到了擴容的效果