《面試八股文》之 Redis十六卷
大家好,我是 moon。
redis 作為我們最常用的內(nèi)存數(shù)據(jù)庫(kù),很多地方你都能夠發(fā)現(xiàn)它的身影,比如說(shuō)登錄信息的存儲(chǔ),分布式鎖的使用,其經(jīng)常被我們當(dāng)做緩存去使用。
可是,用了這么久的reids,你懂它嗎?
一.什么是 redis?它能做什么?

redis: redis 即 Remote Dictionary Server,用中文翻譯過(guò)來(lái)可以理解為遠(yuǎn)程數(shù)據(jù)服務(wù)或遠(yuǎn)程字典服務(wù)。其是使用 C 語(yǔ)言的編寫(xiě)的key-value存儲(chǔ)系統(tǒng)
應(yīng)用場(chǎng)景:緩存,數(shù)據(jù)庫(kù),消息隊(duì)列,分布式鎖,點(diǎn)贊列表,排行榜等等
二.redis 有哪八種數(shù)據(jù)類(lèi)型?有哪些應(yīng)用場(chǎng)景?
redis 總共有八種數(shù)據(jù)結(jié)構(gòu),五種基本數(shù)據(jù)類(lèi)型和三種特殊數(shù)據(jù)類(lèi)型。

五種基本數(shù)據(jù)類(lèi)型:
1.string:字符串類(lèi)型,常被用來(lái)存儲(chǔ)計(jì)數(shù)器,粉絲數(shù)等,簡(jiǎn)單的分布式鎖也會(huì)用到該類(lèi)型
2.hashmap:key - value 形式的,value 是一個(gè)map
3.list:基本的數(shù)據(jù)類(lèi)型,列表。在 Redis 中可以把 list 用作棧、隊(duì)列、阻塞隊(duì)列。
4.set:集合,不能有重復(fù)元素,可以做點(diǎn)贊,收藏等
5.zset:有序集合,不能有重復(fù)元素,有序集合中的每個(gè)元素都需要指定一個(gè)分?jǐn)?shù),根據(jù)分?jǐn)?shù)對(duì)元素進(jìn)行升序排序。可以做排行榜

三種特殊數(shù)據(jù)類(lèi)型:
1.geospatial: Redis 在 3.2 推出 Geo 類(lèi)型,該功能可以推算出地理位置信息,兩地之間的距離。
2.hyperloglog:基數(shù):數(shù)學(xué)上集合的元素個(gè)數(shù),是不能重復(fù)的。這個(gè)數(shù)據(jù)結(jié)構(gòu)常用于統(tǒng)計(jì)網(wǎng)站的 UV。
3.bitmap: bitmap 就是通過(guò)最小的單位 bit 來(lái)進(jìn)行0或者1的設(shè)置,表示某個(gè)元素對(duì)應(yīng)的值或者狀態(tài)。一個(gè) bit 的值,或者是0,或者是1;也就是說(shuō)一個(gè) bit 能存儲(chǔ)的最多信息是2。bitmap 常用于統(tǒng)計(jì)用戶(hù)信息比如活躍粉絲和不活躍粉絲、登錄和未登錄、是否打卡等。
三.redis為什么這么快?

官方數(shù)據(jù) redis 可以做到每秒近10w的并發(fā),這么快的原因主要總結(jié)為以下幾點(diǎn):
1:完全基于內(nèi)存操作
2:使用單線(xiàn)程模型來(lái)處理客戶(hù)端的請(qǐng)求,避免了上下文的切換
3:IO 多路復(fù)用機(jī)制
4:自身使用 C 語(yǔ)言編寫(xiě),有很多優(yōu)化機(jī)制,比如動(dòng)態(tài)字符串 sds
四.聽(tīng)說(shuō) redis 6.0之后又使用了多線(xiàn)程,不會(huì)有線(xiàn)程安全的問(wèn)題嗎?
不會(huì)
其實(shí) redis 還是使用單線(xiàn)程模型來(lái)處理客戶(hù)端的請(qǐng)求,只是使用多線(xiàn)程來(lái)處理數(shù)據(jù)的讀寫(xiě)和協(xié)議解析,執(zhí)行命令還是使用單線(xiàn)程,所以是不會(huì)有線(xiàn)程安全的問(wèn)題。
之所以加入了多線(xiàn)程因?yàn)?redis 的性能瓶頸在于網(wǎng)絡(luò)IO而非CPU,使用多線(xiàn)程能提升IO讀寫(xiě)的效率,從而整體提高redis的性能。
五.redis 的持久化機(jī)制有哪些?優(yōu)缺點(diǎn)說(shuō)說(shuō)
redis 有兩種持久化的方式,AOF 和 RDB.

