Redis 專題

 

1、幾種基礎數據類型

String 計數器,用戶信息(id)映射,唯一性(例如用戶資格判斷),bitmap
Hash 常見場景:存儲對象的屬性信息(用戶資料)
List 常見場景:評論存儲,消息隊列
Set 常見場景:資格判斷(例如用戶獎勵領取判斷),數據去重等
ZSet(Sorted Set) 常見場景:排行榜,延時隊列

 

Redis快的原因:https://zhuanlan.zhihu.com/p/57089960

還有IO多路複用:https://zhuanlan.zhihu.com/p/83398714

Redis分佈式鎖:redisson保持了簡單易用、支持鎖重入、支持阻塞等待、Lua腳本原子操作

分類 方案 實現原理 優點 缺點
基於數據庫 基於mysql 表唯一索引 1.表增加唯一索引
2.加鎖:執行insert語句,若報錯,則表明加鎖失敗
3.解鎖:執行delete語句
完全利用DB現有能力,實現簡單 1.鎖無超時自動失效機制,有死鎖風險
2.不支持鎖重入,不支持阻塞等待
3.操作數據庫開銷大,性能不高
基於MongoDB findAndModify原子操作 1.加鎖:執行findAndModify原子命令查找document,若不存在則新增
2.解鎖:刪除document
實現也很容易,較基於MySQL唯一索引的方案,性能要好很多 1.大部分公司數據庫用MySQL,可能缺乏相應的MongoDB運維、開發人員
2.鎖無超時自動失效機制
基於分佈式協調系統 基於ZooKeeper 1.加鎖:在/lock目錄下創建臨時有序節點,判斷創建的節點序號是否最小。若是,則表示獲取到鎖;否,則則watch /lock目錄下序號比自身小的前一個節點
2.解鎖:刪除節點
1.由zk保障系統高可用
2.Curator框架已原生支持系列分佈式鎖命令,使用簡單
需單獨維護一套zk集羣,維保成本高
基於緩存 基於redis命令 1. 加鎖:執行setnx,若成功再執行expire添加過期時間
2. 解鎖:執行delete命令
實現簡單,相比數據庫和分佈式系統的實現,該方案最輕,性能最好 1.setnx和expire分2步執行,非原子操作;若setnx執行成功,但expire執行失敗,就可能出現死鎖
2.delete命令存在誤刪除非當前線程持有的鎖的可能
3.不支持阻塞等待、不可重入
基於redis Lua腳本能力 1. 加鎖:執行SET lock_name random_value EX seconds NX 命令

2. 解鎖:執行Lua腳本,釋放鎖時驗證random_value 
-- ARGV[1]爲random_value,  KEYS[1]爲lock_name

if redis.call("get", KEYS[1]) == ARGV[1] then

    return redis.call("del",KEYS[1])

else

    return 0

end

同上;實現邏輯上也更嚴謹,除了單點問題,生產環境採用用這種方案,問題也不大。 不支持鎖重入,不支持阻塞等待

 

Redis 集羣方案:

1、http://blog.ipalfish.com/?p=576  

2、動態遷移過程 https://blog.csdn.net/productshop/article/details/50387075  https://zhuanlan.zhihu.com/p/53044266 https://github.com/tiancaiamao/go.blog/blob/master/content/codis%E6%95%B0%E6%8D%AE%E8%BF%81%E7%A7%BB%E6%9C%9F%E9%97%B4%E7%9A%84%E4%B8%80%E8%87%B4%E6%80%A7.md (準確的)

a. Dashboard向所有codis-proxy發出 pre_migrate slot_1 to group 2,將slot_1狀態標記爲”pre_migrate”。

b.  處在pre_migrate的slot_1是隻讀的。

c. 如果收到了所有proxy回覆收到遷移指令,那麼將slot_1的狀態修改爲”migrating”,並將slot_1的server group改爲group2。

d. (誰?)不斷髮送SLOTSMGRT命令給group1的redis ,直到slot_1所有的key遷移完成。

e. 如果遷移期間,有請求 slot_1 的 key 數據。先在group1上強行執行一次 MIGRATE key,將數據遷移過去,然後再將請求轉發到group2上。

 

