Redis內核原理及讀寫一致企業級架構深刻剖析1-綜合組件環境實戰

本套技術專欄是做者(秦凱新)平時工做的總結和昇華,並深度整理大量網上資源和專業書籍。經過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和集羣環境容量規劃等內容,請持續關注本套博客。QQ郵箱地址:1120746959@qq.com,若有任何學術交流,可隨時聯繫。node

1 Redis 工做模型

redis其實是個單線程工做模型,其擁有較多的數據結構,並支持豐富的數據操做,redis目前是原生支持cluster模式。若是須要緩存可以支持更復雜的結構和操做,基於以上緣由,選擇線上使用Redis會是不錯的選擇。mysql

1.1 Redis 高效的緣由:

  • Redis高效的緣由:react

    1)純內存操做redis

    2)核心是基於非阻塞的IO多路複用機制sql

    3)單線程反而避免了多線程的頻繁上下文切換問題數據庫

  • mysql單機支撐到2000qps就會出現性能問題了,因此要是你有個系統,高峯期一秒鐘過來的請求有1萬,那一個mysql單機絕對會死掉。由於 redis單機支撐的併發量輕鬆一秒幾萬十幾萬,支撐高併發so easy。單機承載併發量是mysql單機的幾十倍。緩存

  • 使用Redis可能會遇到的問題:網絡

    • 緩存與數據庫雙寫不一致
    • 緩存雪崩
    • 緩存穿透
    • 緩存併發競爭

1.2 文件事件處理流程

  • (1) redis基於reactor模式開發了網絡事件處理器,這個處理器叫作文件事件處理器,file event handler。這個文件事件處理器,是單線程的,redis才叫作單線程的模型,採用IO多路複用機制同時監聽多個socket,根據socket上的事件來選擇對應的事件處理器來處理這個事件。數據結構

  • (2) 文件事件處理器的結構包含4個部分:多個socket,IO多路複用程序,文件事件分派器,事件處理器(命令請求處理器、命令回覆處理器、鏈接應答處理器,等等)。多線程

  • (3) 多個socket可能併發的產生不一樣的操做,每一個操做對應不一樣的文件事件,可是IO多路複用程序會監聽多個socket,可是會將socket放入一個隊列中排隊,每次從隊列中取出一個socket給事件分派器,事件分派器把socket給對應的事件處理器。

  • (4) 一個socket的事件處理完以後,IO多路複用程序纔會將隊列中的下一個socket給事件分派器。文件事件分派器會根據每一個socket當前產生的事件,來選擇對應的事件處理器來處理。

1.3 文件事件

  • 若是一個客戶端跟redis發起鏈接,此時會產生一個AE_READABLE事件,而後由鏈接應答處理器來處理跟客戶端創建鏈接,建立客戶端對應的socket,同時將這個socket的AE_READABLE事件跟命令請求處理器關聯起來。

  • 若是是客戶端要鏈接redis,那麼會爲socket關聯鏈接應答處理器。

  • 若是是客戶端要寫數據到redis,那麼會爲socket關聯命令請求處理器。

  • 若是是客戶端要從redis讀數據,那麼會爲socket關聯命令回覆處理器。

1.4 客戶端與redis通訊流程

  • 建立客戶端對應的socket

    在redis啓動初始化的時候,redis會將鏈接應答處理器跟AE_READABLE事件關聯起來,接着若是一個客戶端跟redis發起鏈接,此時會產生一個AE_READABLE事件,而後由鏈接應答處理器來處理跟客戶端創建鏈接,建立客戶端對應的socket,同時將這個socket的AE_READABLE事件跟命令請求處理器關聯起來。

  • 請求處理,socket產生一個AE_READABLE事件

    當客戶端向redis發起請求的時候(不論是讀請求仍是寫請求,都同樣),首先就會在socket產生一個AE_READABLE事件,而後由對應的命令請求處理器來處理。這個命令請求處理器就會從socket中讀取請求相關數據,而後進行執行和處理。

  • 客戶端這讀取響應數據

    接着redis這邊準備好了給客戶端的響應數據以後,就會將socket的AE_WRITABLE事件跟命令回覆處理器關聯起來,當客戶端這邊準備好讀取響應數據時,就會在socket上產生一個AE_WRITABLE事件,會由對應的命令回覆處理器來處理,就是將準備好的響應數據寫入socket,供客戶端來讀取。

  • 命令回覆處理器寫完以後,就會刪除這個socket的AE_WRITABLE事件和命令回覆處理器的關聯關係。