AOF:
redis 每次執(zhí)行一個(gè)命令時(shí),都會(huì)把這個(gè)「命令原本的語(yǔ)句記錄到一個(gè).aod的文件當(dāng)中,然后通過(guò)fsync策略,將命令執(zhí)行后的數(shù)據(jù)持久化到磁盤(pán)中」(不包括讀命令),
AOF的優(yōu)缺點(diǎn)

AOF 的「優(yōu)點(diǎn)」:
1.AOF可以「更好的保護(hù)數(shù)據(jù)不丟失」,一般AOF會(huì)以每隔1秒,通過(guò)后臺(tái)的一個(gè)線(xiàn)程去執(zhí)行一次fsync操作,如果redis進(jìn)程掛掉,最多丟失1秒的數(shù)據(jù)
2.AOF是將命令直接追加在文件末尾的,「寫(xiě)入性能非常高」
3.AOF日志文件的命令通過(guò)非常可讀的方式進(jìn)行記錄,這個(gè)非?!高m合做災(zāi)難性的誤刪除緊急恢復(fù)」,如果某人不小心用 flushall 命令清空了所有數(shù)據(jù),只要這個(gè)時(shí)候還沒(méi)有執(zhí)行 rewrite,那么就可以將日志文件中的 flushall 刪除,進(jìn)行恢復(fù)

AOF 的「缺點(diǎn)」:
1.對(duì)于同一份數(shù)據(jù)源來(lái)說(shuō),一般情況下AOF 文件比 RDB 數(shù)據(jù)快照要大
2.由于 .aof 的每次命令都會(huì)寫(xiě)入,那么相對(duì)于 RDB 來(lái)說(shuō)「需要消耗的性能也就更多」,當(dāng)然也會(huì)有 aof 重寫(xiě)將 aof 文件優(yōu)化。
3.「數(shù)據(jù)恢復(fù)比較慢」,不適合做冷備。
RDB:
把某個(gè)時(shí)間點(diǎn) redis 內(nèi)存中的數(shù)據(jù)以二進(jìn)制的形式存儲(chǔ)的一個(gè).rdb為后綴的文件當(dāng)中,也就是「周期性的備份redis中的整個(gè)數(shù)據(jù)」,這是redis默認(rèn)的持久化方式,也就是我們說(shuō)的快照(snapshot),是采用 fork 子進(jìn)程的方式來(lái)寫(xiě)時(shí)同步的。
RDB的優(yōu)缺點(diǎn)

