京東面試官:Redis 這些我必問
原創(chuàng)·LBL-埃文斯
分布式緩存
緩存好處:高性能 + 高并發(fā)
高性能(常用)
數(shù)據(jù)庫查詢耗費了800ms塘雳,其他用戶對同一個數(shù)據(jù)再次查詢 ,假設(shè)該數(shù)據(jù)在10分鐘以內(nèi)沒有變化過,并且 10 分鐘之內(nèi)有 1000 個用戶 都查詢了同一數(shù)據(jù),10 分鐘之內(nèi),那 1000 每個用戶锋恬,每個人查詢這個數(shù)據(jù)都感覺很慢 800ms
比如 :某個商品信息,在 一天之內(nèi)都不會改變,但是這個商品每次查詢一次都要耗費2s邀泉,一天之內(nèi)被瀏覽 100W次
mysql 單機也就 2000qps,緩存單機輕松幾萬幾十萬qps,單機 承載并發(fā)量是 mysql 單機的幾十倍。
如果感覺小編寫得不錯钝鸽,請素質(zhì)三連:點贊+轉(zhuǎn)發(fā)+關(guān)注汇恤。我會努力寫出更好的作品分享給大家。更多學習資料小編已打包好拔恰,可以找我領(lǐng)取哦因谎!領(lǐng)取方式:私信回復(fù)暗號【444】即可免費領(lǐng)取更多完整版資料。
高并發(fā)
在中午高峰期颜懊,有 100W 個用戶訪問系統(tǒng) A财岔,每秒有 4000 個請求去查詢數(shù)據(jù)庫,數(shù)據(jù)庫承載每秒 4000 個請求會宕機饭冬,加上緩存后使鹅,可以 3000 個請求走緩存 ,1000 個請求走數(shù)據(jù)庫昌抠。
緩存是走內(nèi)存的患朱,內(nèi)存天然可以支撐4w/s的請求,數(shù)據(jù)庫(基于磁盤)一般建議并發(fā)請求不要超過 2000/s
緩存不良后果
緩存與數(shù)據(jù)庫雙寫不一致
緩存雪崩
緩存穿透
緩存并發(fā)競爭
Redis 線程模型
redis 單線程 炊苫,memcached 多線程
redis 是單線程 nio 異步線程模型
Redis 和 Memcached 區(qū)別
Redis 支持服務(wù)器端的數(shù)據(jù)操作:Redis比Memcached來說裁厅,擁有更多的數(shù)據(jù)結(jié)構(gòu)和并支持更豐富的數(shù)據(jù)操作,通常在Memcached里侨艾,你需要將數(shù)據(jù)拿到客戶端來進行類似的修改再set回去执虹。這大大增加了網(wǎng)絡(luò) IO 的次數(shù)和數(shù)據(jù)體積。在Redis中唠梨,這些復(fù)雜的操作通常和一般的GET/SET一樣高效袋励。所以,如果需要緩存能支持更復(fù)雜的結(jié)構(gòu)和操作,那么Redis會是不錯的選擇
集群模式:memcached 沒有原生的集群模式茬故,需要依靠客戶端來實現(xiàn)往集群中分片寫入數(shù)據(jù)盖灸,但是 Redis 目前是原生支持 cluster模式的
Redis 單線程模型
一個線程+一個隊列
redis 基于 reactor 模式開發(fā)了網(wǎng)絡(luò)事件處理器,這個處理器叫做文件事件處理器磺芭,file event handler赁炎,這個文件事件處理器是單線程的,所以redis 是單線程的模型钾腺,采用 io多路復(fù)用機制同時監(jiān)聽多個 socket,根據(jù)socket上的事件來選擇對應(yīng)的事件處理器來處理這個事件徙垫。
文件事件處理器包含:多個 socket,io多路復(fù)用程序,文件事件分派器放棒,事件處理器(命令請求處理器姻报、命令恢復(fù)處理器、連接應(yīng)答處理器)
文件事件處理器是單線程的间螟,通過 io 多路復(fù)用機制監(jiān)聽多個 socket逗抑,實現(xiàn)高性能和線程模型簡單性
被監(jiān)聽的 socket 準備好執(zhí)行 accept,read,write,close等操作的時候,會產(chǎn)生對應(yīng)的文件事件寒亥,調(diào)用之前關(guān)聯(lián)好的時間處理器處理
多個 socket并發(fā)操作,產(chǎn)生不同的文件事件荧关,i/o多路復(fù)用會監(jiān)聽多個socket溉奕,將這些 socket放入一個隊列中排隊。事件分派器從隊列中取出socket給對應(yīng)事件處理器忍啤。
一個socket時間處理完后加勤,事件分派器才能從隊列中拿到下一個socket,給對應(yīng)事件處理器來處理同波。
文件事件:
AE_READABLE 對應(yīng) socket變得可讀(客戶端對redis執(zhí)行 write操作)
AE_WRITABLE 對應(yīng) socket 變得可寫(客戶端對 redis執(zhí)行 read操作)
I/O 多路復(fù)用可以同時監(jiān)聽AE_REABLE和 AE_WRITABLE 鳄梅,如果同時達到則優(yōu)先處理 AE_REABLE 時間
文件事件處理器:
連接應(yīng)答處理器 對應(yīng) 客戶端要連接 redis
命令請求處理器 對應(yīng) 客戶端寫數(shù)據(jù)到 redis
命令回復(fù)處理器 對應(yīng) 客戶端從 redis 讀數(shù)據(jù)
流程:
redis 初始化時,會將連接應(yīng)答處理器跟 AE_READABLE事件關(guān)聯(lián)
客戶端對 redis 發(fā)起連接未檩,產(chǎn)生一個 AE_READABLE 事件
連接應(yīng)答處理器處理客戶端 AE_READABLE 事件戴尸,創(chuàng)建客戶端對應(yīng)的 socket,同時將這個 socket的 AE_READABLE 事件和命令請求處理器關(guān)聯(lián)
客戶端對 redis 發(fā)起讀請求冤狡,會在 socket上產(chǎn)生一個 AE_READABLE 事件
綁定 AE_READABLE 事件的命令請求處理器會從 socket 中讀取請求相關(guān)數(shù)據(jù)孙蒙,執(zhí)行對應(yīng)操作,當執(zhí)行完畢后悲雳,將 socket的 AE_WRITABLE 事件跟命令回復(fù)處理器關(guān)聯(lián)
當客戶端這邊準備好讀取響應(yīng)時挎峦,會在 socket上產(chǎn)生一個AE_WRITABLE事件
綁定 AE_WRITABLE 事件的命令回復(fù)處理器將準備好的響應(yīng)數(shù)據(jù)寫入 socket,供客戶端來讀取
命令回復(fù)處理器寫完后合瓢,刪掉 socket的 AE_WRITABLE 事件和命令回復(fù)處理器的綁定關(guān)系
Redis 單線程模型效率高
一秒鐘可以處理幾萬個請求
非阻塞 I/O 多路復(fù)用機制(不處理事件坦胶,只輪詢請求壓入隊列)
純內(nèi)存操作(操作只有幾微秒)
單線程反而 避免了多線程頻繁上下文切換的問題
Redis 數(shù)據(jù)類型
string
普通的 set,get kv緩存
hash
類型 map結(jié)構(gòu),比如一個對象(沒有嵌套對象)緩存到 redis里面,然后讀寫緩存的時候顿苇,可以直接操作hash的字段(比如把 age 改成 21峭咒,其他的不變)
key=150
value = {
"id":150,"name":"zhangsan","age":20
}
list
有序列表 ,元素可以重復(fù)
可以通過 list 存儲一些列表型數(shù)據(jù)結(jié)構(gòu)岖圈,類似粉絲列表讹语,文章評論列表。
例如:微信大 V的粉絲蜂科,可以以 list 的格式放在 redis 里去緩存
key=某大 V value=[zhangsan,lisi,wangwu]
比如 lrange 可以從某個元素開始讀取多少個元素顽决,可以基于 list 實現(xiàn)分頁查詢功能,基于 redis實現(xiàn)高性能分頁导匣,類似微博下來不斷分頁東西才菠。
可以搞個簡單的消息隊列,從 list頭懟進去(lpush)贡定,list尾巴出來 (brpop)
set
無序集合赋访,自動去重
需要對一些數(shù)據(jù)快速全局去重,(當然也可以基于 HashSet缓待,但是單機)
基于 set 玩差集蚓耽、并集、交集的操作旋炒。比如:2 個人的粉絲列表整一個交集步悠,看看 2 個人的共同好友是誰?
把 2 個大 V 的粉絲都放在 2 個 set中瘫镇,對 2 個 set做交集(sinter)
sorted set
排序的 set鼎兽,去重但是可以排序,寫進去的時候給一個分數(shù)铣除,自動根據(jù)分數(shù)排序
排行榜:
將每個用戶以及其對應(yīng)的分數(shù)寫入進去
zadd board score username
zrevrange board 0 99 可以獲取排名前 100 的用戶
zrank board username 可以看到用戶在排行榜里的排名
例如:
zadd board 85 zhangsan
zadd board 72 wangwu
zadd board 96 lis
zadd board 62 zhaoliu
自動排序為:
96 lisi
85 zhangsan
72 wangwu
62 zhaoliu
獲取排名前 3 的用戶 : zrevrange board 0 3
96 lisi
85 zhangsan
72 wangwu
查看zhaoliu的排行 :zrank board zhaoliu 返回 4
Redis 過期策略
內(nèi)存是寶貴的谚咬,磁盤是廉價的
給key設(shè)置過期時間后,redis對這批key是定期刪除+惰性刪除
定期刪除:
redis 默認每隔 100ms隨機抽取一些設(shè)置了過期時間的 key尚粘,檢查其是否過期了择卦,如果過期就刪除。
注意:redis是每隔100ms隨機抽取一些 key來檢查和刪除郎嫁,而不是遍歷所有的設(shè)置過期時間的key(否則CPU 負載會很高互捌,消耗在檢查過期 key 上)
惰性刪除:
獲取某個key的時候, redis 會檢查一下行剂,這個key如果設(shè)置了過期時間那么是否過期秕噪,如果過期了則刪除。
如果定期刪除漏掉了許多過期key厚宰,然后你也沒及時去查腌巾,也沒走惰性刪除遂填,如果大量過期的key堆積在內(nèi)存里,導(dǎo)致 redis 內(nèi)存塊耗盡澈蝙,則走內(nèi)存淘汰機制吓坚。
內(nèi)存淘汰策略:
noeviction:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,新寫入操作直接報錯(沒人用)
allkeys-lru: 當內(nèi)存不足以容納新寫入數(shù)據(jù)時灯荧,在鍵空間中礁击,移除最近最少使用的key(最常用)
allkeys-random: 當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在鍵空間中逗载,隨機移除某個 key哆窿,(沒人用)
volatile-lru:當內(nèi)存不足以容納新寫入數(shù)據(jù)時,在設(shè)置了過期時間的鍵空間中厉斟,移除最近最少使用的key(不合適)
volatile-ttl:當內(nèi)存不足以容納新寫入數(shù)據(jù)時挚躯,在設(shè)置了過期時間的鍵空間中,有更早過期時間的 key 優(yōu)先移除(不合適)
LRU 算法:
packagecom.mousycoder.mycode;importjava.util.LinkedHashMap;importjava.util.Map;/** *@version1.0 *@author: mousycoder *@date: 2019/10/31 17:55 */publicclassLRUCacheextendsLinkedHashMap{privatefinalintCACHE_SIZE;publicLRUCache(intcacheSize){super((int)Math.ceil(cacheSize /0.75) +1,0.75f,true);this.CACHE_SIZE = cacheSize;? ? }@OverrideprotectedbooleanremoveEldestEntry(Map.Entry<K, V> eldest){returnsize() > CACHE_SIZE;? ? }publicstaticvoidmain(String[] args){? ? ? ? LRUCache lruCache =newLRUCache<>(10);for(inti =0; i <15; i++) {? ? ? ? ? ? lruCache.put(i,i);? ? ? ? }? ? ? ? Integer integer1 = lruCache.get(0);for(Integer integer : lruCache.keySet()) {? ? ? ? ? ? System.out.println(integer);? ? ? ? }? ? }}
Redis 高并發(fā)和高可用
緩存架構(gòu)(多級緩存架構(gòu)擦秽、熱點緩存)
redis 高并發(fā)瓶頸在單機码荔,讀寫分離,一般是支撐讀高并發(fā)感挥,寫請求少缩搅,也就 一秒一兩千,大量請求讀触幼,一秒鐘二十萬次誉己。
主從架構(gòu)
一主多從,主負責寫域蜗,將數(shù)據(jù)同步復(fù)制到其他 slave節(jié)點,從節(jié)點負責讀噪猾,所有讀的請求全部走從節(jié)點霉祸。主要是解決讀高并發(fā)。袱蜡、
主從架構(gòu)->讀寫分離->支撐10W+讀QPS架構(gòu)
Redis Replication
master->slave 復(fù)制丝蹭,是異步的
核心機制:
redis 采用異步方式復(fù)制數(shù)據(jù)到 slave 節(jié)點
一個 master node是可以配置多個 slave node的
slave node也可以連接其他的 slave node
slave node 做復(fù)制的時候,是不會 block master node的正常工作
slave node 在做復(fù)制的時候坪蚁,也不會 block對自己的查詢操作奔穿,它會用舊的數(shù)據(jù)集來提供服務(wù)。但是復(fù)制完成時敏晤,需要刪除舊數(shù)據(jù)集贱田,加載新的數(shù)據(jù)集,這個時候就會暫停對外服務(wù)了嘴脾。
slave node 主要用來進行橫向擴容男摧,做讀寫分離蔬墩,擴容 slave node 可以提高讀的吞吐量
master持久化對主從架構(gòu)的意義:
如果開啟了主從架構(gòu),一定要開啟 master node的持久化耗拓,不然 master宕機重啟數(shù)據(jù)是空的拇颅,一經(jīng)復(fù)制,slave的數(shù)據(jù)也丟了
主從復(fù)制原理:
第一次啟動或者斷開重連情況:
當啟動一個 slave node的時候乔询,它會發(fā)送一個 PSYNC 命令給 master node
master 會觸發(fā)一次 full resynchronization (如果不是第一次連接樟插,master 只會復(fù)制給 slave 部分缺少的數(shù)據(jù),從backlog里找)
master會啟動一個后臺線程竿刁,開始生成一份 RDB 快照( bgsave,也可以直接在內(nèi)存中創(chuàng)建)黄锤,同時將從客戶端收到的所有寫命令緩存在內(nèi)存中。RDB 文件生成完畢之后们妥,master會將這個RDB發(fā)送給slave猜扮,slave會先寫入本地磁盤,然后再從本地磁盤加載到內(nèi)存中监婶。然后 master會將內(nèi)存中緩存的寫命令發(fā)送給 slave,slave也會同步這些數(shù)據(jù)(slave如果跟 master網(wǎng)絡(luò)故障旅赢,斷開連接,會自動重連惑惶,master如果發(fā)現(xiàn)有多個 slave 來重新連接煮盼,僅僅只會啟動一個 RDB save 操作,用一份數(shù)據(jù)服務(wù)所有 slave node)
正常情況下:
master 來一條數(shù)據(jù)带污,就異步給 slave
如果感覺小編寫得不錯僵控,請素質(zhì)三連:點贊+轉(zhuǎn)發(fā)+關(guān)注。我會努力寫出更好的作品分享給大家鱼冀。更多學習資料小編已打包好报破,可以找我領(lǐng)取哦!領(lǐng)取方式:私信回復(fù)暗號【444】即可免費領(lǐng)取更多完整版資料千绪。
Redis高可用性
全年 99.99%的時間充易,都是出于可用的狀態(tài),那么就可以稱為高可用性
redis 高可用架構(gòu)叫故障轉(zhuǎn)移荸型,failover盹靴,也可以叫做主備切換,切換的時間不可用瑞妇,但是整體高可用稿静。
sentinal node(哨兵)
Sentinal
作用:
集群監(jiān)控,負責監(jiān)控 redis master 和 slave進程是否正常
消息通知辕狰,如果某個 redis 實例有故障改备,那么哨兵負責發(fā)送消息作為報警通知給管理員
故障轉(zhuǎn)移,如果 master 掛掉蔓倍,會自動轉(zhuǎn)移到 slave
配置中心绍妨,如果故障轉(zhuǎn)移了润脸,通知 client 客戶端新的 master地址
兩節(jié)點哨兵集群
quorum = 1 (代表哨兵最低個數(shù)可以嘗試故障轉(zhuǎn)移,選舉執(zhí)行的哨兵)
master 宕機他去,只有 S2 存活毙驯,因為 quorum =1 可以嘗試故障轉(zhuǎn)移,但是沒達到 majority =2 (最低允許執(zhí)行故障轉(zhuǎn)移的哨兵存活數(shù))的標準灾测,無法執(zhí)行故障轉(zhuǎn)移
三節(jié)點哨兵集群(經(jīng)典)
如果 M1 宕機了爆价,S2,S3 認為 master宕機,選舉一個執(zhí)行故障轉(zhuǎn)移媳搪,因為 3 個哨兵的 majority = 2铭段,所以可以執(zhí)行故障轉(zhuǎn)移
Redis 主從 + 哨兵
丟數(shù)據(jù):
master內(nèi)存中數(shù)據(jù)異步同步到 slave master 就掛掉了,丟掉了 master 內(nèi)存中的數(shù)據(jù)
腦裂,某個 master 所在機器突然脫離了正常的網(wǎng)絡(luò)秦爆,其他 slave機器不能連接序愚,但是實際上 master還在運行,哨兵認為 master 宕機等限,選舉 slave為master爸吮,此時集群里有 2 個 master, client還沒來得及切換到新的master,還繼續(xù)寫在舊的 master上望门,數(shù)據(jù)丟了形娇,此時舊的 master再次恢復(fù),被被作為一個 slave 掛到新的 master 上筹误,自己的數(shù)據(jù)被清空 (腦裂桐早,大腦一分為 2,同時指揮同一個人)
解決方案:
min-slaves-max-lag 10 (至少一個 slave同步的延遲不能超過 10s) 減少異步復(fù)制的數(shù)據(jù)丟失厨剪,發(fā)現(xiàn)slave復(fù)制數(shù)據(jù)和 ack延時過長哄酝,拒絕寫入,減少同步數(shù)據(jù)損失祷膳。讓client做降級寫到本地磁盤里和限流陶衅,或者先暫存到消息隊列,然后重新發(fā)回 master
min-slaves-to-write 1 減少腦裂帶來的數(shù)據(jù)丟失钾唬,最多損失 10 s數(shù)據(jù),假設(shè)master 不能繼續(xù)給 slave發(fā)送數(shù)據(jù)侠驯,并且 slave 10s沒給自己的 ack消息抡秆,直接拒絕客戶端寫請求,同時 client做降寫到本地磁盤吟策、限流儒士,或者先暫存到消息隊列,然后重新發(fā)回 master
哨兵
sdown 主觀宕機檩坚,哨兵覺得一個 master 宕機(ping 超過了
is-master-down-after-milliseconds毫秒數(shù))
odown 客觀宕機着撩,quorum數(shù)量的哨兵都覺得 master宕機
哨兵互相感知通過 redis的 pub/sub系統(tǒng)诅福,每隔 2 秒往同一個 channel里發(fā)消息(自己的 host,ip,runid),其他哨兵可以消費這個消息
以及同步交換master的監(jiān)控信息拖叙。
哨兵確保其他slave修改master信息為新選舉的master
當一個 master被認為 odown && marjority哨兵都同意氓润,那么某個哨兵會執(zhí)行主備切換,選舉一個slave成為master(考慮 1. 跟master斷開連接的時長 2. slave 優(yōu)先級 3.復(fù)制 offset 4. runid)
選舉算法:
如果slave跟master斷開連接已經(jīng)超過 down-after-milliseconds * 10 + master宕機時間薯鳍,則放棄
按 slave 優(yōu)先級排序 咖气,slave-priority 越小越靠前
replica offset ,哪個slave復(fù)制越多的數(shù)據(jù)挖滤,越靠前
runid 越小崩溪,越靠前
quorum 數(shù)量哨兵認為odown->選舉一個哨兵切換->獲得 majority哨兵的授權(quán)(quorum < majority 需要 majority個哨兵授權(quán),quorum >= majority 需要 quorum 哨兵授權(quán))
第一個選舉出來的哨兵切換失敗了斩松,其他哨兵等待 failover-time之后伶唯,重新拿confiuration epoch做為新的version 切換,保證拿到最新配置惧盹,用于 configuration傳播(通過 pu/sub消息機制乳幸,其他哨兵對比 version 新舊更新 master配置)
Redis 優(yōu)化方案
高并發(fā):主從架構(gòu)
高容量:Redis集群,支持每秒幾十萬的讀寫并發(fā)
高可用:主從+哨兵
Redis 持久化
持久化的意義在于故障恢復(fù)數(shù)據(jù)備份(到其他服務(wù)器)+故障恢復(fù)(遇到災(zāi)難岭参,機房斷電反惕,電纜被切)
RDB 對 Redis 中的數(shù)據(jù)執(zhí)行周期性的持久化。
AOF 機制演侯,每條寫命令作為日志姿染,以 append-only模式寫入一個日志文件總,在 redis重啟的時候秒际,可以通過回放AOF日志中的寫入指令來重新構(gòu)建整個數(shù)據(jù)集
AOF 只有一個悬赏,Redis 中的數(shù)據(jù)是有一定限量的,內(nèi)存大小是一定的,AOF 是存放寫命令的娄徊,當大到一定的時候闽颇,AOF 做 rewrite 操作,就會基于當時 redis 內(nèi)存中的數(shù)據(jù)寄锐,來重新構(gòu)造一個更小的 AOF 文件兵多,然后將舊的膨脹很大的文件給刪掉,AOF 文件一直會被限制在和Redis內(nèi)存中一樣的數(shù)據(jù)橄仆。AOF同步間隔比 RDB 小剩膘,數(shù)據(jù)更完整
RDB
優(yōu)點:
RDB 會生成多個數(shù)據(jù)文件,每個數(shù)據(jù)文件都代表了某一個時刻中 redis 的數(shù)據(jù)盆顾,這種多個數(shù)據(jù)文件的方式怠褐,非常適合做冷備,可以將這種完整的數(shù)據(jù)文件發(fā)送到一些遠程的安全存儲上去您宪,RDB 做冷備奈懒,生成多個文件奠涌,每個文件都代表某一個時刻的完整的數(shù)據(jù)快照,AOF 也可以做冷備磷杏,只有一個文件溜畅,每隔一定時間去 copy一份這個文件出來。 RDB 做冷備茴丰,由Redis控制固定時長去生成快照文件达皿,比較方便。AOF贿肩,需要自己寫腳本定時控制峦椰。
RDB 對 redis對外提供的讀寫服務(wù),影響非常小汰规,可以讓 redis 保持高性能汤功,因為 redis 主進程只需要 fork一個子進程,讓子進程執(zhí)行磁盤 IO 操作來進行 RDB 持久化
相對于 AOF 持久化機制來說溜哮,直接基于 RDB 數(shù)據(jù)文件來重啟和恢復(fù) redis 進程滔金,更加快速
缺點:
如果想要在 redis故障時,盡可能少丟數(shù)據(jù)茂嗓,那么 RDB 沒有 AOF 好餐茵,一般 RDB 數(shù)據(jù)快照,都是間隔 5 分鐘述吸,或者更長的時候生成一次忿族,這個時候就得接受一旦 redis 進程宕機,那么會丟失最近 5 分鐘數(shù)據(jù)
RDB 每次在 fork子進程來執(zhí)行 RDB 快早數(shù)據(jù)文件生成的時候蝌矛,如果數(shù)據(jù)文件特別大道批,可能會導(dǎo)致對客戶端提供的服務(wù)暫停數(shù)毫秒,甚至數(shù)秒(RDB 生成間隔不要太長)
AOF 存放的指令日志入撒,數(shù)據(jù)恢復(fù)的時候隆豹,需要回放執(zhí)行所有指令日志,RDB 就是一份數(shù)據(jù)文件茅逮,直接加載到內(nèi)存中璃赡。
AOF
優(yōu)點:
更好保護數(shù)據(jù)不丟失,后臺線程 fsync 操作献雅,最多丟失一秒鐘數(shù)據(jù)碉考,保證 os cache中的數(shù)據(jù)寫入磁盤中
AOF 用 append-only 模式,沒有磁盤尋址開銷惩琉,寫入性能非常高豆励,文件不容易損壞夺荒。
AOF 日志過大的時候瞒渠,后臺 rewrite log時候良蒸,老的日志文件照常寫入,新的merge后的日志文件 ready的時候伍玖,再交換新老日志文件
適合災(zāi)難性恢復(fù)嫩痰,某人不小心 flushall清空所有數(shù)據(jù),只要后臺 rewrite還沒發(fā)生窍箍,那么可以立刻拷貝 AOF 文件串纺,將最后一條 flushall命令給刪了,然后再將該 AOF 文件放回去椰棘,可以通過恢復(fù)機制纺棺,自動恢復(fù)所有數(shù)據(jù)
缺點:
AOF 日志文件比 RDB 數(shù)據(jù)快照文件大
降低 Redis的寫 QPS
AOF 復(fù)雜,Bug多
數(shù)據(jù)恢復(fù)比較慢
最佳方案
AOF 來保證數(shù)據(jù)不丟失邪狞,RDB 做不同時間的冷備
Redis Cluster
支持 N 個 Redis master node,每個 master node掛載多個 slave node
多master + 讀寫分離 + 高可用
數(shù)據(jù)量很少,高并發(fā) -> replication + sentinal 集群
海量數(shù)據(jù) + 高并發(fā) + 高可用 -> redis cluster
分布式算法
hash算法->一致性 hash 算法-> redis cluster->hash slot算法
redis cluster :自動對數(shù)據(jù)進行分片,每個 master 上放一部分數(shù)據(jù)鸯隅,提供內(nèi)置的高可用支持冠王,部分master不可用時,還是可以繼續(xù)工作
cluster bus 通過 16379進行通信剑令,故障檢測糊啡,配置更新,故障轉(zhuǎn)移授權(quán)吁津,另外一種二進制協(xié)議棚蓄,主要用于節(jié)點間進行高效數(shù)據(jù)交換,占用更少的網(wǎng)絡(luò)帶寬和處理時間
hash算法
key進行hash腺毫,然后對節(jié)點數(shù)量取模癣疟,最大問題只有任意一個 master 宕機,大量數(shù)據(jù)就要根據(jù)新的節(jié)點數(shù)取模潮酒,會導(dǎo)致大量緩存失效睛挚。
一致性 hash 算法
key進行hash,對應(yīng)圓環(huán)上一個點急黎,順時針尋找距離最近的一個點扎狱。保證任何一個 master 宕機,只受 master 宕機那臺影響勃教,其他節(jié)點不受影響淤击,此時會瞬間去查數(shù)據(jù)庫。
緩存熱點問題:
可能集中在某個 hash區(qū)間內(nèi)的值特別多故源,那么會導(dǎo)致大量的數(shù)據(jù)都涌入同一個 master 內(nèi)污抬,造成 master的熱點問題,性能出現(xiàn)瓶頸。
解決方法:
給每個 master 都做了均勻分布的虛擬節(jié)點印机,這樣每個區(qū)間內(nèi)大量數(shù)據(jù)都會均勻的分布到不同節(jié)點內(nèi)矢腻,而不是順時針全部涌入到同一個節(jié)點中。
Hash Slot算法
redis cluster 有固定 16384 個 hash slot,對每個key計算 CRC16 值射赛,然后對16384取模多柑,可以獲取 key對應(yīng)的 hash slot
redis cluster 中每個 master 都會持有部分 slot ,當一臺 master 宕機時候,會最快速度遷移 hash slot到可用的機器上(只會短暫的訪問不到)
走同一個 hash slot 通過 hash tag實現(xiàn)
Redis Cluster 核心
基礎(chǔ)通信
通過 gossip 協(xié)議通信(小道留言楣责,所有節(jié)點都持有一份元數(shù)據(jù)竣灌,不同的節(jié)點如果出現(xiàn)了元數(shù)據(jù)的變更,就不斷將元數(shù)據(jù)發(fā)送給其他節(jié)點秆麸,讓其他節(jié)點也進行元數(shù)據(jù)的變更)
集群元數(shù)據(jù):包括 hashslot->node之間的映射表關(guān)系初嘹,master->slave之間的關(guān)系,故障的信息
集群元數(shù)據(jù)集中式存儲(storm)沮趣,底層基于zookeeper(分布式協(xié)調(diào)中間件)集群所有元數(shù)據(jù)的維護削樊。好處:元數(shù)據(jù)的更新和讀取,時效性好兔毒,一旦變更漫贞,其他節(jié)點立刻可以感知。缺點:所有元數(shù)據(jù)的更新壓力全部集中在一個地方育叁,可能會導(dǎo)致元數(shù)據(jù)的存儲有壓力迅脐。
goosip: 好處:元數(shù)據(jù)的更新比較分散,有一定的延時豪嗽,降低了壓力谴蔑。缺點:更新有延時,集群的一些操作會滯后龟梦。(reshared操作時configuration error)
10000 端口
自己提供服務(wù)的端口號+ 10000 隐锭,每隔一段時間就會往另外幾個節(jié)點發(fā)送ping消息,同時其他幾點接收到ping之后返回pong
交換的信息
故障信息计贰,節(jié)點的增加和移除钦睡, hash slot 信息
gossip協(xié)議
meet:某個節(jié)點發(fā)送 meet給新加入的節(jié)點,讓新節(jié)點加入集群中躁倒,然后新節(jié)點就會開始于其他節(jié)點進行通信
ping:每個節(jié)點都會頻繁給其他節(jié)點發(fā)送ping荞怒,其中包含自己的狀態(tài)還有自己維護的集群元數(shù)據(jù),互相通過ping交換元數(shù)據(jù)
ping:返回ping和meet秧秉,包含自己的狀態(tài)和其他信息
fail:某個節(jié)點判斷另一個節(jié)點fail之后褐桌,就發(fā)送 fail 給其他節(jié)點,通知其他節(jié)點象迎,指定的節(jié)點宕機了
ping消息
ping 很頻繁荧嵌,且攜帶元數(shù)據(jù),會加重網(wǎng)絡(luò)負擔
每個節(jié)點每秒會執(zhí)行 10 次 ping,每次選擇 5 個最久沒有通信的其他節(jié)點
當如果發(fā)現(xiàn)某個節(jié)點通信延遲達到了 cluster_node_timeout /2 啦撮,那么立即發(fā)送 ping恋技, 避免數(shù)據(jù)交換延遲過長,落后時間太長(2 個節(jié)點之間 10 分鐘沒有交換數(shù)據(jù)逻族,整個集群處于嚴重的元數(shù)據(jù)不一致的情況)。
每次ping骄崩,一個是帶上自己的節(jié)點信息聘鳞,還有就是帶上1/10其他節(jié)點的信息,發(fā)送出去要拂,進行數(shù)據(jù)交換
至少包含 3 個其他節(jié)點信息抠璃,最多包含總節(jié)點-2 個其他節(jié)點的信息
JRedis原理
請求重定向
客戶端發(fā)送到任意一個redis實例發(fā)送命令,每個redis實例接受到命令后脱惰,都會計算key對應(yīng)的hash slot搏嗡,如果在本地就本地處理,否則返回moved給客戶端拉一,讓客戶端進行重定向 (redis-cli -c)
hash slot
通過tag指定key對應(yīng)的slot,同一個 tag 下的 key采盒,都會在一個 hash slot中,比如 set key1:{100} 和 set key2:{100}
smart jedis
本地維護一份hashslot->node的映射表蔚润。
JedisCluster 初始化的時候磅氨,隨機選擇一個 node,初始化 hashslot->node 映射表嫡纠,同時為每個節(jié)點創(chuàng)建一個JedisPool連接池烦租,每次基于JedisCluster執(zhí)行操作,首先JedisCluster都會在本地計算key的hashslot除盏,然后再本地映射表中找到對應(yīng)的節(jié)點叉橱,如果發(fā)現(xiàn)對應(yīng)的節(jié)點返回moved,那么利用該節(jié)點的元數(shù)據(jù)者蠕,更新 hashslot->node映射表(重試超過 5 次報錯)
hashslot遷移和ask重定向
hash slot正在遷移窃祝,那么會返回ask 重定向給jedis,jedis 接受到ask重定向之后,踱侣,會重定向到目標節(jié)點去執(zhí)行
高可用性和主備切換原理
判斷節(jié)點宕機:
如果一個節(jié)點認為另外一個節(jié)點宕機了锌杀, 就是pfail,主觀宕機
如果多個節(jié)點都認為另外一個節(jié)點宕機了,那么就是fail泻仙,客觀宕機(跟哨兵原理一樣)
在cluster-node-timeout內(nèi)糕再,某個節(jié)點一直沒有返回 pong,那么就被認為是 pfail
如果一個節(jié)點認為某個節(jié)點pfail了,那么會在gossip消息中玉转,ping給其他節(jié)點突想,如果超過半數(shù)的節(jié)點認為pfail了,那么就會變成fail。
從節(jié)點過濾:
對宕機的 mster node 猾担,從其所有的 slave node中袭灯,選擇一個切換成 master node
檢查每個 slave node與master node斷開連接的時間,如果超過了cluster-node-timeout *
cluster-slave-validity-factor绑嘹,那么就沒資格切換成 master(和哨兵一致)
從節(jié)點選舉:
每個從節(jié)點稽荧,根據(jù)自己對 master 復(fù)制數(shù)據(jù)的 offset,設(shè)置一個選舉時間工腋,offset越大(復(fù)制數(shù)據(jù)越多)的從節(jié)點姨丈,選舉時間越靠前,所有的 master node 開始投票擅腰,給要進行選舉的 slave進行投票蟋恬,如果大部分 master node(N/2 +1) 都投票給某個從節(jié)點,那么選舉通過趁冈,從節(jié)點執(zhí)行主備切換歼争,從節(jié)點切換成主節(jié)點
總結(jié):和哨兵很像,直接集成了 replication 和 sentinal
緩存雪崩
方案:
事前:保證 redis 集群高可用性 (主從+哨兵或 redis cluster)渗勘,避免全盤崩潰
事中:本地 ehcache 緩存 + hystrix 限流(保護數(shù)據(jù)庫) & 降級沐绒,避免 MySQL被打死
事后: redis持久化,快速恢復(fù)緩存數(shù)據(jù)旺坠,繼續(xù)分流高并發(fā)請求
限制組件每秒就 2000 個請求通過限流組件進入數(shù)據(jù)庫洒沦,剩余的 3000 個請求走降級,返回一些默認 的值价淌,或者友情提示
好處 :
數(shù)據(jù)庫絕對不會死申眼,確保了每秒只會過去 2000 個請求
只要數(shù)據(jù)庫不死,對于用戶來說 2/5的請求可以被處理
系統(tǒng)沒死蝉衣,用戶多點幾次可能就刷出來了
緩存穿透
4000 個請求黑客攻擊請求數(shù)據(jù)庫里沒有的數(shù)據(jù)
解決方案:把黑客查數(shù)據(jù)庫中不存在的數(shù)據(jù)的值括尸,寫到緩存中,比如: set -999 UNKNOWN
緩存與數(shù)據(jù)庫雙寫一致性
cache aside pattern
讀的時候病毡,先讀緩存濒翻,緩存沒有,就讀數(shù)據(jù)庫啦膜,然后取出數(shù)據(jù)后放入緩存有送,同時返回響應(yīng)
更新的時候,刪除緩存僧家,更新數(shù)據(jù)庫
為什么不更新緩存:
更新緩存代價太高(更新 20 次雀摘,只讀 1 次),lazy思想八拱,需要的時候再計算阵赠,不需要的時候不計算
修改數(shù)據(jù)庫成功涯塔,刪除緩存失敗,導(dǎo)致數(shù)據(jù)庫是新的數(shù)據(jù)清蚀,緩存中是舊的數(shù)據(jù)
方案:先刪除緩存匕荸,再修改數(shù)據(jù)庫
修改數(shù)據(jù)庫還沒修改完,同時又有查詢請求枷邪,把舊的數(shù)據(jù)放到緩存中(高并發(fā)榛搔,每秒并發(fā)讀幾萬,每秒只要有數(shù)據(jù)更新請求东揣,就可能出現(xiàn)數(shù)據(jù)庫+緩存不一致情況)
方案:寫践惑,讀路由到相同的一個內(nèi)存隊列(唯一標識,hash救斑,取模)里,更新和讀操作進行串行化(后臺線程異步執(zhí)行隊列串行化操作)真屯,(隊列里只放一個更新查詢操作即可脸候,多余的過濾掉,內(nèi)存隊列里沒有該數(shù)據(jù)更新操作绑蔫,直接返回 )有該數(shù)據(jù)更新操作則輪詢?nèi)【彺嬷翟寺伲瑫r取不到緩存值,直接取一次數(shù)據(jù)庫的舊值
TP 99 意思是99%的請求可以在200ms內(nèi)返回
注意點:多個商品的更新操作都積壓在一個隊列里面(太多操作積壓只能增加機器)配深,導(dǎo)致讀請求發(fā)生大量的超時携添,導(dǎo)致大量的讀請求走數(shù)據(jù)庫
一秒 500 寫操作,每200ms篓叶,100 個寫操作烈掠,20 個內(nèi)存隊列,每個隊列積壓 5 個寫操作缸托,一般在20ms完成
Redis 并發(fā)競爭問題
方案:分布式鎖 + 時間戳比較
如果感覺小編寫得不錯左敌,請素質(zhì)三連:點贊+轉(zhuǎn)發(fā)+關(guān)注。我會努力寫出更好的作品分享給大家俐镐。更多學習資料小編已打包好矫限,可以找我領(lǐng)取哦!領(lǐng)取方式:私信回復(fù)暗號【444】即可免費領(lǐng)取更多完整版資料佩抹。
Redis 集群部署架構(gòu)
10臺機器叼风,5 主 5 從,每個節(jié)點QPS 5W 棍苹,一共 25W QPS(Redis cluster 32G + 8 核 无宿,Redis 進程不超過 10G)總內(nèi)存 50g,每條數(shù)據(jù)10kb枢里,10W 條數(shù)據(jù)1g懈贺,200W 條數(shù)據(jù) 20G经窖,占用總內(nèi)存不到50%,目前高峰期 3500 QPS
作者:mousycoder