整理好了,Redis面試題
大家好,我是帥地。
驗(yàn)證自己 Redis 學(xué)得如何,最好的方式就是看一看市面上的一些面試題,帥地地這個(gè)周末把 Redis 面試題補(bǔ)全了一些,供大家復(fù)習(xí)和準(zhǔn)備面試。
不過(guò)呢,公眾號(hào)沒(méi)有目錄功能,閱讀起來(lái)不是很方便,所以呢,我在網(wǎng)站也同步了一份,網(wǎng)站有目錄的功能,方便閱讀一些。
地址:www.iamshuaidi.com/1864.html
1、談下你對(duì) Redis 的了解?
Redis(全稱:Remote Dictionary Server 遠(yuǎn)程字典服務(wù))是一個(gè)開源的使用 ANSI C 語(yǔ)言編寫、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value 數(shù)據(jù)庫(kù),并提供多種語(yǔ)言的 API。
2、Redis 一般都有哪些使用場(chǎng)景?

Redis 適合的場(chǎng)景
緩存:減輕 MySQL 的查詢壓力,提升系統(tǒng)性能;
排行榜:利用 Redis 的 SortSet(有序集合)實(shí)現(xiàn);
計(jì)算器/限速器:利用 Redis 中原子性的自增操作,我們可以統(tǒng)計(jì)類似用戶點(diǎn)贊數(shù)、用戶訪問(wèn)數(shù)等。這類操作如果用 MySQL,頻繁的讀寫會(huì)帶來(lái)相當(dāng)大的壓力;限速器比較典型的使用場(chǎng)景是限制某個(gè)用戶訪問(wèn)某個(gè) API 的頻率,常用的有搶購(gòu)時(shí),防止用戶瘋狂點(diǎn)擊帶來(lái)不必要的壓力;
好友關(guān)系:利用集合的一些命令,比如求交集、并集、差集等??梢苑奖憬鉀Q一些共同好友、共同愛(ài)好之類的功能;
消息隊(duì)列:除了 Redis 自身的發(fā)布/訂閱模式,我們也可以利用 List 來(lái)實(shí)現(xiàn)一個(gè)隊(duì)列機(jī)制,比如:到貨通知、郵件發(fā)送之類的需求,不需要高可靠,但是會(huì)帶來(lái)非常大的 DB 壓力,完全可以用 List 來(lái)完成異步解耦;
Session 共享:Session 是保存在服務(wù)器的文件中,如果是集群服務(wù),同一個(gè)用戶過(guò)來(lái)可能落在不同機(jī)器上,這就會(huì)導(dǎo)致用戶頻繁登陸;采用 Redis 保存 Session 后,無(wú)論用戶落在那臺(tái)機(jī)器上都能夠獲取到對(duì)應(yīng)的 Session 信息。
Redis 不適合的場(chǎng)景
數(shù)據(jù)量太大、數(shù)據(jù)訪問(wèn)頻率非常低的業(yè)務(wù)都不適合使用 Redis,數(shù)據(jù)太大會(huì)增加成本,訪問(wèn)頻率太低,保存在內(nèi)存中純屬浪費(fèi)資源。
3、Redis 有哪些常見(jiàn)的功能?
數(shù)據(jù)緩存功能
分布式鎖的功能
支持?jǐn)?shù)據(jù)持久化
支持事務(wù)
支持消息隊(duì)列
4、Redis 支持的數(shù)據(jù)類型有哪些?
1. string 字符串
字符串類型是 Redis 最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu),首先鍵是字符串類型,而且其他幾種結(jié)構(gòu)都是在字符串類型基礎(chǔ)上構(gòu)建的。字符串類型實(shí)際上可以是字符串:簡(jiǎn)單的字符串、XML、JSON;數(shù)字:整數(shù)、浮點(diǎn)數(shù);二進(jìn)制:圖片、音頻、視頻。
使用場(chǎng)景:緩存、計(jì)數(shù)器、共享 Session、限速。
2. Hash(哈希)
在 Redis中哈希類型是指鍵本身是一種鍵值對(duì)結(jié)構(gòu),如 value={{field1,value1},……{fieldN,valueN}}
使用場(chǎng)景:哈希結(jié)構(gòu)相對(duì)于字符串序列化緩存信息更加直觀,并且在更新操作上更加便捷。所以常常用于用戶信息等管理,但是哈希類型和關(guān)系型數(shù)據(jù)庫(kù)有所不同,哈希類型是稀疏的,而關(guān)系型數(shù)據(jù)庫(kù)是完全結(jié)構(gòu)化的,關(guān)系型數(shù)據(jù)庫(kù)可以做復(fù)雜的關(guān)系查詢,而 Redis 去模擬關(guān)系型復(fù)雜查詢開發(fā)困難且維護(hù)成本高。
3. List(列表)
列表類型是用來(lái)儲(chǔ)存多個(gè)有序的字符串,列表中的每個(gè)字符串成為元素,一個(gè)列表最多可以儲(chǔ)存 2 ^ 32 - 1 個(gè)元素,在 Redis 中,可以隊(duì)列表兩端插入和彈出,還可以獲取指定范圍的元素列表、獲取指定索引下的元素等,列表是一種比較靈活的數(shù)據(jù)結(jié)構(gòu),它可以充當(dāng)棧和隊(duì)列的角色。
使用場(chǎng)景:Redis 的 lpush + brpop 命令組合即可實(shí)現(xiàn)阻塞隊(duì)列,生產(chǎn)者客戶端是用 lpush 從列表左側(cè)插入元素,多個(gè)消費(fèi)者客戶端使用 brpop 命令阻塞式的“搶”列表尾部的元素,多個(gè)客戶端保證了消費(fèi)的負(fù)載均衡和高可用性。