a.剛開始,Codis Dashboard 會先統計哪些 slot 是需要遷移的。然後開始針對每個 slot 進行遷移,從代碼上來看同時遷移的 slot 數量是可以配置的,一般應該不會同時遷移過多 slot。

b.遷移 slot 數據時,會先將 slot 的最新信息通知給各個 Codis Proxy,信息包括 slot 的 id、原 Redis Group ID(我們叫它 From),新 Redis Group ID(我們叫它 Target),通過這些信息告知 Proxy 這個 slot 正在遷移數據。

c.當且只有當所有的 Proxy 都成功更新了 Slot 信息之後,Codis Dashboard 纔會真正開始遷移數據。爲了遷移數據 Codis Dashboard 會發送 SLOTSMGRTTAGSLOT 命令,讓 Target 節點從 From 節點遷移 slot 的 kv 數據

d.Redis Group 完成數據遷移之後,Codis Dashboard 會將新的 slot 信息發送給各個 Proxy,告知 Proxy 遷移工作已經完成,可以按照普通的請求代理邏輯來處理這個 slot 的請求了。

 

Redis

 

Redis不是強一致性性的

  • 1 Redis持久化方式
    • RDB持久化方式會在一個特定的時間點保存那個時間點的一個數據快照(全量的);
    • AOF(AOF Append Only File,僅追加數據)持久化方式則會記錄每一個服務器收到的寫操作。
    • 兩種持久化方案對比:https://www.jianshu.com/p/2b5793039531
    • Redis的持久化是可以禁用的,就是說你可以讓數據的生命週期只存在於服務器的運行時間裏。兩種方式的持久化是可以同時存在的,但是當Redis重啓時,AOF文件會被優先用於重建數據。使用的建議是Master關閉持久化,只有slave進行持久化。slave建議鏈式連接。
  • 2 Redis的數據安全問題
    • Redis在持久化的時候,不像Mysql和RocketMq那樣,保證相關數據持久化到磁盤後,才返回成功。而是先將結果放到內存,隔一段時間再將數據持久化到磁盤,這樣的處理,保證了其高效性。但是如果此時機器宕機,那麼就會丟失上次持久化之後到宕機期間變更的數據。從這個角度看,持久化的頻率越高,數據的安全性就越高。AOF默認1s就行一次持久化,而RDB可以根據時間,也可以根據指定時間範圍內寫指令次數決定持久化時間點,普遍會大於1s。如果數據量較大RDB方式下的持久化操作會對redis有較大影響。(AOF緩存區除了記錄寫入指令外,還記錄最近一次同步磁盤時間。主線程在向AOF緩衝區寫入數據的時候,會判斷距離上次刷盤時間是否超過2s,如果超過,就阻塞寫命令。因此AOF在everysec刷盤策略下可能會丟失2s的數據)
  • 3 Redis的服務可用性問題

 

Redis基礎數據類型

 

Redis是一個基於內存同時具備數據持久化能力的高性能、低時延的KV數據庫。key只能是String字符串結構,value的數據結構可以是String,List,HashMap,set(集合),Zset(有序集合)。

 

壓縮列表(zipList)

  • 壓縮列表是Redis爲了節約內存而開發的,是由一系列特殊編碼的連續內存塊(而不是像雙端鏈表一樣每個節點是指針)組成的順序型數據結構;具體結構相對比較複雜,略。與雙端鏈表相比,壓縮列表可以節省內存空間,但是進行修改或增刪操作時,複雜度較高;因此當節點數量較少時,可以使用壓縮列表;但是節點數量多時,還是使用雙端鏈表划算。
  • 壓縮列表不僅用於實現列表,也用於實現哈希、有序列表;使用非常廣泛。

 

字符串

  • 字符串是最基礎的類型,因爲所有的鍵都是字符串類型,且字符串之外的其他幾種複雜類型的元素也是字符串。字符串長度不能超過512MB。
  • Redis的字符串沒有直接使用C語言的字符串,而是用了簡單動態字符串SDS(Simple Dynamic String)。SDS相較於C字符串的優點:
    • 獲取字符串長度:記錄了字符串長度,可以快速獲取字符串長度。
    • 緩衝區溢出:因爲記錄了字符串長度和可用長度,可以自動分配內存,避免溢出。
    • 內存預分配策略和惰性釋放內存,有效減少了內存重新分配的次數。
    • 存儲二進制數據:因爲C字符串用空字符作爲結尾,因此不能存儲二進制數據。

 