RDB的優(yōu)點(diǎn):
1.它是將某一時(shí)間點(diǎn)redis內(nèi)的所有數(shù)據(jù)保存下來(lái),所以當(dāng)我們做「大型的數(shù)據(jù)恢復(fù)時(shí),RDB的恢復(fù)速度會(huì)很快」
2.由于RDB的FROK子進(jìn)程這種機(jī)制,隊(duì)友給客戶(hù)端提供讀寫(xiě)服務(wù)的影響會(huì)非常小
RDB的缺點(diǎn):
舉個(gè)例子假設(shè)我們定時(shí)5分鐘備份一次,在10:00的時(shí)候 redis 備份了數(shù)據(jù),但是如果在10:04的時(shí)候服務(wù)掛了,那么我們就會(huì)丟失在10:00到10:04的整個(gè)數(shù)據(jù)
1:「有可能會(huì)產(chǎn)生長(zhǎng)時(shí)間的數(shù)據(jù)丟失」
2:可能會(huì)有長(zhǎng)時(shí)間停頓:我們前面講了,fork 子進(jìn)程這個(gè)過(guò)程是和 redis 的數(shù)據(jù)量有很大關(guān)系的,如果「數(shù)據(jù)量很大,那么很有可能會(huì)使redis暫停幾秒」
六. Redis的過(guò)期鍵的刪除策略有哪些?
過(guò)期策略通常有以下三種:
- 定時(shí)過(guò)期:每個(gè)設(shè)置過(guò)期時(shí)間的key都需要?jiǎng)?chuàng)建一個(gè)定時(shí)器,到過(guò)期時(shí)間就會(huì)立即清除。該策略可以立即清除過(guò)期的數(shù)據(jù),對(duì)內(nèi)存很友好;但是會(huì)占用大量的CPU資源去處理過(guò)期的數(shù)據(jù),從而影響緩存的響應(yīng)時(shí)間和吞吐量。
- 惰性過(guò)期:只有當(dāng)訪(fǎng)問(wèn)一個(gè)key時(shí),才會(huì)判斷該key是否已過(guò)期,過(guò)期則清除。該策略可以最大化地節(jié)省CPU資源,卻對(duì)內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過(guò)期key沒(méi)有再次被訪(fǎng)問(wèn),從而不會(huì)被清除,占用大量?jī)?nèi)存。
- 定期過(guò)期:每隔一定的時(shí)間,會(huì)掃描一定數(shù)量的數(shù)據(jù)庫(kù)的expires字典中一定數(shù)量的key,并清除其中已過(guò)期的key。該策略是前兩者的一個(gè)折中方案。通過(guò)調(diào)整定時(shí)掃描的時(shí)間間隔和每次掃描的限定耗時(shí),可以在不同情況下使得CPU和內(nèi)存資源達(dá)到最優(yōu)的平衡效果。
七. Redis的內(nèi)存滿(mǎn)了怎么辦?
實(shí)際上Redis定義了「8種內(nèi)存淘汰策略」用來(lái)處理redis內(nèi)存滿(mǎn)的情況:
1.noeviction:直接返回錯(cuò)誤,不淘汰任何已經(jīng)存在的redis鍵
2.allkeys-lru:所有的鍵使用lru算法進(jìn)行淘汰
3.volatile-lru:有過(guò)期時(shí)間的使用lru算法進(jìn)行淘汰
4.allkeys-random:隨機(jī)刪除redis鍵
5.volatile-random:隨機(jī)刪除有過(guò)期時(shí)間的redis鍵
6.volatile-ttl:刪除快過(guò)期的redis鍵
7.volatile-lfu:根據(jù)lfu算法從有過(guò)期時(shí)間的鍵刪除
8.allkeys-lfu:根據(jù)lfu算法從所有鍵刪除
八.Redis 的熱 key 問(wèn)題怎么解決?
熱 key 就是說(shuō),在某一時(shí)刻,有非常多的請(qǐng)求訪(fǎng)問(wèn)某個(gè) key,流量過(guò)大,導(dǎo)致該 redi 服務(wù)器宕機(jī)
解決方案:
- 可以將結(jié)果緩存到本地內(nèi)存中
- 將熱 key 分散到不同的服務(wù)器中
- 設(shè)置永不過(guò)期
九.緩存擊穿、緩存穿透、緩存雪崩是什么?
怎么解決呢?緩存穿透:
緩存穿透是指用戶(hù)請(qǐng)求的數(shù)據(jù)在緩存中不存在并且在數(shù)據(jù)庫(kù)中也不存在,導(dǎo)致用戶(hù)每次請(qǐng)求該數(shù)據(jù)都要去數(shù)據(jù)庫(kù)中查詢(xún)一遍,然后返回空。
解決方案:
- 布隆過(guò)濾器
- 返回空對(duì)象
緩存擊穿:
緩存擊穿,是指一個(gè) key 非常熱點(diǎn),在不停的扛著大并發(fā),大并發(fā)集中對(duì)這一個(gè)點(diǎn)進(jìn)行訪(fǎng)問(wèn),當(dāng)這個(gè) key 在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存,直接請(qǐng)求數(shù)據(jù)庫(kù),就像在一個(gè)屏障上鑿開(kāi)了一個(gè)洞。
解決方案:
- 互斥鎖
- 永不過(guò)期
緩存雪崩:
- 緩存雪崩是指緩存中不同的數(shù)據(jù)大批量到過(guò)期時(shí)間,而查詢(xún)數(shù)據(jù)量巨大,請(qǐng)求直接落到數(shù)據(jù)庫(kù)上導(dǎo)致宕機(jī)。
解決方案:
- 均勻過(guò)期
- 加互斥鎖
- 緩存永不過(guò)期
- 雙層緩存策略
十.Redis 有哪些部署方式?
- 單機(jī)模式:這也是最基本的部署方式,只需要一臺(tái)機(jī)器,負(fù)責(zé)讀寫(xiě),一般只用于開(kāi)發(fā)人員自己測(cè)試
- 哨兵模式:哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令,哨兵是一個(gè)獨(dú)立的進(jìn)程,作為進(jìn)程,它會(huì)獨(dú)立運(yùn)行。其原理是哨兵通過(guò)發(fā)送命令,等待Redis服務(wù)器響應(yīng),從而監(jiān)控運(yùn)行的多個(gè)Redis實(shí)例。它具備自動(dòng)故障轉(zhuǎn)移、集群監(jiān)控、消息通知等功能。
- cluster集群模式:在redis3.0版本中支持了cluster集群部署的方式,這種集群部署的方式能自動(dòng)將數(shù)據(jù)進(jìn)行分片,每個(gè)master上放一部分?jǐn)?shù)據(jù),提供了內(nèi)置的高可用服務(wù),即使某個(gè)master掛了,服務(wù)還可以正常地提供。
- 主從復(fù)制:在主從復(fù)制這種集群部署模式中,我們會(huì)將數(shù)據(jù)庫(kù)分為兩類(lèi),第一種稱(chēng)為主數(shù)據(jù)庫(kù)(master),另一種稱(chēng)為從數(shù)據(jù)庫(kù)(slave)。主數(shù)據(jù)庫(kù)會(huì)負(fù)責(zé)我們整個(gè)系統(tǒng)中的讀寫(xiě)操作,從數(shù)據(jù)庫(kù)會(huì)負(fù)責(zé)我們整個(gè)數(shù)據(jù)庫(kù)中的讀操作。其中在職場(chǎng)開(kāi)發(fā)中的真實(shí)情況是,我們會(huì)讓主數(shù)據(jù)庫(kù)只負(fù)責(zé)寫(xiě)操作,讓從數(shù)據(jù)庫(kù)只負(fù)責(zé)讀操作,就是為了讀寫(xiě)分離,減輕服務(wù)器的壓力。
十一.哨兵有哪些作用?
1.監(jiān)控整個(gè)主數(shù)據(jù)庫(kù)和從數(shù)據(jù)庫(kù),觀(guān)察它們是否正常運(yùn)行
2.當(dāng)主數(shù)據(jù)庫(kù)發(fā)生異常時(shí),自動(dòng)的將從數(shù)據(jù)庫(kù)升級(jí)為主數(shù)據(jù)庫(kù),繼續(xù)保證整個(gè)服務(wù)的穩(wěn)定
十二.哨兵選舉過(guò)程是怎么樣的?
1.第一個(gè)發(fā)現(xiàn)該master掛了的哨兵,向每個(gè)哨兵發(fā)送命令,讓對(duì)方選舉自己成為領(lǐng)頭哨兵
2.其他哨兵如果沒(méi)有選舉過(guò)他人,就會(huì)將這一票投給第一個(gè)發(fā)現(xiàn)該master掛了的哨兵
3.第一個(gè)發(fā)現(xiàn)該master掛了的哨兵如果發(fā)現(xiàn)由超過(guò)一半哨兵投給自己,并且其數(shù)量也超過(guò)了設(shè)定的quoram參數(shù),那么該哨兵就成了領(lǐng)頭哨兵
4.如果多個(gè)哨兵同時(shí)參與這個(gè)選舉,那么就會(huì)重復(fù)該過(guò)程,知道選出一個(gè)領(lǐng)頭哨兵
選出領(lǐng)頭哨兵后,就開(kāi)始了故障修復(fù),會(huì)從選出一個(gè)從數(shù)據(jù)庫(kù)作為新的master
十三.cluster集群模式是怎么存放數(shù)據(jù)的?
一個(gè)cluster集群中總共有16384個(gè)節(jié)點(diǎn),集群會(huì)將這16384個(gè)節(jié)點(diǎn)平均分配給每個(gè)節(jié)點(diǎn),當(dāng)然,我這里的節(jié)點(diǎn)指的是每個(gè)主節(jié)點(diǎn),就如同下圖:
十四.cluster的故障恢復(fù)是怎么做的?
判斷故障的邏輯其實(shí)與哨兵模式有點(diǎn)類(lèi)似,在集群中,每個(gè)節(jié)點(diǎn)都會(huì)定期的向其他節(jié)點(diǎn)發(fā)送ping命令,通過(guò)有沒(méi)有收到回復(fù)來(lái)判斷其他節(jié)點(diǎn)是否已經(jīng)下線(xiàn)。
如果長(zhǎng)時(shí)間沒(méi)有回復(fù),那么發(fā)起ping命令的節(jié)點(diǎn)就會(huì)認(rèn)為目標(biāo)節(jié)點(diǎn)疑似下線(xiàn),也可以和哨兵一樣稱(chēng)作主觀(guān)下線(xiàn),當(dāng)然也需要集群中一定數(shù)量的節(jié)點(diǎn)都認(rèn)為該節(jié)點(diǎn)下線(xiàn)才可以,我們來(lái)說(shuō)說(shuō)具體過(guò)程:
1.當(dāng)A節(jié)點(diǎn)發(fā)現(xiàn)目標(biāo)節(jié)點(diǎn)疑似下線(xiàn),就會(huì)向集群中的其他節(jié)點(diǎn)散播消息,其他節(jié)點(diǎn)就會(huì)向目標(biāo)節(jié)點(diǎn)發(fā)送命令,判斷目標(biāo)節(jié)點(diǎn)是否下線(xiàn)
2.如果集群中半數(shù)以上的節(jié)點(diǎn)都認(rèn)為目標(biāo)節(jié)點(diǎn)下線(xiàn),就會(huì)對(duì)目標(biāo)節(jié)點(diǎn)標(biāo)記為下線(xiàn),從而告訴其他節(jié)點(diǎn),讓目標(biāo)節(jié)點(diǎn)在整個(gè)集群中都下線(xiàn)
十五.主從同步原理是怎樣的?
1.當(dāng)一個(gè)從數(shù)據(jù)庫(kù)啟動(dòng)時(shí),它會(huì)向主數(shù)據(jù)庫(kù)發(fā)送一個(gè)SYNC命令,master收到后,在后臺(tái)保存快照,也就是我們說(shuō)的RDB持久化,當(dāng)然保存快照是需要消耗時(shí)間的,并且redis是單線(xiàn)程的,在保存快照期間redis受到的命令會(huì)緩存起來(lái)
2.快照完成后會(huì)將緩存的命令以及快照一起打包發(fā)給slave節(jié)點(diǎn),從而保證主從數(shù)據(jù)庫(kù)的一致性。
3.從數(shù)據(jù)庫(kù)接受到快照以及緩存的命令后會(huì)將這部分?jǐn)?shù)據(jù)寫(xiě)入到硬盤(pán)上的臨時(shí)文件當(dāng)中,寫(xiě)入完成后會(huì)用這份文件去替換掉RDB快照文件,當(dāng)然,這個(gè)操作是不會(huì)阻塞的,可以繼續(xù)接收命令執(zhí)行,具體原因其實(shí)就是fork了一個(gè)子進(jìn)程,用子進(jìn)程去完成了這些功能。
因?yàn)椴粫?huì)阻塞,所以,這部分初始化完成后,當(dāng)主數(shù)據(jù)庫(kù)執(zhí)行了改變數(shù)據(jù)的命令后,會(huì)異步的給slave,這也就是我們說(shuō)的復(fù)制同步階段,這個(gè)階段會(huì)貫穿在整個(gè)中從同步的過(guò)程中,直到主從同步結(jié)束后,復(fù)制同步才會(huì)終止。
十六.無(wú)硬盤(pán)復(fù)制是什么?
我們剛剛說(shuō)了主從之間是通過(guò)RDB快照來(lái)交互的,雖然看來(lái)邏輯很簡(jiǎn)單,但是還是會(huì)存在一些問(wèn)題,但是會(huì)存在著一些問(wèn)題。
1.master禁用了RDB快照時(shí),發(fā)生了主從同步(復(fù)制初始化)操作,也會(huì)生成RDB快照,但是之后如果master發(fā)成了重啟,就會(huì)用RDB快照去恢復(fù)數(shù)據(jù),這份數(shù)據(jù)可能已經(jīng)很久了,中間就會(huì)丟失數(shù)據(jù)
2.在這種一主多從的結(jié)構(gòu)中,master每次和slave同步數(shù)據(jù)都要進(jìn)行一次快照,從而在硬盤(pán)中生成RDB文件,會(huì)影響性能
為了解決這種問(wèn)題,redis在后續(xù)的更新中也加入了無(wú)硬盤(pán)復(fù)制功能,也就是說(shuō)直接通過(guò)網(wǎng)絡(luò)發(fā)送給slave,避免了和硬盤(pán)交互,但是也是有io消耗