4. Set(集合)
集合類型也是用來(lái)保存多個(gè)字符串的元素,但和列表不同的是集合中不允許有重復(fù)的元素,并且集合中的元素是無(wú)序的,不能通過(guò)索引下標(biāo)獲取元素,Redis 除了支持集合內(nèi)的增刪改查,同時(shí)還支持多個(gè)集合取交集、并集、差集。合理的使用好集合類型,能在實(shí)際開發(fā)中解決很多實(shí)際問(wèn)題。
使用場(chǎng)景:如:一個(gè)用戶對(duì)娛樂(lè)、體育比較感興趣,另一個(gè)可能對(duì)新聞感興趣,這些興趣就是標(biāo)簽,有了這些數(shù)據(jù)就可以得到同一標(biāo)簽的人,以及用戶的共同愛(ài)好的標(biāo)簽,這些數(shù)據(jù)對(duì)于用戶體驗(yàn)以及曾強(qiáng)用戶粘度比較重要。
5. zset(sorted set:有序集合)
有序集合和集合有著必然的聯(lián)系,它保留了集合不能有重復(fù)成員的特性,但不同得是,有序集合中的元素是可以排序的,但是它和列表的使用索引下標(biāo)作為排序依據(jù)不同的是:它給每個(gè)元素設(shè)置一個(gè)分?jǐn)?shù),作為排序的依據(jù)。
使用場(chǎng)景:排行榜是有序集合經(jīng)典的使用場(chǎng)景。例如:視頻網(wǎng)站需要對(duì)用戶上傳的文件做排行榜,榜單維護(hù)可能是多方面:按照時(shí)間、按照播放量、按照獲得的贊數(shù)等。
5、Redis 為什么這么快?
完全基于內(nèi)存,絕大部分請(qǐng)求是純粹的內(nèi)存操作,非常快速;
數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)單,對(duì)數(shù)據(jù)操作也簡(jiǎn)單;
采用單線程,避免了不必要的上下文切換和競(jìng)爭(zhēng)條件,也不存在多進(jìn)程或者多線程導(dǎo)致的切換而消耗 CPU,不用去考慮各種鎖的問(wèn)題,不存在加鎖釋放鎖操作,沒(méi)有因?yàn)榭赡艹霈F(xiàn)死鎖而導(dǎo)致的性能消耗;
使用多路 I/O 復(fù)用模型,非阻塞 IO。
6、什么是緩存穿透?怎么解決?
緩存穿透是指查詢一個(gè)一定不存在的數(shù)據(jù),由于緩存是不命中時(shí)需要從數(shù)據(jù)庫(kù)查詢,查不到數(shù)據(jù)則不寫入緩存,這將導(dǎo)致這個(gè)不存在的數(shù)據(jù)每次請(qǐng)求都要到數(shù)據(jù)庫(kù)去查詢,造成緩存穿透。
解決辦法:
1、緩存空對(duì)象:如果一個(gè)查詢返回的數(shù)據(jù)為空(不管是數(shù)據(jù)不存在,還是系統(tǒng)故障),我們?nèi)匀话堰@個(gè)空結(jié)果進(jìn)行緩存,但它的過(guò)期時(shí)間會(huì)很短,最長(zhǎng)不超過(guò)五分鐘。
緩存空對(duì)象帶來(lái)的問(wèn)題:
空值做了緩存,意味著緩存中存了更多的鍵,需要更多的內(nèi)存空間,比較有效的方法是針對(duì)這類數(shù)據(jù)設(shè)置一個(gè)較短的過(guò)期時(shí)間,讓其自動(dòng)剔除。
緩存和存儲(chǔ)的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致,可能會(huì)對(duì)業(yè)務(wù)有一定影響。例如:過(guò)期時(shí)間設(shè)置為 5分鐘,如果此時(shí)存儲(chǔ)添加了這個(gè)數(shù)據(jù),那此段時(shí)間就會(huì)出現(xiàn)緩存和存儲(chǔ)數(shù)據(jù)的不一致,此時(shí)可以利用消息系統(tǒng)或者其他方式清除掉緩存層中的空對(duì)象。
2、布隆過(guò)濾器:將所有可能存在的數(shù)據(jù)哈希到一個(gè)足夠大的 bitmap 中,一個(gè)一定不存在的數(shù)據(jù)會(huì)被這個(gè) bitmap 攔截掉,從而避免了對(duì)底層存儲(chǔ)系統(tǒng)的查詢壓力。
7、什么是緩存雪崩?該如何解決?
如果緩存集中在一段時(shí)間內(nèi)失效,發(fā)生大量的緩存穿透,所有的查詢都落在數(shù)據(jù)庫(kù)上,造成了緩存雪崩。
解決辦法:
加鎖排隊(duì):在緩存失效后,通過(guò)加鎖或者隊(duì)列來(lái)控制讀數(shù)據(jù)庫(kù)寫緩存的線程數(shù)量。比如對(duì)某個(gè) key 只允許一個(gè)線程查詢數(shù)據(jù)和寫緩存,其他線程等待;
數(shù)據(jù)預(yù)熱:可以通過(guò)緩存 reload 機(jī)制,預(yù)先去更新緩存,再即將發(fā)生大并發(fā)訪問(wèn)前手動(dòng)觸發(fā)加載緩存不同的 key,設(shè)置不同的過(guò)期時(shí)間,讓緩存失效的時(shí)間點(diǎn)盡量均勻;
做二級(jí)緩存,或者雙緩存策略:Cache1 為原始緩存,Cache2 為拷貝緩存,Cache1 失效時(shí),可以訪問(wèn) Cache2,Cache1 緩存失效時(shí)間設(shè)置為短期,Cache2 設(shè)置為長(zhǎng)期。
在緩存的時(shí)候給過(guò)期時(shí)間加上一個(gè)隨機(jī)值,這樣就會(huì)大幅度的減少緩存在同一時(shí)間過(guò)期。
8、 怎么保證緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性?
從理論上說(shuō),只要我們?cè)O(shè)置了合理的鍵的過(guò)期時(shí)間,我們就能保證緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)最終是一致的。因?yàn)橹灰彺鏀?shù)據(jù)過(guò)期了,就會(huì)被刪除。隨后讀的時(shí)候,因?yàn)榫彺胬餂](méi)有,就可以查數(shù)據(jù)庫(kù)的數(shù)據(jù),然后將數(shù)據(jù)庫(kù)查出來(lái)的數(shù)據(jù)寫入到緩存中。除了設(shè)置過(guò)期時(shí)間,我們還需要做更多的措施來(lái)盡量避免數(shù)據(jù)庫(kù)與緩存處于不一致的情況發(fā)生。
新增、更改、刪除數(shù)據(jù)庫(kù)操作時(shí)同步更新 Redis,可以使用事物機(jī)制來(lái)保證數(shù)據(jù)的一致性。
9、Redis 持久化有幾種方式?
持久化就是把內(nèi)存的數(shù)據(jù)寫到磁盤中去,防止服務(wù)宕機(jī)了內(nèi)存數(shù)據(jù)丟失。Redis 提供了兩種持久化方式:RDB(默認(rèn)) 和 AOF。
RDB
RDB 是 Redis DataBase 的縮寫。按照一定的時(shí)間周期策略把內(nèi)存的數(shù)據(jù)以快照的形式保存到硬盤的二進(jìn)制文件。即 Snapshot 快照存儲(chǔ),對(duì)應(yīng)產(chǎn)生的數(shù)據(jù)文件為 dump.rdb,通過(guò)配置文件中的 save 參數(shù)來(lái)定義快照的周期。核心函數(shù):rdbSave(生成 RDB 文件)和 rdbLoad(從文件加載內(nèi)存)兩個(gè)函數(shù)。