2 redis 過時策略及內存淘汰機制

  • 用內存當緩存。內存是無限的嗎?內存是很寶貴並且是有限的,磁盤是廉價並且是大量的。可能一臺機器就幾十個G的內存,可是能夠有幾個T的硬盤空間。redis主要是基於內存來進行高性能、高併發的讀寫操做的。

    set key value 過時時間(1小時)
      set進去的key,1小時以後就沒了,就失效了,可是數據明明都過時了,怎麼還佔用着內存呢?
    複製代碼
  • 按期刪除+惰性刪除

    所謂按期刪除,指的是redis默認是每隔100ms就隨機抽取一些設置了過時時間的key,檢查其是否過時,若是過時就刪除。假設redis裏放了10萬個key,都設置了過時時間,你每隔幾百毫秒,就檢查10萬個key,那redis基本上就死了,cpu負載會很高的,消耗在你的檢查過時key上了。注意,這裏可不是每隔100ms就遍歷全部的設置過時時間的key,那樣就是一場性能上的災難。實際上redis是每隔100ms隨機抽取一些key來檢查和刪除的。

    所謂惰性刪除了,指的是在你獲取某個key的時候,redis會檢查一下 ,這個key若是設置了過時時間那麼是否過時了?若是過時了此時就會刪除,不會給你返回任何東西。

    若是按期刪除漏掉了不少過時key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?若是大量過時key堆積在內存裏,致使redis內存塊耗盡了,也會出現嚴重的線上事故。

  • 內存淘汰

    若是redis的內存佔用過多的時候,此時會進行內存淘汰,有以下一些策略:

    redis 10個key,如今已經滿了,redis須要刪除掉5個key
      1個key,最近1分鐘被查詢了100次
      1個key,最近10分鐘被查詢了50次
      1個key,最近1個小時倍查詢了1次
    複製代碼

    1)noeviction:當內存不足以容納新寫入數據時,新寫入操做會報錯。

    2)allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最經常使用的)

    3)allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,這個通常沒人用吧,爲啥要隨機,確定是把最近最少使用的key給幹掉啊。

    4)volatile-lru:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,移除最近最少使用的key(這個通常不太合適)

    5)volatile-random:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,隨機移除某個key

    6)volatile-ttl:當內存不足以容納新寫入數據時,在設置了過時時間的鍵空間中,有更早過時時間的key優先移除

3 redis 高併發和高可用企業級實踐

  • redis高併發:主從架構,一主多從,通常來講,不少項目其實就足夠了,單主用來寫入數據,單機幾萬QPS,多從用來查詢數據,多個從實例能夠提供每秒10萬的QPS。

    redis高併發的同時,還須要容納大量的數據:一主多從,每一個實例都容納了完整的數據,好比redis主就10G的內存量,其實你就最對只能容納10g的數據量。若是你的緩存要容納的數據量很大,達到了幾十g,甚至幾百g,或者是幾t,那你就須要redis集羣,並且用redis集羣以後,能夠提供可能每秒幾十萬的讀寫併發。

  • redis高可用:若是你作主從架構部署,其實就是加上哨兵就能夠了,就能夠實現,任何一個實例宕機,自動會進行主備切換。

4 redis replication基本原理

  • redis採用異步方式複製數據到slave節點,不過redis 2.8開始, slave node會週期性地確認本身每次複製的數據量

  • 一個master node是能夠配置多個slave node的

  • slave node也能夠鏈接其餘的slave node

  • slave node作複製的時候,是不會阻塞master node正常工做的

  • slave node在作複製的時候,也不會block對本身的查詢操做,它會用舊的數據集來提供服務; 可是複製完成的時候,須要刪除舊數據集,加載新數據集,這個時候就會暫停對外服務了

  • slave node主要用來進行橫向擴容,作讀寫分離,擴容的slave node能夠提升讀的吞吐量。

5 Redis數據持久性重要性

5.1 數據丟失風險

  • 若是採用了主從架構,那麼建議必須開啓master node的持久化,不建議用slave node做爲master node的數據熱備,由於那樣的話,若是你關掉master的持久化,可能在master宕機重啓的時候數據是空的,而後可能一通過複製,salve node數據也丟了。

    master -> RDB和AOF都關閉了 -> 所有在內存中

    master宕機,重啓,是沒有本地數據能夠恢復的,而後就會直接認爲本身IDE數據是空的

    master就會將空的數據集同步到slave上去,全部slave的數據所有清空。

    100%的數據丟失,所以master節點,必需要使用持久化機制。

  • 採用高可用機制也是有風險的,slave node能夠自動接管master node,可是也可能sentinal尚未檢測到master failure,master node就自動重啓了,仍是可能致使上面的全部slave node數據清空故障。