列表

  • Redis中的列表支持兩端插入和彈出,並可以獲得指定位置(或範圍)的元素,可以充當數組、隊列、棧等。
  • 列表的內部編碼可以是壓縮列表(ziplist)或雙端鏈表(linkedlist)。
    • 雙端鏈表同時保存了表頭指針和表尾指針,並且每個節點都有指向前和指向後的指針;鏈表中保存了列表的長度
    • 只有同時滿足下面兩個條件時,纔會使用壓縮列表:列表中元素數量小於512個;列表中所有字符串對象都不足64字節。如果有一個條件不滿足,則使用雙端列表;且編碼只可能由壓縮列表轉化爲雙端鏈表,反方向則不可能。

 

哈希

  • 哈希(作爲一種數據結構),不僅是redis對外提供的5種對象類型的一種(與字符串、列表、集合、有序結合並列),也是Redis作爲Key-Value數據庫所使用的數據結構。爲了說明的方便,在本文後面當使用“內層的哈希”時,代表的是redis對外提供的5種對象類型的一種;使用“外層的哈希”代指Redis作爲Key-Value數據庫所使用的數據結構。
  • 內層的哈希使用的內部編碼可以是壓縮列表(ziplist)和哈希表(hashtable)兩種;Redis的外層的哈希則只使用了hashtable。壓縮列表前面已介紹。與哈希表相比,壓縮列表用於元素個數少、元素長度小的場景;其優勢在於集中存儲,節省空間;同時,雖然對於元素的操作複雜度也由O(1)變爲了O(n),但由於哈希中元素數量較少,因此操作的時間並沒有明顯劣勢。
    • 只有同時滿足下面兩個條件時,纔會使用壓縮列表:哈希中元素數量小於512個;哈希中所有鍵值對的鍵和值字符串長度都小於64字節。如果有一個條件不滿足,則使用哈希表;且編碼只可能由壓縮列表轉化爲哈希表,反方向則不可能。

 

SET

  • 集合(set)與列表類似,都是用來保存多個字符串,但集合與列表有兩點不同:集合中的元素是無序的(這個順序是指插入順序),因此不能通過索引來操作元素;集合中的元素不能有重複。
  • 集合的內部編碼可以是整數集合(intset)或哈希表(hashtable)。整數集合適用於集合所有元素都是整數且集合元素數量較小的時候,與哈希表相比,整數集合的優勢在於集中存儲,節省空間;同時,雖然對於元素的操作複雜度也由O(1)變爲了O(n),但由於集合數量較少,因此操作的時間並沒有明顯劣勢。
    • 只有同時滿足下面兩個條件時,集合纔會使用整數集合:集合中元素數量小於512個;集合中所有元素都是整數值。如果有一個條件不滿足,則使用哈希表;且編碼只可能由整數集合轉化爲哈希表,反方向則不可能。

 

ZSET(有序集合)

  • 有序集合與集合一樣,元素都不能重複;但與集合不同的是,有序集合中的元素是有順序的。與列表使用索引下標作爲排序依據不同,有序集合爲每個元素設置一個分數(score)作爲排序依據。
  • 有序集合的內部編碼可以是壓縮列表(ziplist)或跳躍表(skiplist)。跳躍表是一種有序數據結構,通過在每個節點中維持多個指向其他節點的指針,從而達到快速訪問節點的目的。除了跳躍表,實現有序數據結構的另一種典型實現是平衡樹;大多數情況下,跳躍表的效率可以和平衡樹媲美,且跳躍表實現比平衡樹簡單很多,因此redis中選用跳躍表代替平衡樹。跳躍表支持平均O(logN)、最壞O(N)的複雜點進行節點查找,並支持順序操作。Redis的跳躍表實現由zskiplist和zskiplistNode兩個結構組成:前者用於保存跳躍表信息(如頭結點、尾節點、長度等),後者用於表示跳躍表節點。具體結構相對比較複雜,略。
    • 只有同時滿足下面兩個條件時,纔會使用壓縮列表:有序集合中元素數量小於128個;有序集合中所有成員長度都不足64字節。如果有一個條件不滿足,則使用跳躍表;且編碼只可能由壓縮列表轉化爲跳躍表,反方向則不可能。

 