AOF
AOF 是 Append-only file 的縮寫。Redis會(huì)將每一個(gè)收到的寫命令都通過(guò) Write 函數(shù)追加到文件最后,類似于 MySQL 的 binlog。當(dāng) Redis 重啟是會(huì)通過(guò)重新執(zhí)行文件中保存的寫命令來(lái)在內(nèi)存中重建整個(gè)數(shù)據(jù)庫(kù)的內(nèi)容。每當(dāng)執(zhí)行服務(wù)器(定時(shí))任務(wù)或者函數(shù)時(shí),flushAppendOnlyFile 函數(shù)都會(huì)被調(diào)用, 這個(gè)函數(shù)執(zhí)行以下兩個(gè)工作:
WRITE:根據(jù)條件,將 aof_buf 中的緩存寫入到 AOF 文件;
SAVE:根據(jù)條件,調(diào)用 fsync 或 fdatasync 函數(shù),將 AOF 文件保存到磁盤中。

RDB 和 AOF 的區(qū)別:
AOF 文件比 RDB 更新頻率高,優(yōu)先使用 AOF 還原數(shù)據(jù);
AOF比 RDB 更安全也更大;
RDB 性能比 AOF 好;
如果兩個(gè)都配了優(yōu)先加載 AOF。
10、Redis 怎么實(shí)現(xiàn)分布式鎖?
Redis 為單線程模式,采用隊(duì)列模式將并發(fā)訪問(wèn)變成串行訪問(wèn),且多客戶端對(duì) Redis 的連接并不存在競(jìng)爭(zhēng)關(guān)系。Redis 中可以使用 SETNX 命令實(shí)現(xiàn)分布式鎖。一般使用 setnx(set if not exists) 指令,只允許被一個(gè)程序占有,使用完調(diào)用 del 釋放鎖。
11、Redis 內(nèi)存淘汰策略有哪些?
volatile-lru:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中挑選最近最少使用的數(shù)據(jù)淘汰;
volatile-ttl:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中挑選將要過(guò)期的數(shù)據(jù)淘汰。
volatile-random:從已設(shè)置過(guò)期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中任意選擇數(shù)據(jù)淘汰。
allkeys-lru:從數(shù)據(jù)集(server. db[i]. dict)中挑選最近最少使用的數(shù)據(jù)淘汰。
allkeys-random:從數(shù)據(jù)集(server. db[i]. dict)中任意選擇數(shù)據(jù)淘汰。
no-enviction(驅(qū)逐):禁止驅(qū)逐數(shù)據(jù)。
12、Redis 常見(jiàn)性能問(wèn)題和解決方案?
Master 最好不要做任何持久化工作,如 RDB 內(nèi)存快照和 AOF 日志文件。如果數(shù)據(jù)比較重要,某個(gè) Slave 開啟 AOF 備份數(shù)據(jù),策略設(shè)置為每秒同步一次;
為了主從復(fù)制的速度和連接的穩(wěn)定性, Master 和 Slave 最好在同一個(gè)局域網(wǎng)內(nèi);
主從復(fù)制不要用圖狀結(jié)構(gòu),用單向鏈表結(jié)構(gòu)更為穩(wěn)定,即:Master <- Slave1 <- Slave2 <- Slave3…
13、Redis的過(guò)期鍵的刪除策略
我們都知道,Redis是key-value數(shù)據(jù)庫(kù),我們可以設(shè)置Redis中緩存的key的過(guò)期時(shí)間。Redis的過(guò)期策略就是指當(dāng)Redis中緩存的key過(guò)期了,Redis如何處理。
過(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)訪問(wèn)一個(gè)key時(shí),才會(huì)判斷該key是否已過(guò)期,過(guò)期則清除。該策略可以最大化地節(jié)省CPU資源,卻對(duì)內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過(guò)期key沒(méi)有再次被訪問(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)的平衡效果。
(expires字典會(huì)保存所有設(shè)置了過(guò)期時(shí)間的key的過(guò)期時(shí)間數(shù)據(jù),其中,key是指向鍵空間中的某個(gè)鍵的指針,value是該鍵的毫秒精度的UNIX時(shí)間戳表示的過(guò)期時(shí)間。鍵空間是指該Redis集群中保存的所有鍵。)
Redis中同時(shí)使用了惰性過(guò)期和定期過(guò)期兩種過(guò)期策略。
14、我們知道通過(guò)expire來(lái)設(shè)置key 的過(guò)期時(shí)間,那么對(duì)過(guò)期的數(shù)據(jù)怎么處理呢?
除了緩存服務(wù)器自帶的緩存失效策略之外(Redis默認(rèn)的有6中策略可供選擇),我們還可以根據(jù)具體的業(yè)務(wù)需求進(jìn)行自定義的緩存淘汰,常見(jiàn)的策略有兩種:
定時(shí)去清理過(guò)期的緩存;
當(dāng)有用戶請(qǐng)求過(guò)來(lái)時(shí),再判斷這個(gè)請(qǐng)求所用到的緩存是否過(guò)期,過(guò)期的話就去底層系統(tǒng)得到新數(shù)據(jù)并更新緩存。
兩者各有優(yōu)劣,第一種的缺點(diǎn)是維護(hù)大量緩存的key是比較麻煩的,第二種的缺點(diǎn)就是每次用戶請(qǐng)求過(guò)來(lái)都要判斷緩存失效,邏輯相對(duì)比較復(fù)雜!具體用哪種方案,大家可以根據(jù)自己的應(yīng)用場(chǎng)景來(lái)權(quán)衡。
15、Hash 沖突怎么辦?
Redis 通過(guò)鏈?zhǔn)焦?/strong>解決沖突:也就是同一個(gè) 桶里面的元素使用鏈表保存。但是當(dāng)鏈表過(guò)長(zhǎng)就會(huì)導(dǎo)致查找性能變差可能,所以 Redis 為了追求快,使用了兩個(gè)全局哈希表。用于 rehash 操作,增加現(xiàn)有的哈希桶數(shù)量,減少哈希沖突。
開始默認(rèn)使用 「hash 表 1 」保存鍵值對(duì)數(shù)據(jù),「hash 表 2」 此刻沒(méi)有分配空間。當(dāng)數(shù)據(jù)越來(lái)越多觸發(fā) rehash 操作,則執(zhí)行以下操作:
給 「hash 表 2 」分配更大的空間;
將 「hash 表 1 」的數(shù)據(jù)重新映射拷貝到 「hash 表 2」 中;
釋放 「hash 表 1」 的空間。
值得注意的是,將 hash 表 1 的數(shù)據(jù)重新映射到 hash 表 2 的過(guò)程中并不是一次性的,這樣會(huì)造成 Redis 阻塞,無(wú)法提供服務(wù)。
而是采用了漸進(jìn)式 rehash,每次處理客戶端請(qǐng)求的時(shí)候,先從「 hash 表 1」 中第一個(gè)索引開始,將這個(gè)位置的 所有數(shù)據(jù)拷貝到 「hash 表 2」 中,就這樣將 rehash 分散到多次請(qǐng)求過(guò)程中,避免耗時(shí)阻塞。
16、什么是 RDB 內(nèi)存快照?
在 Redis 執(zhí)行「寫」指令過(guò)程中,內(nèi)存數(shù)據(jù)會(huì)一直變化。所謂的內(nèi)存快照,指的就是 Redis 內(nèi)存中的數(shù)據(jù)在某一刻的狀態(tài)數(shù)據(jù)。
好比時(shí)間定格在某一刻,當(dāng)我們拍照的,通過(guò)照片就能把某一刻的瞬間畫面完全記錄下來(lái)。
Redis 跟這個(gè)類似,就是把某一刻的數(shù)據(jù)以文件的形式拍下來(lái),寫到磁盤上。這個(gè)快照文件叫做 RDB 文件,RDB 就是 Redis DataBase 的縮寫。