5.2 主從架構和斷點續傳原理

  • 當啓動一個slave node的時候,它會發送一個PSYNC命令給master node。若是這是slave node從新鏈接master node,那麼master node僅僅會複製給slave部分缺乏的數據; 不然若是是slave node第一次鏈接master node,那麼會觸發一次full resynchronization

    開始full resynchronization的時候,master會啓動一個後臺線程,開始生成一份RDB快照文件,同時還會將從客戶端收到的全部寫命令緩存在內存中。RDB文件生成完畢以後,master會將這個RDB 發送給slave,slave會先寫入本地磁盤,而後再從本地磁盤加載到內存中。而後master會將內存中緩存的寫命令發送給slave,slave也會同步這些數據。

    slave node若是跟master node有網絡故障,斷開了鏈接,會自動重連。master若是發現有多個slave node都來從新鏈接,僅僅會啓動一個rdb save操做,用一份數據服務全部slave node。

  • 從redis 2.8開始,就支持主從複製的斷點續傳,若是主從複製過程當中,網絡鏈接斷掉了,那麼能夠接着上次複製的地方,繼續複製下去,而不是從頭開始複製一份

    master node會在內存中常見一個backlog,master和slave都會保存一個replica offset還有一個master id,offset就是保存在backlog中的。若是master和slave網絡鏈接斷掉了,slave會讓master從上次的replica offset開始繼續複製

    可是若是沒有找到對應的offset,那麼就會執行一次resynchronization

  • 無磁盤化複製,master在內存中直接建立rdb,而後發送給slave,不會在本身本地落地磁盤了,

    repl-diskless-sync
    repl-diskless-sync-delay,等待必定時長再開始複製,由於要等更多slave從新鏈接過來
    複製代碼
  • 過時key處理

    slave不會過時key,只會等待master過時key。若是master過時了一個key,或者經過LRU淘汰了一個key,那麼會模擬一條del命令發送給slave。

5 Redis主從複製原理深刻剖析

5.1 完整流程

  • slave node啓動,僅僅保存master node的信息,包括master node的host和ip,可是複製流程沒開始時, master host和ip是從哪兒來的,是在redis.conf裏面的slaveof配置的。
  • slave node內部有個定時任務,每秒檢查是否有新的master node要鏈接和複製,若是發現,就跟master node創建socket網絡鏈接
  • slave node發送ping命令給master node
  • 口令認證,若是master設置了requirepass,那麼salve node必須發送masterauth的口令過去進行認證
  • master node第一次執行全量複製,將全部數據發給slave node
  • master node後續持續將寫命令,異步複製給slave node

5.2 數據同步

  • master和slave都會維護一個offset

    master會在自身不斷累加offset,slave也會在自身不斷累加offset slave每秒都會上報本身的offset給master,同時master也會保存每一個slave的offset

    這個倒不是說特定就用在全量複製的,主要是master和slave都要知道各自的數據的offset,才能知道互相之間的數據不一致的狀況。

  • backlog

    master node有一個backlog,默認是1MB大小

    master node給slave node複製數據時,也會將數據在backlog中同步寫一份

    backlog主要是用來作全量複製中斷後增量複製的

  • master run id

    info server,能夠看到master run id,若是根據host+ip定位master node,是不靠譜的,若是master node重啓或者數據出現了變化,那麼slave node應該根據不一樣的run id區分,run id不一樣就作全量複製 若是須要不更改run id重啓redis,可使用redis-cli debug reload命令

  • psync

    從節點使用psync從master node進行復制,psync runid offset

    master node會根據自身的狀況返回響應信息,也多是FULLRESYNC runid offset觸發全量複製,多是CONTINUE觸發增量複製。

5.3 全量複製

  • master執行bgsave,在本地生成一份rdb快照文件
  • master node將rdb快照文件發送給salve node,若是rdb複製時間超過60秒(repl-timeout),那麼slave node就會認爲複製失敗,能夠適當調節大這個參數
  • 對於千兆網卡的機器,通常每秒傳輸100MB,6G文件,極可能超過60s
  • master node在生成rdb時,會將全部新的寫命令緩存在內存中,在salve node保存了rdb以後,再將新的寫命令複製給salve node
  • client-output-buffer-limit slave 256MB 64MB 60,若是在複製期間,內存緩衝區持續消耗超過64MB,或者一次性超過256MB,那麼中止複製,複製失敗
  • slave node接收到rdb以後,清空本身的舊數據,而後從新加載rdb到本身的內存中,同時基於舊的數據版本對外提供服務
  • 若是slave node開啓了AOF,那麼會當即執行BGREWRITEAOF,重寫AOF
  • rdb生成、rdb經過網絡拷貝、slave舊數據的清理、slave of rewrite,很耗費時間,若是複製的數據量在4G~6G之間,那麼極可能全量複製時間消耗到1分半到2分鐘。

5.4 增量複製

  • 若是全量複製過程當中,master-slave網絡鏈接斷掉,那麼salve從新鏈接master時,會觸發增量複製
  • master直接從本身的backlog中獲取部分丟失的數據,發送給slave node,默認backlog就是1MB
  • msater就是根據slave發送的psync中的offset來從backlog中獲取數據的。

5.5 heartbeat

  • 主從節點互相都會發送heartbeat信息,master默認每隔10秒發送一次heartbeat,salve node每隔1秒發送一個heartbeat。

5.6 異步複製

master每次接收到寫命令以後,先在內部寫入數據,而後異步發送給slave node

6 總結

結合大數據在咱們工業大數據平臺的實踐,總結成一篇實踐指南,方便之後查閱反思,後續我會根據本篇博客進行代碼技術實踐實現。

凱新雲技術社區

相關文章
相關標籤/搜索