本套技術專欄是做者(秦凱新)平時工做的總結和昇華,並深度整理大量網上資源和專業書籍。經過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和集羣環境容量規劃等內容,請持續關注本套博客。QQ郵箱地址:1120746959@qq.com,若有任何學術交流,可隨時聯繫。node
redis其實是個單線程工做模型,其擁有較多的數據結構,並支持豐富的數據操做,redis目前是原生支持cluster模式。若是須要緩存可以支持更復雜的結構和操做,基於以上緣由,選擇線上使用Redis會是不錯的選擇。mysql
Redis高效的緣由:react
1)純內存操做redis
2)核心是基於非阻塞的IO多路複用機制sql
3)單線程反而避免了多線程的頻繁上下文切換問題數據庫
mysql單機支撐到2000qps就會出現性能問題了,因此要是你有個系統,高峯期一秒鐘過來的請求有1萬,那一個mysql單機絕對會死掉。由於 redis單機支撐的併發量輕鬆一秒幾萬十幾萬,支撐高併發so easy。單機承載併發量是mysql單機的幾十倍。緩存
使用Redis可能會遇到的問題:網絡
(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當前產生的事件,來選擇對應的事件處理器來處理。
若是一個客戶端跟redis發起鏈接,此時會產生一個AE_READABLE事件,而後由鏈接應答處理器來處理跟客戶端創建鏈接,建立客戶端對應的socket,同時將這個socket的AE_READABLE事件跟命令請求處理器關聯起來。
若是是客戶端要鏈接redis,那麼會爲socket關聯鏈接應答處理器。
若是是客戶端要寫數據到redis,那麼會爲socket關聯命令請求處理器。
若是是客戶端要從redis讀數據,那麼會爲socket關聯命令回覆處理器。
建立客戶端對應的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事件和命令回覆處理器的關聯關係。
用內存當緩存。內存是無限的嗎?內存是很寶貴並且是有限的,磁盤是廉價並且是大量的。可能一臺機器就幾十個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優先移除
redis高併發:主從架構,一主多從,通常來講,不少項目其實就足夠了,單主用來寫入數據,單機幾萬QPS,多從用來查詢數據,多個從實例能夠提供每秒10萬的QPS。
redis高併發的同時,還須要容納大量的數據:一主多從,每一個實例都容納了完整的數據,好比redis主就10G的內存量,其實你就最對只能容納10g的數據量。若是你的緩存要容納的數據量很大,達到了幾十g,甚至幾百g,或者是幾t,那你就須要redis集羣,並且用redis集羣以後,能夠提供可能每秒幾十萬的讀寫併發。
redis高可用:若是你作主從架構部署,其實就是加上哨兵就能夠了,就能夠實現,任何一個實例宕機,自動會進行主備切換。
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能夠提升讀的吞吐量。
若是採用了主從架構,那麼建議必須開啓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數據清空故障。
當啓動一個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。
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觸發增量複製。
master每次接收到寫命令以後,先在內部寫入數據,而後異步發送給slave node
結合大數據在咱們工業大數據平臺的實踐,總結成一篇實踐指南,方便之後查閱反思,後續我會根據本篇博客進行代碼技術實踐實現。
凱新雲技術社區