在做數(shù)據(jù)恢復(fù)時(shí),直接將 RDB 文件讀入內(nèi)存完成恢復(fù)。
17、在生成 RDB 期間,Redis 可以同時(shí)處理寫請(qǐng)求么?
可以的,Redis 使用操作系統(tǒng)的多進(jìn)程寫時(shí)復(fù)制技術(shù) COW(Copy On Write) 來(lái)實(shí)現(xiàn)快照持久化,保證數(shù)據(jù)一致性。
Redis 在持久化時(shí)會(huì)調(diào)用 glibc 的函數(shù)fork產(chǎn)生一個(gè)子進(jìn)程,快照持久化完全交給子進(jìn)程來(lái)處理,父進(jìn)程繼續(xù)處理客戶端請(qǐng)求。
當(dāng)主線程執(zhí)行寫指令修改數(shù)據(jù)的時(shí)候,這個(gè)數(shù)據(jù)就會(huì)復(fù)制一份副本, bgsave 子進(jìn)程讀取這個(gè)副本數(shù)據(jù)寫到 RDB 文件。
這既保證了快照的完整性,也允許主線程同時(shí)對(duì)數(shù)據(jù)進(jìn)行修改,避免了對(duì)正常業(yè)務(wù)的影響。

18、如何實(shí)現(xiàn)數(shù)據(jù)盡可能少丟失又能兼顧性能呢?
重啟 Redis 時(shí),我們很少使用 rdb 來(lái)恢復(fù)內(nèi)存狀態(tài),因?yàn)闀?huì)丟失大量數(shù)據(jù)。我們通常使用 AOF 日志重放,但是重放 AOF 日志性能相對(duì) rdb 來(lái)說(shuō)要慢很多,這樣在 Redis 實(shí)例很大的情況下,啟動(dòng)需要花費(fèi)很長(zhǎng)的時(shí)間。
Redis 4.0 為了解決這個(gè)問(wèn)題,帶來(lái)了一個(gè)新的持久化選項(xiàng)——混合持久化。將 rdb 文件的內(nèi)容和增量的 AOF 日志文件存在一起。這里的 AOF 日志不再是全量的日志,而是自持久化開始到持久化結(jié)束的這段時(shí)間發(fā)生的增量 AOF 日志,通常這部分 AOF 日志很小。
于是在 Redis 重啟的時(shí)候,可以先加載 rdb 的內(nèi)容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重啟效率因此大幅得到提升。
19、你知道 哨兵集群原理么?
哨兵是 Redis 的一種運(yùn)行模式,它專注于對(duì) Redis 實(shí)例(主節(jié)點(diǎn)、從節(jié)點(diǎn))運(yùn)行狀態(tài)的監(jiān)控,并能夠在主節(jié)點(diǎn)發(fā)生故障時(shí)通過(guò)一系列的機(jī)制實(shí)現(xiàn)選主及主從切換,實(shí)現(xiàn)故障轉(zhuǎn)移,確保整個(gè) Redis 系統(tǒng)的可用性。
他的架構(gòu)圖如下:

