一、redis為什么那么快
- QPS達(dá)到10萬(wàn)/秒
- 用C語(yǔ)言實(shí)現(xiàn)
- 基于內(nèi)存
- 單線程,不用線程上下文切換及加鎖
- String,常見(jiàn)的緩存,存儲(chǔ)登錄session等
- hash,存儲(chǔ)對(duì)象,單獨(dú)修改對(duì)象屬性
- List,有序列表,可實(shí)現(xiàn)簡(jiǎn)單的消息隊(duì)列,阻塞隊(duì)列
- Set,分布式去重
- Zset,也叫做sorted set,有序集合,關(guān)聯(lián)一個(gè)double類型的分?jǐn)?shù),根據(jù)分?jǐn)?shù)排序,可實(shí)現(xiàn)排行榜、延時(shí)隊(duì)列
- Stream,redis 5.0后的新數(shù)據(jù)類型,消費(fèi)者可分組,一條消息只能被同組的一個(gè)消費(fèi)者消費(fèi),但可以被不同組的多個(gè)消費(fèi)者重復(fù)消費(fèi),借此實(shí)現(xiàn)可持久化的發(fā)布&訂閱功能
- Cache Aside Pattern
- 應(yīng)用程序同時(shí)對(duì)接緩存、數(shù)據(jù)庫(kù)
- 查詢時(shí)先查詢緩存,緩存未命中則查詢數(shù)據(jù)庫(kù),同時(shí)更新數(shù)據(jù)庫(kù)
- 更新時(shí)先更新數(shù)據(jù)庫(kù),在刪除緩存緩存
- 最常用的模式
- 會(huì)有數(shù)據(jù)不一致性問(wèn)題
- 適用讀多寫(xiě)少的場(chǎng)景
- 兩種加載緩存的方式,讀到再加載,或者啟動(dòng)時(shí)就加載
- 為什么是先更新數(shù)據(jù)庫(kù)再刪除緩存,而不是先刪除緩存再更新數(shù)據(jù)庫(kù)?
- 為什么是更新數(shù)據(jù)庫(kù)后刪除緩存,而不是更新緩存?
- Read/Write Through Pattern
- 應(yīng)用程序只對(duì)接緩存,由緩存對(duì)接數(shù)據(jù)庫(kù),相當(dāng)于Cache Provider中封裝了數(shù)據(jù)庫(kù)
- 查詢時(shí)若緩存未命中則,則Cache Provider去查詢數(shù)據(jù)庫(kù),設(shè)置到緩存后再返回
- 更新時(shí),由Cache Provider同時(shí)更新緩存和數(shù)據(jù)庫(kù),這里有事務(wù)保證
- 此模式很耗時(shí),但能保證數(shù)據(jù)一致性
- 少見(jiàn)的模式
- Write Behind
- 應(yīng)用程序只更新緩存,不直接更新數(shù)據(jù)庫(kù)
- 在一定時(shí)間觸發(fā)異步的方式寫(xiě)入數(shù)據(jù)庫(kù)
- 類似于MySQL InnoDB 緩沖池的模式
- 在寫(xiě)入數(shù)據(jù)庫(kù)前斷電掛機(jī)會(huì)有丟失數(shù)據(jù)可能
- 放大了數(shù)據(jù)不一致性,但速度很快
- 適用于高并發(fā)寫(xiě),但對(duì)數(shù)據(jù)一致性要求不高且允許丟失的場(chǎng)景
電商首頁(yè)熱點(diǎn)數(shù)據(jù)會(huì)做緩存,定時(shí)任務(wù)刷新,所有key失效時(shí)間一樣。
熱點(diǎn)key大面積集中失效,大量請(qǐng)求一下子打到數(shù)據(jù)庫(kù),導(dǎo)致數(shù)據(jù)庫(kù)掛掉。
- 失效時(shí)間加隨機(jī)值,不讓它們集中失效
- 設(shè)置熱點(diǎn)key永不過(guò)期,有更新時(shí)就更新緩存
- 如果Redis是集群部署,可讓熱點(diǎn)key分布在不同的Redis庫(kù)中
緩存和數(shù)據(jù)庫(kù)中都不存在的數(shù)據(jù),被攻擊者利用,如id = -1,發(fā)起攻擊的時(shí)候會(huì)繞過(guò)換過(guò),不斷查詢數(shù)據(jù)庫(kù)。
- 對(duì)參數(shù)合法性進(jìn)行檢驗(yàn)
- 使用布隆過(guò)濾器,會(huì)有一定誤判
- 數(shù)據(jù)庫(kù)查詢?yōu)閚ull時(shí),可以緩存約定的數(shù)據(jù),如“請(qǐng)稍后重試”,緩存時(shí)間設(shè)置短點(diǎn),如30秒(防止正常了這個(gè)id下有數(shù)據(jù)了也無(wú)法正常使用)
- 限流
一個(gè)熱點(diǎn)key,在失效的瞬間,遭遇高并發(fā),大量請(qǐng)求在緩存中查不到,會(huì)直接去查數(shù)據(jù)庫(kù)
- 設(shè)置熱點(diǎn)key永不過(guò)期
- 使用分布式互斥鎖,保證在緩存失效時(shí),只有一個(gè)請(qǐng)求能查到數(shù)據(jù)庫(kù)
數(shù)據(jù)一致性就是指數(shù)據(jù)庫(kù)和緩存的數(shù)據(jù)一致的問(wèn)題。
根據(jù)三種緩存模式可知,在數(shù)據(jù)一致性和效率是兩個(gè)極端,只能取一個(gè)中間平衡點(diǎn)。
一般是采用旁路緩存模式,且采用先更新數(shù)據(jù)庫(kù),后刪除緩存的方式。
但就這時(shí)候還是會(huì)有少量請(qǐng)求因?yàn)閯h除緩存不及時(shí)而讀到舊數(shù)據(jù),不過(guò)一般都能順利刪除緩存,這已經(jīng)是對(duì)業(yè)務(wù)影響最輕的做法。
這時(shí)候如果允許短期的數(shù)據(jù)不?致不會(huì)影響業(yè)務(wù),那么只要下次更新時(shí)可以成功,能保證最終?致性就可以,那么可以不用再做處理。
如果還要再完美,可以捕捉刪除緩存異常增加重試,對(duì)耗時(shí)敏感的可以進(jìn)行異步補(bǔ)償重試,即放到mq里面監(jiān)聽(tīng),但是這樣對(duì)業(yè)務(wù)侵入性比較大,也可以采用監(jiān)聽(tīng)MySQL binlog日志的方式進(jìn)行重試。
十、布隆過(guò)濾器
原理:
一個(gè)元素被加入到集合時(shí),通過(guò)k個(gè)哈希函數(shù)將這個(gè)元素映射成一個(gè)位數(shù)組中的k個(gè)點(diǎn),把它們?cè)O(shè)置為1。
檢索時(shí),看這些位置是不是為1就知道這個(gè)元素在不在集合中了,如果都是1則可能存在,如果有一個(gè)是0則一定不存在。
缺點(diǎn):
- 存在誤判的肯定,可通過(guò)建立白名單來(lái)存儲(chǔ)誤判的元素
- 刪除困難,初始化要把所有合法元素加到過(guò)濾器中,刪除時(shí)設(shè)置為0可能會(huì)影響其他元素的判斷,可通過(guò)Count Bloom Filter
- 使用zookeeper分布式鎖保證線程安全
- 如果也要更新數(shù)據(jù)庫(kù),涉及到雙寫(xiě),就會(huì)出現(xiàn)數(shù)據(jù)一致性問(wèn)題,可以參考上面的刪除key
- 如果不能刪除key,則在更新緩存時(shí)比較數(shù)據(jù)的更新時(shí)間
- 記錄內(nèi)存快照的方式
- 使用bgsave,fork一個(gè)子進(jìn)程進(jìn)行,不會(huì)阻塞set操作,類似于GC的守護(hù)進(jìn)行
- Copy On Write,寫(xiě)時(shí)復(fù)制機(jī)制,備份的時(shí)候發(fā)生寫(xiě)入操作,則備份的是寫(xiě)入之前的數(shù)據(jù),所以會(huì)有數(shù)據(jù)丟失
- 定期進(jìn)行,一般是5分鐘一次,斷電可能會(huì)丟失較多數(shù)據(jù)
- 恢復(fù)塊、備份久
- 可能把RDB快照文件定期放到遠(yuǎn)程存儲(chǔ),一般做冷備
- RDB備份的文件體積小,恢復(fù)很快
- 日志追加的方式,類似于MySQL innoDB引擎中redo.log,備份當(dāng)前操作命令
- 恢復(fù)慢、備份塊
- 會(huì)不會(huì)丟失數(shù)據(jù)取決于Appendfsync配置,配置為實(shí)時(shí)備份則每次寫(xiě)操作都會(huì)備份,性能低,一般是配置為每秒一次,這樣最多是丟失一秒的數(shù)據(jù)
- 適合做災(zāi)備
- 隨著時(shí)間增長(zhǎng),AOF文件會(huì)越來(lái)越大,Redis提供了日志重寫(xiě)功能,可以壓縮命令,重寫(xiě)后新的AOF文件僅包含舊AOF文件命令的最小集合
- AOF備份的文件體積大,即使經(jīng)過(guò)重寫(xiě),仍然很大,恢復(fù)很慢
Redis 4.0后使用了RDB+AOF混合持久化模式,生成RDB文件重新記錄,這時(shí)AOF日志不再是全量的,而是增量的日志記錄,體積很小。
十四、Redis過(guò)期策略
Redis需要?jiǎng)h除失效的數(shù)據(jù)以清空內(nèi)存,過(guò)期策略就是怎么刪除過(guò)期數(shù)據(jù)。
- 定期刪除:默認(rèn)每隔100ms隨機(jī)抽取部門(mén)設(shè)置了過(guò)期時(shí)間的key,檢查key是否失效,失效了就刪除。(不全部檢查是因?yàn)樾实停愃朴贛ySQL全表掃描)
- 惰性刪除:當(dāng)應(yīng)用程序來(lái)查key的時(shí)候,檢查到key失效就會(huì)刪除,未失效就返回。
Redis使用定期刪除+惰性刪除,能保證最終一定會(huì)刪除過(guò)期的key,但是定期刪除會(huì)有漏網(wǎng)之魚(yú),而應(yīng)用程序又很久沒(méi)來(lái)查詢就會(huì)導(dǎo)致長(zhǎng)時(shí)間滯留在內(nèi)存之中,這時(shí)需要用到內(nèi)存淘汰機(jī)制。
十五、Redis內(nèi)存淘汰機(jī)制
FIFO:First In First Out,先進(jìn)先出
LRU:Least Recently Used,最近最少使用,從時(shí)間上看很久沒(méi)有使用的被淘汰
LFU:Least Frequently Used,最不經(jīng)常使用,從次數(shù)上看使用得最少的被淘汰
- volatile-lru:將設(shè)定了超時(shí)時(shí)間的數(shù)據(jù),采用LRU算法將數(shù)據(jù)提前刪除
- allkeys-lru:對(duì)所有的數(shù)據(jù)采用LRU算法進(jìn)行刪除
- volatile-lfu:設(shè)定超時(shí)時(shí)間的數(shù)據(jù)采用LFU算法刪除
- allkeys-lfu:對(duì)所有數(shù)據(jù)采用LFU算法刪除
- volatile-random:設(shè)定了超時(shí)時(shí)間的數(shù)據(jù)隨機(jī)刪除
- allkeys-random:所有數(shù)據(jù)隨機(jī)刪除
- volatile-ttl:設(shè)定了超時(shí)時(shí)間的數(shù)據(jù)根據(jù)剩余時(shí)間少的刪除數(shù)據(jù)
- noeviction:不刪除內(nèi)存數(shù)據(jù),如果內(nèi)存溢出報(bào)錯(cuò)返回(默認(rèn)策略)
全量同步主要發(fā)生在Slave初始化階段,當(dāng)啟動(dòng)一臺(tái)Slave時(shí),它需要連接到Master,把Master數(shù)據(jù)都復(fù)制一份。
- Slave連接上Master,發(fā)送sync命令給到Master。
- Master執(zhí)行bgsave,按照全量備份方式生成一份RDB快照,并用內(nèi)存緩沖區(qū)記錄此后執(zhí)行的所有寫(xiě)命令。
- Master向Slave發(fā)送RDB快照。
- Slave收到RDB文件后,丟棄所有舊數(shù)據(jù),并載入收到的快照文件。
- Master發(fā)送完RDB快照就接著發(fā)緩沖區(qū)中的寫(xiě)命令。
- Slave載入完RDB快照,就開(kāi)始接收&執(zhí)行Master發(fā)送過(guò)來(lái)的寫(xiě)命令。
Master每執(zhí)行一個(gè)寫(xiě)命令就會(huì)向Slave發(fā)送相同的寫(xiě)命令,Slave接收&執(zhí)行收到的寫(xiě)命令。
十八、Redis主從復(fù)制
主從剛剛連接的時(shí)候,進(jìn)行全量同步;全同步結(jié)束后,進(jìn)行增量同步。當(dāng)然,如果有需要,slave 在任何時(shí)候都可以發(fā)起全量同步。redis策略是,無(wú)論如何,首先會(huì)嘗試進(jìn)行增量同步,如不成功,再要求從機(jī)進(jìn)行全量同步。
主從復(fù)制,只是實(shí)現(xiàn)了容災(zāi)備份,不能故障轉(zhuǎn)移,不是實(shí)現(xiàn)高可用。
十九、Redis高可用方案A:哨兵模式+主動(dòng)復(fù)制
哨兵是什么?
- 哨兵是一個(gè)獨(dú)立的進(jìn)程。
- 哨兵的作用主要有兩個(gè),A:通過(guò)心跳機(jī)制監(jiān)控Redis服務(wù)器運(yùn)行狀態(tài),包括Master和Slave。B:當(dāng)哨兵監(jiān)測(cè)到master宕機(jī),會(huì)自動(dòng)將slave切換成master,然后通過(guò)發(fā)布訂閱模式通知其他的哨兵、slave,修改配置文件,讓它們切換主機(jī)。
- 當(dāng)一個(gè)哨兵監(jiān)測(cè)到Master宕機(jī),系統(tǒng)并不會(huì)馬上進(jìn)行故障切換,僅僅是哨兵1主觀的認(rèn)為主服務(wù)器不可用,這個(gè)現(xiàn)象成為“主觀下線”。
- 當(dāng)后面的哨兵也檢測(cè)到主服務(wù)器不可用,并且數(shù)量達(dá)到一定值時(shí),那么哨兵之間就會(huì)進(jìn)行一次投票,投票的結(jié)果由一個(gè)哨兵發(fā)起,進(jìn)行故障切換。
- 切換成功后,就會(huì)通過(guò)發(fā)布訂閱模式,讓各個(gè)哨兵把自己監(jiān)控的從服務(wù)器實(shí)現(xiàn)切換主機(jī),這個(gè)過(guò)程稱為“客觀下線”。
- 優(yōu)點(diǎn):實(shí)現(xiàn)了容災(zāi)備份和自動(dòng)故障切換,是高可用方案。
- 缺點(diǎn):不好在線擴(kuò)容(Slave可以隨時(shí)配置多個(gè),提高讀并發(fā),但Master只有一個(gè),提高不了寫(xiě)并發(fā)),配置麻煩,只有一個(gè)主節(jié)點(diǎn)對(duì)外提供服務(wù),沒(méi)法支持很高的并發(fā)量。
Redis集群是一個(gè)由多個(gè)主從節(jié)點(diǎn)群組組成的分布式服務(wù)集群,他具有復(fù)制、高可用、分片特性,Redis集群不需要sentinel哨兵,也能完成節(jié)點(diǎn)移除和故障轉(zhuǎn)移的功能,需要將每個(gè)節(jié)點(diǎn)設(shè)置成集群模式,這種集群模式?jīng)]有中心節(jié)點(diǎn),可水平擴(kuò)展;Redis集群的性能和高可用均優(yōu)于之前版本的哨兵模式,且集群配置非常簡(jiǎn)單。
故障切換過(guò)程是怎么樣的?
- Redis的所有節(jié)點(diǎn)都會(huì)保存當(dāng)前redis集群中的全部主從狀態(tài)信息,并且每個(gè)節(jié)點(diǎn)都能夠相互通信。
- 當(dāng)一個(gè)節(jié)點(diǎn)發(fā)生宕機(jī),則集群中的其他節(jié)點(diǎn)通過(guò)心跳機(jī)制檢查Redis節(jié)點(diǎn)是否宕機(jī)。
- 當(dāng)有半數(shù)以上的節(jié)點(diǎn)認(rèn)為宕機(jī),則認(rèn)為主節(jié)點(diǎn)宕機(jī),同時(shí)由Redis剩余的主節(jié)點(diǎn)進(jìn)入選舉機(jī)制,投票選舉鏈接宕機(jī)的主節(jié)點(diǎn)的從機(jī),實(shí)現(xiàn)故障遷移。
- 集群中如果主機(jī)宕機(jī),那么從機(jī)可以繼續(xù)提供服務(wù),當(dāng)主機(jī)中沒(méi)有從機(jī)時(shí),則向其它主機(jī)借用多余的從機(jī),繼續(xù)提供服務(wù),如果主機(jī)宕機(jī)時(shí)沒(méi)有從機(jī)可用,則集群崩潰。即:每個(gè)節(jié)點(diǎn)都至少保持是“一主一從”。
數(shù)據(jù)存儲(chǔ)原理是什么?
- hash槽存儲(chǔ)原理,所有的鍵根據(jù)哈希函數(shù)(CRC16[key]&16383)映射到0-16384槽內(nèi)。
- 當(dāng)向redis集群中插入數(shù)據(jù)時(shí),首先將key進(jìn)行計(jì)算.之后將計(jì)算結(jié)果匹配到具體的某一個(gè)槽的區(qū)間內(nèi),之后再將數(shù)據(jù)set到管理該槽的節(jié)點(diǎn)中。
二十一、keys命令
- keys命令可以列出所有符合給定模式 pattern的key
- 單因?yàn)閞edis是單線程的,使用keys命令會(huì)導(dǎo)致線程阻塞一段時(shí)間,線上服務(wù)停頓,知道指令執(zhí)行完畢,服務(wù)才能恢復(fù),如列出10億個(gè)相同前綴的key時(shí),影響特別大。
- 可以使用scan指令代替,但會(huì)有一定重復(fù),通過(guò)代碼去重就好。
- 使用String類型緩存用戶登錄狀態(tài)
- 使用Hash類型緩存一張配置表、字典表
- 使用setnx+expire+Lua實(shí)現(xiàn)分布式鎖
- 使用List類型實(shí)現(xiàn)高性能的分頁(yè)(如文章的評(píng)論列表)、簡(jiǎn)單的消息隊(duì)列功能
- 使用Set類型實(shí)現(xiàn)分布式全局去重
- 使用Zset類型實(shí)現(xiàn)熱榜、排行榜、延時(shí)隊(duì)列功能
- 使用pub/sub實(shí)現(xiàn)簡(jiǎn)單的發(fā)布&訂閱功能(不可持久化)
- 使用Stream類型實(shí)現(xiàn)有消費(fèi)組的發(fā)布&訂閱功能(可持久化)
- 使用Bitmap(位圖)實(shí)現(xiàn)簽到、布隆過(guò)濾器功能
- 使用HyperLogLog實(shí)現(xiàn)的不精確的去重統(tǒng)計(jì),如PV(頁(yè)面訪問(wèn))、UV(用戶訪問(wèn))
- 使用Geospatial保存地理位置,計(jì)算位置距離,實(shí)現(xiàn)附近的人功能
- 使用Pipeline(管道)把一批命令打包好發(fā)送到redis一次性執(zhí)行,減少客戶端與 redis 的通信次數(shù)來(lái)實(shí)現(xiàn)降低往返延時(shí)時(shí)間
- 使用Lua腳本保證原子性,實(shí)現(xiàn)秒殺場(chǎng)景扣除商品庫(kù)存
- 使用Set類型實(shí)現(xiàn)標(biāo)簽系統(tǒng)