Redis漸進式ReHash

  • 如果hash負載較高,需要進行rehash。重新申請更大的存儲空間(大於當前鍵值對數量的最近一個2的指數大小),將之前的數據拷貝到新申請的hash表中。爲滿足ReHash,在基礎的數據結構層面提供了以下兩點支持:
    • Redis hash字典,包含了兩個hash表,ht[0]是正常使用的,ht[1]只在擴容時使用。
    • rehashidx用來標識是否在進行rehash,以及rehash進度。rehashidx = -1,表示沒有在進行ReHash。
  • Redis爲什麼採用漸進式ReHash,在添加鍵值對後,判讀是否需要進行擴容,如果需要擴容,則進入擴容流程。如果像JAVA的hashMap擴容一樣,一次性將所有數據遷移完成,操作才返回,如果hash表比較大,耗時會較長,那麼對應的插入操作會阻塞較長時間。Redis是單線程的,較長的阻塞對性能影響較大,因此採用ReHash,將遷移分散到後續的操作中,每次操作只遷移一個hash桶。
    • 問題在於,如果對應hashmap訪問頻率較低,導致在很長的一段時間都無法完成遷移,也就是ht[0]和ht[1]在較長的時間共存,如果這樣的情況較多,會佔用較多的內存,可能會造成redis數據的驅逐。因此可以開啓一個線程定時協助遷移。

 

ReHash觸發的條件

  • 爲了讓哈希表的負載因子(load factor)維持在一個合理的範圍內,會使用rehash(重新散列)操作對哈希表進行相應的擴展或收縮。負載因子 = 鍵值對數量/hash表大小
  • Redis服務器目前沒有在執行BGSAVE命令或BGREWRITEAOF命令,並且哈希表的負載因子大於等於1。(引起rehash普遍的場景)
  • Redis服務器目前在執行BGSAVE命令或BGREWRITEAOF命令,並且哈希表的負載因子大於等於5。
  • 當哈希表的負載因子小於0.1時,對哈希表執行收縮操作。

 

ReHash的過程

  • Redis rehash採取漸進式rehash,在對hash表執行增刪改查操作之前,先根據rehashidx判斷是否在進行rehash(rehashidx 正常情況下爲 -1),如果在進行rehash,則首先對rehashidx對應的hash桶進行遷移,再執行正常的增刪改查。
    • 此處有一個優化點,根據rehashidx進行桶遷移的時候,可能出現對應桶爲空的情況,需要處理下一個,如果某個區間連續很多桶均爲空,會在這個地方浪費較多時間,導致較長的阻塞,因此限制最大桶爲空的情況,如果超過限制,桶還是爲空,則直接結束本次桶遷移,繼續後續的操作。
  • 如果遷移完成,將ht[0]指針指向ht[1]指向的數組,並將ht[1]置爲null,rehashidx置爲-1。

 

ReHash過程中操作的處理

  • ReHash過程中,存在兩個hash表,查詢、刪除、修改操作均先訪問ht[0],如果不存在,訪問ht[1];新增加入到ht[1]中。

 

Redis的數據淘汰策略

  • Redis提供6種數據淘汰策略:
  • 1、volatile-lru:從已設置過期時間的數據集中挑選最近最少使用的數據淘汰
  • 2、volatile-ttl:從已設置過期時間的數據集中挑選將要過期的數據淘汰
  • 3、volatile-random:從已設置過期時間的數據集中任意選擇數據淘汰
  • 4、allkeys-lru:從數據集(server.db[i].dict)中挑選最近最少使用的數據淘汰
  • 5、allkeys-random:從數據集(server.db[i].dict)中任意選擇數據淘汰
  • 6、no-enviction(驅逐):禁止驅逐數據
  • Redis 確定驅逐某個鍵值對後,會刪除這個數據,並將這個數據變更消息發佈到本地(AOF 持久化)和從機(主從連接)。

 