Redis 哨兵具備的能力有如下幾個(gè):
監(jiān)控:持續(xù)監(jiān)控 master 、slave 是否處于預(yù)期工作狀態(tài)。
自動(dòng)切換主庫(kù):當(dāng) Master 運(yùn)行故障,哨兵啟動(dòng)自動(dòng)故障恢復(fù)流程:從 slave 中選擇一臺(tái)作為新 master。
通知:讓 slave 執(zhí)行 replicaof ,與新的 master 同步;并且通知客戶端與新 master 建立連接。
20、什么是 Cluster 集群?
Redis 集群是一種分布式數(shù)據(jù)庫(kù)方案,集群通過(guò)分片(sharding)來(lái)進(jìn)行數(shù)據(jù)管理(「分治思想」的一種實(shí)踐),并提供復(fù)制和故障轉(zhuǎn)移功能。
將數(shù)據(jù)劃分為 16384 的 slots,每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分槽位。槽位的信息存儲(chǔ)于每個(gè)節(jié)點(diǎn)中。
它是去中心化的,如圖所示,該集群由三個(gè) Redis 節(jié)點(diǎn)組成,每個(gè)節(jié)點(diǎn)負(fù)責(zé)整個(gè)集群的一部分?jǐn)?shù)據(jù),每個(gè)節(jié)點(diǎn)負(fù)責(zé)的數(shù)據(jù)多少可能不一樣。

三個(gè)節(jié)點(diǎn)相互連接組成一個(gè)對(duì)等的集群,它們之間通過(guò) Gossip協(xié)議相互交互集群信息,最后每個(gè)節(jié)點(diǎn)都保存著其他節(jié)點(diǎn)的 slots 分配情況。
使用 Redis Cluster 集群,主要解決了大數(shù)據(jù)量存儲(chǔ)導(dǎo)致的各種慢問(wèn)題
21、哈希槽又是如何映射到 Redis 實(shí)例上呢?
根據(jù)鍵值對(duì)的 key,使用 CRC16 算法,計(jì)算出一個(gè) 16 bit 的值;
將 16 bit 的值對(duì) 16384 執(zhí)行取模,得到 0 ~ 16383 的數(shù)表示 key 對(duì)應(yīng)的哈希槽。
根據(jù)該槽信息定位到對(duì)應(yīng)的實(shí)例。
鍵值對(duì)數(shù)據(jù)、哈希槽、Redis 實(shí)例之間的映射關(guān)系如下:

22、Cluster 如何實(shí)現(xiàn)故障轉(zhuǎn)移?
Redis 集群節(jié)點(diǎn)采用 Gossip 協(xié)議來(lái)廣播自己的狀態(tài)以及自己對(duì)整個(gè)集群認(rèn)知的改變。比如一個(gè)節(jié)點(diǎn)發(fā)現(xiàn)某個(gè)節(jié)點(diǎn)失聯(lián)了 (PFail),它會(huì)將這條信息向整個(gè)集群廣播,其它節(jié)點(diǎn)也就可以收到這點(diǎn)失聯(lián)信息。
如果一個(gè)節(jié)點(diǎn)收到了某個(gè)節(jié)點(diǎn)失聯(lián)的數(shù)量 (PFail Count) 已經(jīng)達(dá)到了集群的大多數(shù),就可以標(biāo)記該節(jié)點(diǎn)為確定下線狀態(tài) (Fail),然后向整個(gè)集群廣播,強(qiáng)迫其它節(jié)點(diǎn)也接收該節(jié)點(diǎn)已經(jīng)下線的事實(shí),并立即對(duì)該失聯(lián)節(jié)點(diǎn)進(jìn)行主從切換。
23、Redis如何做內(nèi)存優(yōu)化?
可以好好利用Hash,list,sorted set,set等集合類型數(shù)據(jù),因?yàn)橥ǔG闆r下很多小的Key-Value可以用更緊湊的方式存放到一起。盡可能使用散列表(hashes),散列表(是說(shuō)散列表里面存儲(chǔ)的數(shù)少)使用的內(nèi)存非常小,所以你應(yīng)該盡可能的將你的數(shù)據(jù)模型抽象到一個(gè)散列表里面。比如你的web系統(tǒng)中有一個(gè)用戶對(duì)象,不要為這個(gè)用戶的名稱,姓氏,郵箱,密碼設(shè)置單獨(dú)的key,而是應(yīng)該把這個(gè)用戶的所有信息存儲(chǔ)到一張散列表里面
24、Redis線程模型
Redis基于Reactor模式開發(fā)了網(wǎng)絡(luò)事件處理器,這個(gè)處理器被稱為文件事件處理器(file event handler)。它的組成結(jié)構(gòu)為4部分:多個(gè)套接字、IO多路復(fù)用程序、文件事件分派器、事件處理器。因?yàn)槲募录峙善麝?duì)列的消費(fèi)是單線程的,所以Redis才叫單線程模型。
文件事件處理器使用 I/O 多路復(fù)用(multiplexing)程序來(lái)同時(shí)監(jiān)聽(tīng)多個(gè)套接字, 并根據(jù)套接字目前執(zhí)行的任務(wù)來(lái)為套接字關(guān)聯(lián)不同的事件處理器。
當(dāng)被監(jiān)聽(tīng)的套接字準(zhǔn)備好執(zhí)行連接應(yīng)答(accept)、讀?。╮ead)、寫入(write)、關(guān)閉(close)等操作時(shí), 與操作相對(duì)應(yīng)的文件事件就會(huì)產(chǎn)生, 這時(shí)文件事件處理器就會(huì)調(diào)用套接字之前關(guān)聯(lián)好的事件處理器來(lái)處理這些事件。
雖然文件事件處理器以單線程方式運(yùn)行, 但通過(guò)使用 I/O 多路復(fù)用程序來(lái)監(jiān)聽(tīng)多個(gè)套接字, 文件事件處理器既實(shí)現(xiàn)了高性能的網(wǎng)絡(luò)通信模型, 又可以很好地與 redis 服務(wù)器中其他同樣以單線程方式運(yùn)行的模塊進(jìn)行對(duì)接, 這保持了 Redis 內(nèi)部單線程設(shè)計(jì)的簡(jiǎn)單性。
25、Redis事務(wù)及其相關(guān)面試題
什么是事務(wù)?
事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會(huì)序列化、按順序地執(zhí)行。事務(wù)在執(zhí)行的過(guò)程中,不會(huì)被其他客戶端發(fā)送來(lái)的命令請(qǐng)求所打斷。
事務(wù)是一個(gè)原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部都不執(zhí)行。
Redis事務(wù)的概念
Redis 事務(wù)的本質(zhì)是通過(guò)MULTI、EXEC、WATCH等一組命令的集合。事務(wù)支持一次執(zhí)行多個(gè)命令,一個(gè)事務(wù)中所有命令都會(huì)被序列化。在事務(wù)執(zhí)行過(guò)程,會(huì)按照順序串行化執(zhí)行隊(duì)列中的命令,其他客戶端提交的命令請(qǐng)求不會(huì)插入到事務(wù)執(zhí)行命令序列中。
總結(jié)說(shuō):redis事務(wù)就是一次性、順序性、排他性的執(zhí)行一個(gè)隊(duì)列中的一系列命令。
搜索公眾號(hào) Java面試題精選,回復(fù)“面試資料”,送你一份Java面試寶典.pdf
Redis事務(wù)的三個(gè)階段
事務(wù)開始 MULTI
命令入隊(duì)
事務(wù)執(zhí)行 EXEC
事務(wù)執(zhí)行過(guò)程中,如果服務(wù)端收到有EXEC、DISCARD、WATCH、MULTI之外的請(qǐng)求,將會(huì)把請(qǐng)求放入隊(duì)列中排
事務(wù)管理(ACID)概述
原子性(Atomicity)
原子性是指事務(wù)是一個(gè)不可分割的工作單位,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生。
一致性(Consistency)
事務(wù)前后數(shù)據(jù)的完整性必須保持一致。
隔離性(Isolation)
多個(gè)事務(wù)并發(fā)執(zhí)行時(shí),一個(gè)事務(wù)的執(zhí)行不應(yīng)影響其他事務(wù)的執(zhí)行
持久性(Durability)
持久性是指一個(gè)事務(wù)一旦被提交,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就是永久性的,接下來(lái)即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響
Redis的事務(wù)總是具有ACID中的一致性和隔離性,其他特性是不支持的。當(dāng)服務(wù)器運(yùn)行在AOF持久化模式下,并且appendfsync選項(xiàng)的值為always時(shí),事務(wù)也具有耐久性。
Redis事務(wù)支持隔離性嗎
Redis 是單進(jìn)程程序,并且它保證在執(zhí)行事務(wù)時(shí),不會(huì)對(duì)事務(wù)進(jìn)行中斷,事務(wù)可以運(yùn)行直到執(zhí)行完所有事務(wù)隊(duì)列中的命令為止。因此,Redis 的事務(wù)是總是帶有隔離性的。
Redis事務(wù)保證原子性嗎,支持回滾嗎
Redis中,單條命令是原子性執(zhí)行的,但事務(wù)不保證原子性,且沒(méi)有回滾。事務(wù)中任意命令執(zhí)行失敗,其余的命令仍會(huì)被執(zhí)行。
Redis事務(wù)其他實(shí)現(xiàn)
基于Lua腳本,Redis可以保證腳本內(nèi)的命令一次性、按順序地執(zhí)行,
其同時(shí)也不提供事務(wù)運(yùn)行錯(cuò)誤的回滾,執(zhí)行過(guò)程中如果部分命令運(yùn)行錯(cuò)誤,剩下的命令還是會(huì)繼續(xù)運(yùn)行完基于中間標(biāo)記變量,通過(guò)另外的標(biāo)記變量來(lái)標(biāo)識(shí)事務(wù)是否執(zhí)行完成,讀取數(shù)據(jù)時(shí)先讀取該標(biāo)記變量判斷是否事務(wù)執(zhí)行完成。但這樣會(huì)需要額外寫代碼實(shí)現(xiàn),比較繁瑣
26、Redis是單線程的,如何提高多核CPU的利用率?
可以在同一個(gè)服務(wù)器部署多個(gè)Redis的實(shí)例,并把他們當(dāng)作不同的服務(wù)器來(lái)使用,在某些時(shí)候,無(wú)論如何一個(gè)服務(wù)器是不夠的, 所以,如果你想使用多個(gè)CPU,你可以考慮一下分片(shard)。
27、為什么要做Redis分區(qū)?
分區(qū)可以讓Redis管理更大的內(nèi)存,Redis將可以使用所有機(jī)器的內(nèi)存。如果沒(méi)有分區(qū),你最多只能使用一臺(tái)機(jī)器的內(nèi)存。分區(qū)使Redis的計(jì)算能力通過(guò)簡(jiǎn)單地增加計(jì)算機(jī)得到成倍提升,Redis的網(wǎng)絡(luò)帶寬也會(huì)隨著計(jì)算機(jī)和網(wǎng)卡的增加而成倍增長(zhǎng)。
28、你知道有哪些Redis分區(qū)實(shí)現(xiàn)方案?
客戶端分區(qū)就是在客戶端就已經(jīng)決定數(shù)據(jù)會(huì)被存儲(chǔ)到哪個(gè)redis節(jié)點(diǎn)或者從哪個(gè)redis節(jié)點(diǎn)讀取。大多數(shù)客戶端已經(jīng)實(shí)現(xiàn)了客戶端分區(qū)。
代理分區(qū) 意味著客戶端將請(qǐng)求發(fā)送給代理,然后代理決定去哪個(gè)節(jié)點(diǎn)寫數(shù)據(jù)或者讀數(shù)據(jù)。代理根據(jù)分區(qū)規(guī)則決定請(qǐng)求哪些Redis實(shí)例,然后根據(jù)Redis的響應(yīng)結(jié)果返回給客戶端。redis和memcached的一種代理實(shí)現(xiàn)就是Twemproxy
查詢路由(Query routing) 的意思是客戶端隨機(jī)地請(qǐng)求任意一個(gè)redis實(shí)例,然后由Redis將請(qǐng)求轉(zhuǎn)發(fā)給正確的Redis節(jié)點(diǎn)。Redis Cluster實(shí)現(xiàn)了一種混合形式的查詢路由,但并不是直接將請(qǐng)求從一個(gè)redis節(jié)點(diǎn)轉(zhuǎn)發(fā)到另一個(gè)redis節(jié)點(diǎn),而是在客戶端的幫助下直接redirected到正確的redis節(jié)點(diǎn)。
29、Redis分區(qū)有什么缺點(diǎn)?
涉及多個(gè)key的操作通常不會(huì)被支持。例如你不能對(duì)兩個(gè)集合求交集,因?yàn)樗麄兛赡鼙淮鎯?chǔ)到不同的Redis實(shí)例(實(shí)際上這種情況也有辦法,但是不能直接使用交集指令)。
同時(shí)操作多個(gè)key,則不能使用Redis事務(wù).
分區(qū)使用的粒度是key,不能使用一個(gè)非常長(zhǎng)的排序key存儲(chǔ)一個(gè)數(shù)據(jù)集(The partitioning granularity is the key, so it is not possible to shard a dataset with a single huge key like a very big sorted set)
當(dāng)使用分區(qū)的時(shí)候,數(shù)據(jù)處理會(huì)非常復(fù)雜,例如為了備份你必須從不同的Redis實(shí)例和主機(jī)同時(shí)收集RDB / AOF文件。
分區(qū)時(shí)動(dòng)態(tài)擴(kuò)容或縮容可能非常復(fù)雜。Redis集群在運(yùn)行時(shí)增加或者刪除Redis節(jié)點(diǎn),能做到最大程度對(duì)用戶透明地?cái)?shù)據(jù)再平衡,但其他一些客戶端分區(qū)或者代理分區(qū)方法則不支持這種特性。然而,有一種預(yù)分片的技術(shù)也可以較好的解決這個(gè)問(wèn)題。
30、Redis實(shí)現(xiàn)分布式鎖
Redis為單進(jìn)程單線程模式,采用隊(duì)列模式將并發(fā)訪問(wèn)變成串行訪問(wèn),且多客戶端對(duì)Redis的連接并不存在競(jìng)爭(zhēng)關(guān)系Redis中可以使用SETNX命令實(shí)現(xiàn)分布式鎖。
當(dāng)且僅當(dāng) key 不存在,將 key 的值設(shè)為 value。若給定的 key 已經(jīng)存在,則 SETNX 不做任何動(dòng)作
SETNX 是『SET if Not eXists』(如果不存在,則 SET)的簡(jiǎn)寫。
返回值:設(shè)置成功,返回 1 。設(shè)置失敗,返回 0 。