Redis 爲什麼快(https://www.zhihu.com/search?type=content&q=redis 單線程)

  • 完全基於內存
  • 採用單線程
    • 爲什麼講 CPU不是瓶頸,而網絡和內存是瓶頸,就不需要多線程了。
    • 上線文切換的消耗在哪
    • 單線程指的是處理網絡請求的是單線程,一個redis實例肯定包含多個線程
  • 使用多路I/O複用模型,非阻塞IO。
    • (確認是否也實現了零拷貝)
  • 自定義的數據結構

 

緩存穿透、緩存擊穿、緩存雪崩

  • 緩存作用於業務請求和數據庫中間,流程是先查詢緩存,如果緩存沒有再去查數據庫,如果數據庫查到的話,更新緩存,下次就會直接查詢緩存了。緩存穿透、緩存擊穿、緩存雪崩描述的都是緩存無效,直接查詢數據庫所引發的問題,區別在於場景。
  • 緩存穿透
    • 描述:
      • 如果查詢的數據在數據庫裏面不存在,那麼就無法有效更新緩存,因此每次查詢都會直接穿透緩存,直接查詢數據庫。
    • 解決方案:
      • 如果不存在,也寫入一個無效緩存,但是注意加上過期時間,避免數據庫已經存在,但是緩存裏面還是一個無效值(這不僅是用這種方式解決緩存穿透存在的問題,所有緩存形式都面臨這個問題)。
      • 布隆過濾器,將數據庫中存在的全量key存儲在布隆過濾器裏面。這種方案是用內存將全量信息都存儲下來了,可以前置到redis之前,過濾掉所有無效的key請求。採用這種方案的時候可能會存在一個疑問?使用布隆過濾器要維護布隆過濾器和數據庫的一致性,如果這樣,幹嘛不直接維護Redis和數據庫的一致性。這裏要理解布隆過濾器和redis之間作用的差異性,布隆過濾器僅存儲key,因此僅關注key的刪除和新增,不關注value,而key僅會新增和少量刪除,因此不用考慮key的淘汰策略,可以認爲一個key在布隆過濾器添加了,就會在一次運行期間或是永久(需要持久化)有效;redis存儲的是key和對應的value,value往往是易變的,因此一般要設置過期策略,以保持和數據庫數據的一致性。
      • 布隆過濾器過濾器的原理是,用一個二進制數組作爲基礎的數據結構,通過一系列隨機映射函數,將一個key映射到部分二進制位上。布隆過濾器存在的問題是,二進制位各個key是可以複用的,可能存在誤匹配的問題。例如:key1 是b1 b2, key2 是b3 b4,而key3是b2 b3,如果布隆過濾器中存在了key1、key2,即便不存在key3也會被誤認爲存在。
  • 緩存擊穿
    • 描述:
      • 緩存擊穿是指某一key失效,併發的請求先查詢redis,發小無有效數據,都直接請求數據庫。
    • 解決方案:
      • 如果發現緩存失效,先阻塞獲取一個互斥鎖,然後再查詢緩存,然後再查詢數據庫,更新緩存。(這樣處理可以使得只有一個線程會最終請求到數據庫,而其他均會讀取Redis)
  • 緩存雪崩
    • 描述:
      • 同一時刻,大量的redis key失效,直接請求數據庫。(除了key統一過期外,redis節點宕機,也會發生此類問題)。
    • 解決方案:
      • 針對key統一過期:
        • 設置隨機的過期時間
        • 用有限的併發請求數據庫,例如用單線程的線程池處理請求
        • 限制訪問數據庫的流量
      • 針對Redis節點失效:
        • 事前:使用本地緩存,保證Redis失效時,系統也是可用的(請求-本地緩存-Redis-數據庫);使用集羣分散熱點數據;使用哨兵+主從結構,實現故障探測和轉移,保證高可用;使用持久化保證數據安全,以便快速恢復。
        • 用有限的併發請求數據庫,例如用單線程的線程池處理請求
        • 限制訪問數據庫的流量

 

Codis

 

架構

  • Codis fe:集羣管理界面,多個Codis集羣可以共用一個Codis FE,通過配置文件管理後端的codis-dashboard。
  • Codis Dashboard(配置中心):集羣管理工具,支持Codis Proxy、Codis Server管理以及slot分配、遷移等操作。對於一個Codis集羣,Dashboard最多部署一個。
  • Storage(存儲):集羣狀態的外部存儲,目前支持ZooKeeper、Etcd、Fs三種。存儲slot和codis server的映射關係,以及一些集羣狀態,例如遷移狀態。
  • Codis Proxy(接入層,無狀態的):實現Redis協議,除部分不支持跨slot的命令外,表現與原生Redis無差異。Codis Proxy的路由信息不是從存儲獲取的,而是從Dashboard獲取的。
  • Codis Group(由codis server組成):一個Redis主從集羣,充當存儲節點。新加key到slot的映射結構,以及slot有關的操作及數據遷移指令。和普通的Redis服務器相比,最大的不同是,Codis 對 Redis 進行了擴展,支持 Redis服務器之間同步數據。
  • redis-sentinel(集羣哨兵):用於實現redis集羣的故障探測、故障轉移。

 

動態擴縮容

  • 擴縮容最爲關鍵的一點就是slot的遷移
  • 在使用自動擴縮容功能時,dashboard 會計算出需要遷移的slot。
  • dashboard 將slot的最新信息通知給各個Codis Proxy,信息包括slot的id、原 Redis Group ID(我們叫它From),新Redis Group ID(我們叫它 Target),通過這些信息告知Proxy這個slot正在遷移數據。如果Codis Proxy收到對應的信息,就會將對應的slot狀態改爲pre_migrate,處在pre_migrate狀態的slot讀寫都是阻塞的(部分說只禁讀,後面會討論只禁讀存在的問題)。
  • 如果dashboard收到所有的Codis Proxy的回覆,確保所有Codis Proxy都收到了slot的遷移指令。就會進入migrate階段,主要做兩件事情:a.向所有的Codis Proxy下發migrate指令,成功收到指令的Codis Proxy會將對應slot的狀態標記爲migrate;b.向相關codis server下發遷移指令,將From節點待遷移slot下的所有key都遷移到Target節點。如果Codis Proxy收到了對處在migrate狀態的slot的key的請求,處理分爲兩步:向From下發對應keyslotmigratekey(key遷移指令),確保key轉移到了Target節點上;將請求轉發給Target節點,按正常的redis命令處理。
  • Redis Group完成數據遷移之後,Codis Dashboard 會將新的slot信息發送給各個 Proxy,告知 Proxy 遷移工作已經完成,可以按照普通的請求代理邏輯來處理這個slot的請求了。

備註: slot遷移過程中的,兩階段提交思考

  • 1、直接進入migrate狀態,存在什麼問題?
    • 如果部分Codis Proxy沒有收到,或是延遲收到,就會存在不一致的情況。主要表現在,集羣A中的一個key已經遷移到了B(無論是Codis Proxy主導的遷移,還是codis server主導的遷移),但是沒有進入migrate狀態的Codis Proxy依舊會將這個key路由到集羣A,集羣A會判斷沒有這個key,這與實際情況不符。
  • 2、進入pre_migrate狀態,不禁止讀存在什麼問題?
  • 部分文章認爲slot進入pre_migrate狀態,僅禁止對應的寫請求就好。實際上讀也會存在問題,如果部分Codis Proxy進入了migrate狀態,但是部分Proxy還處在pre_migrate狀態,那麼讀也會出現不一致。因爲pre_migrate狀態依舊會走老路徑,但是key已經被遷移走了。

 

數據可靠&高可用&容災&故障轉移

  • Redis 的數據可靠性是通過下面幾點實現的:
    • 單機數據高可靠(持久化)
    • 遠程數據熱備(主從複製)
    • 定期冷備歸檔(RDB數據)
  • 高可用&容災&故障轉移主要是依賴codis的集羣方案:
    • 哨兵集羣(Sentinel)保證redis高可用。由一個或多個Sentinel實例組成的Sentinel系統,可以監視任意多個主服務器,以及這些主服務器屬下的所有的從服務器,並在被監視的主服務器進入下線狀態時,自動將下線主服務器屬下的某個從服務器升級爲新的主服務器,然後由主服務器代替已下線的主服務器繼續處理命令請求。同時將其他從服務器修改爲新的主服務的從節點。
    • 哨兵集羣(Sentinel)保證redis高可用,核心兩點是怎麼進行故障探測,怎麼進行故障轉移(剔除無法服務的機器,尋找可替代的從節點)。

migrate(MIGRATE 127.0.0.1 6380 key2 0[過期時間,0爲永不過期] 1000[遷移過程超時時間])

  • 序列化(相當於dump) (source)
  • 網絡傳輸 (source -> target)
  • 反序列化(相當於restore)(target)
  • 返回(target -> source)
  • 刪除之前的key(source)

 

Redis 主從複製

  • 主從複製是從節點發起的,所有操作都是從節點主導的

1.連接建立

  • 從節點保存主節點IP和端口
  • 建立socket連接,並使用ping命令進行連接測試
  • 從節點向主節點發送監聽端口

2.數據同步

  • 連接建立後,從節點查看是否執行過數據同步(未執行過slaveof命令,剛執行過slaveof no one)。如果之前沒有執行過數據同步,則發起全量複製。如果已經同步過,執行部分複製。
  • 主節點收到從節點的全量複製命令後,執行bgsave命令,在後臺生成RDB文件,並將之後的寫命令放置到複製緩衝區裏面。
  • 主節點收到從節點的部分複製命令後,查看從節點的傳輸的複製偏移量(offset)是否在複製積壓緩衝區內,如果在,將之後的內容傳輸給從節點執行部分複製,如果不在說明覆制積壓緩衝區已經淘汰了部分數據,執行全部複製。

3.全量複製

  • 從節點發起全量複製以及從節點發起部分複製,但是主節點發現不滿足部分複製,均會執行全量複製。有兩種場景不滿足部分複製,會進入全量複製:
    • 每個redis節點都有一個隨機ID(runid),初次複製的時候,主節點會將自己的runid發送給從節點,從節點進行部分複製的時候,主節點發現傳過來的runid不是自己的,只能進入全量複製。
    • 如果從節點的複製偏移不在複製積壓緩衝區內,只能進入全量複製。
  • 全量複製流程:
    • 從節點發起全量複製或是不滿足條件的部分複製
    • 主節點收到從節點發起的全量複製,或是收到從節點發起的部分複製但是發現複製偏移量不在複製積壓緩衝區內,執行bgsave命令(fork一個子進程執行,異步執行,不影響主節點響應命令),生成當前的內存快照RDB文件,並將之後的寫命令放到複製緩衝區內。
    • 主節點bgsave命令執行完成後,將RDB文件發送給從節點
    • 從節點收到RDB文件後,先清除自己的數據,然後將RDB文件載入,此時從節點和主節點執行bgsave命令時保持一致
    • 主節點將緩衝區數據發送給從節點,從節點執行這些命令
  • 全量複製是一個非常重的操作,主要的時間消耗:
    • bgsave時間
    • 主節點將RDB文件通過網絡傳輸給從節點
    • 從節點清空數據
    • 從節點載入RDB文件
    • 可能的AOF重寫時間

4.部分複製

  • 由於全量複製在主節點數據量較大時效率太低,因此Redis2.8開始提供部分複製,用於處理網絡中斷時的數據同步。
  • 如果主節點版本夠新,且runid與從節點發送的runid相同,且從節點發送的offset之後的數據在複製積壓緩衝區中都存在,則回覆+CONTINUE,表示將進行部分複製,從節點等待主節點發送其缺少的數據即可;

5.命令傳播

  • 在命令傳播階段,除了發送寫命令,主從節點還維持着心跳機制:PING和REPLCONF ACK。心跳機制對於主從複製的超時判斷、數據安全等有作用。
  • 在命令傳播階段,主節點除了將寫命令發送給從節點,還會發送一份給複製積壓緩衝區,作爲寫命令的備份;除了存儲寫命令,複製積壓緩衝區中還存儲了其中的每個節點對應的複製偏移量(offset)。由於複製積壓緩衝區定長且是先進先出,所以它保存的是主節點最近執行的寫命令;時間較早的寫命令會被擠出緩衝區。注意,無論主節點有一個還是多個從節點,都只需要一個複製積壓緩衝區。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章