使用SETNX完成同步鎖的流程及事項(xiàng)如下:
使用SETNX命令獲取鎖,若返回0(key已存在,鎖已存在)則獲取失敗,反之獲取成功
為了防止獲取鎖后程序出現(xiàn)異常,導(dǎo)致其他線程/進(jìn)程調(diào)用SETNX命令總是返回0而進(jìn)入死鎖狀態(tài),需要為該key設(shè)置一個(gè)“合理”的過(guò)期時(shí)間
釋放鎖,使用DEL命令將鎖數(shù)據(jù)刪除
31、如何解決 Redis 的并發(fā)競(jìng)爭(zhēng) Key 問(wèn)題
所謂 Redis 的并發(fā)競(jìng)爭(zhēng) Key 的問(wèn)題也就是多個(gè)系統(tǒng)同時(shí)對(duì)一個(gè) key 進(jìn)行操作,但是最后執(zhí)行的順序和我們期望的順序不同,這樣也就導(dǎo)致了結(jié)果的不同!
推薦一種方案:分布式鎖(zookeeper 和 redis 都可以實(shí)現(xiàn)分布式鎖)。(如果不存在 Redis 的并發(fā)競(jìng)爭(zhēng) Key 問(wèn)題,不要使用分布式鎖,這樣會(huì)影響性能)
基于zookeeper臨時(shí)有序節(jié)點(diǎn)可以實(shí)現(xiàn)的分布式鎖。大致思想為:每個(gè)客戶端對(duì)某個(gè)方法加鎖時(shí),在zookeeper上的與該方法對(duì)應(yīng)的指定節(jié)點(diǎn)的目錄下,生成一個(gè)唯一的瞬時(shí)有序節(jié)點(diǎn)。判斷是否獲取鎖的方式很簡(jiǎn)單,只需要判斷有序節(jié)點(diǎn)中序號(hào)最小的一個(gè)。當(dāng)釋放鎖的時(shí)候,只需將這個(gè)瞬時(shí)節(jié)點(diǎn)刪除即可。同時(shí),其可以避免服務(wù)宕機(jī)導(dǎo)致的鎖無(wú)法釋放,而產(chǎn)生的死鎖問(wèn)題。完成業(yè)務(wù)流程后,刪除對(duì)應(yīng)的子節(jié)點(diǎn)釋放鎖。
在實(shí)踐中,當(dāng)然是從以可靠性為主。所以首推Zookeeper。
參考:https://www.jianshu.com/p/8bddd381de06
32、分布式Redis是前期做還是后期規(guī)模上來(lái)了再做好?為什么?
既然Redis是如此的輕量(單實(shí)例只使用1M內(nèi)存),為防止以后的擴(kuò)容,最好的辦法就是一開始就啟動(dòng)較多實(shí)例。即便你只有一臺(tái)服務(wù)器,你也可以一開始就讓Redis以分布式的方式運(yùn)行,使用分區(qū),在同一臺(tái)服務(wù)器上啟動(dòng)多個(gè)實(shí)例。
一開始就多設(shè)置幾個(gè)Redis實(shí)例,例如32或者64個(gè)實(shí)例,對(duì)大多數(shù)用戶來(lái)說(shuō)這操作起來(lái)可能比較麻煩,但是從長(zhǎng)久來(lái)看做這點(diǎn)犧牲是值得的。
這樣的話,當(dāng)你的數(shù)據(jù)不斷增長(zhǎng),需要更多的Redis服務(wù)器時(shí),你需要做的就是僅僅將Redis實(shí)例從一臺(tái)服務(wù)遷移到另外一臺(tái)服務(wù)器而已(而不用考慮重新分區(qū)的問(wèn)題)。一旦你添加了另一臺(tái)服務(wù)器,你需要將你一半的Redis實(shí)例從第一臺(tái)機(jī)器遷移到第二臺(tái)機(jī)器。
33、什么是 RedLock
Redis 官方站提出了一種權(quán)威的基于 Redis 實(shí)現(xiàn)分布式鎖的方式名叫 Redlock,此種方式比原先的單節(jié)點(diǎn)的方法更安全。它可以保證以下特性:
安全特性:互斥訪問(wèn),即永遠(yuǎn)只有一個(gè) client 能拿到鎖
避免死鎖:最終 client 都可能拿到鎖,不會(huì)出現(xiàn)死鎖的情況,即使原本鎖住某資源的 client crash 了或者出現(xiàn)了網(wǎng)絡(luò)分區(qū)
容錯(cuò)性:只要大部分 Redis 節(jié)點(diǎn)存活就可以正常提供服務(wù)
后續(xù)還會(huì)繼續(xù)更新,盡量補(bǔ)全一些,大家也可以來(lái)我的網(wǎng)站閱讀,每次更新都會(huì)在網(wǎng)站實(shí)時(shí)更新,并且網(wǎng)站帶有目錄的功能。
Redis 面試題網(wǎng)站:www.iamshuaidi.com/1864.html
注:點(diǎn)擊閱讀原文可直達(dá),建議PC端打開
網(wǎng)站還有Java,C++,計(jì)算機(jī)基礎(chǔ),消息隊(duì)列等其他面試題哦,網(wǎng)站:www.iamshuaidi.com
