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

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

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

AOF
AOF 是 Append-only file 的縮寫。Redis會(huì)將每一個(gè)收到的寫命令都通過 Write 函數(shù)追加到文件最后,類似于 MySQL 的 binlog。當(dāng) Redis 重啟是會(huì)通過重新執(zhí)行文件中保存的寫命令來在內(nèi)存中重建整個(gè)數(shù)據(jù)庫的內(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ā)訪問變成串行訪問,且多客戶端對(duì) Redis 的連接并不存在競爭關(guān)系。Redis 中可以使用 SETNX 命令實(shí)現(xiàn)分布式鎖。一般使用 setnx(set if not exists) 指令,只允許被一個(gè)程序占有,使用完調(diào)用 del 釋放鎖。
11、Redis 內(nèi)存淘汰策略有哪些?
volatile-lru:從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中挑選最近最少使用的數(shù)據(jù)淘汰;
volatile-ttl:從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server. db[i]. expires)中挑選將要過期的數(shù)據(jù)淘汰。
volatile-random:從已設(shè)置過期時(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 常見性能問題和解決方案?
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的過期鍵的刪除策略
我們都知道,Redis是key-value數(shù)據(jù)庫,我們可以設(shè)置Redis中緩存的key的過期時(shí)間。Redis的過期策略就是指當(dāng)Redis中緩存的key過期了,Redis如何處理。
過期策略通常有以下三種:
定時(shí)過期:每個(gè)設(shè)置過期時(shí)間的key都需要?jiǎng)?chuàng)建一個(gè)定時(shí)器,到過期時(shí)間就會(huì)立即清除。該策略可以立即清除過期的數(shù)據(jù),對(duì)內(nèi)存很友好;但是會(huì)占用大量的CPU資源去處理過期的數(shù)據(jù),從而影響緩存的響應(yīng)時(shí)間和吞吐量。
惰性過期:只有當(dāng)訪問一個(gè)key時(shí),才會(huì)判斷該key是否已過期,過期則清除。該策略可以最大化地節(jié)省CPU資源,卻對(duì)內(nèi)存非常不友好。極端情況可能出現(xiàn)大量的過期key沒有再次被訪問,從而不會(huì)被清除,占用大量內(nèi)存。
定期過期:每隔一定的時(shí)間,會(huì)掃描一定數(shù)量的數(shù)據(jù)庫的expires字典中一定數(shù)量的key,并清除其中已過期的key。該策略是前兩者的一個(gè)折中方案。通過調(diào)整定時(shí)掃描的時(shí)間間隔和每次掃描的限定耗時(shí),可以在不同情況下使得CPU和內(nèi)存資源達(dá)到最優(yōu)的平衡效果。
(expires字典會(huì)保存所有設(shè)置了過期時(shí)間的key的過期時(shí)間數(shù)據(jù),其中,key是指向鍵空間中的某個(gè)鍵的指針,value是該鍵的毫秒精度的UNIX時(shí)間戳表示的過期時(shí)間。鍵空間是指該Redis集群中保存的所有鍵。)
Redis中同時(shí)使用了惰性過期和定期過期兩種過期策略。
14、我們知道通過expire來設(shè)置key 的過期時(shí)間,那么對(duì)過期的數(shù)據(jù)怎么處理呢?
除了緩存服務(wù)器自帶的緩存失效策略之外(Redis默認(rèn)的有6中策略可供選擇),我們還可以根據(jù)具體的業(yè)務(wù)需求進(jìn)行自定義的緩存淘汰,常見的策略有兩種:
定時(shí)去清理過期的緩存;
當(dāng)有用戶請(qǐng)求過來時(shí),再判斷這個(gè)請(qǐng)求所用到的緩存是否過期,過期的話就去底層系統(tǒng)得到新數(shù)據(jù)并更新緩存。
兩者各有優(yōu)劣,第一種的缺點(diǎn)是維護(hù)大量緩存的key是比較麻煩的,第二種的缺點(diǎn)就是每次用戶請(qǐng)求過來都要判斷緩存失效,邏輯相對(duì)比較復(fù)雜!具體用哪種方案,大家可以根據(jù)自己的應(yīng)用場景來權(quán)衡。
15、Hash 沖突怎么辦?
Redis 通過鏈?zhǔn)焦?/strong>解決沖突:也就是同一個(gè) 桶里面的元素使用鏈表保存。但是當(dāng)鏈表過長就會(huì)導(dǎo)致查找性能變差可能,所以 Redis 為了追求快,使用了兩個(gè)全局哈希表。用于 rehash 操作,增加現(xiàn)有的哈希桶數(shù)量,減少哈希沖突。
開始默認(rèn)使用 「hash 表 1 」保存鍵值對(duì)數(shù)據(jù),「hash 表 2」 此刻沒有分配空間。當(dāng)數(shù)據(jù)越來越多觸發(fā) rehash 操作,則執(zhí)行以下操作:
給 「hash 表 2 」分配更大的空間;
將 「hash 表 1 」的數(shù)據(jù)重新映射拷貝到 「hash 表 2」 中;
釋放 「hash 表 1」 的空間。
值得注意的是,將 hash 表 1 的數(shù)據(jù)重新映射到 hash 表 2 的過程中并不是一次性的,這樣會(huì)造成 Redis 阻塞,無法提供服務(wù)。
而是采用了漸進(jìn)式 rehash,每次處理客戶端請(qǐng)求的時(shí)候,先從「 hash 表 1」 中第一個(gè)索引開始,將這個(gè)位置的 所有數(shù)據(jù)拷貝到 「hash 表 2」 中,就這樣將 rehash 分散到多次請(qǐng)求過程中,避免耗時(shí)阻塞。
16、什么是 RDB 內(nèi)存快照?
在 Redis 執(zhí)行「寫」指令過程中,內(nèi)存數(shù)據(jù)會(huì)一直變化。所謂的內(nèi)存快照,指的就是 Redis 內(nèi)存中的數(shù)據(jù)在某一刻的狀態(tài)數(shù)據(jù)。
好比時(shí)間定格在某一刻,當(dāng)我們拍照的,通過照片就能把某一刻的瞬間畫面完全記錄下來。
Redis 跟這個(gè)類似,就是把某一刻的數(shù)據(jù)以文件的形式拍下來,寫到磁盤上。這個(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) 來實(shí)現(xiàn)快照持久化,保證數(shù)據(jù)一致性。
Redis 在持久化時(shí)會(huì)調(diào)用 glibc 的函數(shù)fork產(chǎn)生一個(gè)子進(jìn)程,快照持久化完全交給子進(jìn)程來處理,父進(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 來恢復(fù)內(nèi)存狀態(tài),因?yàn)闀?huì)丟失大量數(shù)據(jù)。我們通常使用 AOF 日志重放,但是重放 AOF 日志性能相對(duì) rdb 來說要慢很多,這樣在 Redis 實(shí)例很大的情況下,啟動(dòng)需要花費(fèi)很長的時(shí)間。
Redis 4.0 為了解決這個(gè)問題,帶來了一個(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í)通過一系列的機(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)切換主庫:當(dāng) Master 運(yùn)行故障,哨兵啟動(dòng)自動(dòng)故障恢復(fù)流程:從 slave 中選擇一臺(tái)作為新 master。
通知:讓 slave 執(zhí)行 replicaof ,與新的 master 同步;并且通知客戶端與新 master 建立連接。
20、什么是 Cluster 集群?
Redis 集群是一種分布式數(shù)據(jù)庫方案,集群通過分片(sharding)來進(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ì)等的集群,它們之間通過 Gossip協(xié)議相互交互集群信息,最后每個(gè)節(jié)點(diǎn)都保存著其他節(jié)點(diǎn)的 slots 分配情況。
使用 Redis Cluster 集群,主要解決了大數(shù)據(jù)量存儲(chǔ)導(dǎo)致的各種慢問題
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é)議來廣播自己的狀態(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),散列表(是說散列表里面存儲(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)程序來同時(shí)監(jiān)聽多個(gè)套接字, 并根據(jù)套接字目前執(zhí)行的任務(wù)來為套接字關(guān)聯(lián)不同的事件處理器。
當(dāng)被監(jiān)聽的套接字準(zhǔn)備好執(zhí)行連接應(yīng)答(accept)、讀取(read)、寫入(write)、關(guān)閉(close)等操作時(shí), 與操作相對(duì)應(yīng)的文件事件就會(huì)產(chǎn)生, 這時(shí)文件事件處理器就會(huì)調(diào)用套接字之前關(guān)聯(lián)好的事件處理器來處理這些事件。
雖然文件事件處理器以單線程方式運(yùn)行, 但通過使用 I/O 多路復(fù)用程序來監(jiān)聽多個(gè)套接字, 文件事件處理器既實(shí)現(xiàn)了高性能的網(wǎng)絡(luò)通信模型, 又可以很好地與 redis 服務(wù)器中其他同樣以單線程方式運(yùn)行的模塊進(jìn)行對(duì)接, 這保持了 Redis 內(nèi)部單線程設(shè)計(jì)的簡單性。
25、Redis事務(wù)及其相關(guān)面試題
什么是事務(wù)?
事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會(huì)序列化、按順序地執(zhí)行。事務(wù)在執(zhí)行的過程中,不會(huì)被其他客戶端發(fā)送來的命令請(qǐng)求所打斷。
事務(wù)是一個(gè)原子操作:事務(wù)中的命令要么全部被執(zhí)行,要么全部都不執(zhí)行。
Redis事務(wù)的概念
Redis 事務(wù)的本質(zhì)是通過MULTI、EXEC、WATCH等一組命令的集合。事務(wù)支持一次執(zhí)行多個(gè)命令,一個(gè)事務(wù)中所有命令都會(huì)被序列化。在事務(wù)執(zhí)行過程,會(huì)按照順序串行化執(zhí)行隊(duì)列中的命令,其他客戶端提交的命令請(qǐng)求不會(huì)插入到事務(wù)執(zhí)行命令序列中。
總結(jié)說:redis事務(wù)就是一次性、順序性、排他性的執(zhí)行一個(gè)隊(duì)列中的一系列命令。
搜索公眾號(hào) Java面試題精選,回復(fù)“面試資料”,送你一份Java面試寶典.pdf
Redis事務(wù)的三個(gè)階段
事務(wù)開始 MULTI
命令入隊(duì)
事務(wù)執(zhí)行 EXEC
事務(wù)執(zhí)行過程中,如果服務(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ù)庫中數(shù)據(jù)的改變就是永久性的,接下來即使數(shù)據(jù)庫發(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ù)不保證原子性,且沒有回滾。事務(wù)中任意命令執(zhí)行失敗,其余的命令仍會(huì)被執(zhí)行。
Redis事務(wù)其他實(shí)現(xiàn)
基于Lua腳本,Redis可以保證腳本內(nèi)的命令一次性、按順序地執(zhí)行,
其同時(shí)也不提供事務(wù)運(yùn)行錯(cuò)誤的回滾,執(zhí)行過程中如果部分命令運(yùn)行錯(cuò)誤,剩下的命令還是會(huì)繼續(xù)運(yùn)行完基于中間標(biāo)記變量,通過另外的標(biāo)記變量來標(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ù)器來使用,在某些時(shí)候,無論如何一個(gè)服務(wù)器是不夠的, 所以,如果你想使用多個(gè)CPU,你可以考慮一下分片(shard)。
27、為什么要做Redis分區(qū)?
分區(qū)可以讓Redis管理更大的內(nèi)存,Redis將可以使用所有機(jī)器的內(nèi)存。如果沒有分區(qū),你最多只能使用一臺(tái)機(jī)器的內(nèi)存。分區(qū)使Redis的計(jì)算能力通過簡單地增加計(jì)算機(jī)得到成倍提升,Redis的網(wǎng)絡(luò)帶寬也會(huì)隨著計(jì)算機(jī)和網(wǎ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è)非常長的排序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è)問題。
30、Redis實(shí)現(xiàn)分布式鎖
Redis為單進(jìn)程單線程模式,采用隊(duì)列模式將并發(fā)訪問變成串行訪問,且多客戶端對(duì)Redis的連接并不存在競爭關(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)的簡寫。
返回值:設(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è)“合理”的過期時(shí)間
釋放鎖,使用DEL命令將鎖數(shù)據(jù)刪除
31、如何解決 Redis 的并發(fā)競爭 Key 問題
所謂 Redis 的并發(fā)競爭 Key 的問題也就是多個(gè)系統(tǒng)同時(shí)對(duì)一個(gè) key 進(jìn)行操作,但是最后執(zhí)行的順序和我們期望的順序不同,這樣也就導(dǎo)致了結(jié)果的不同!
推薦一種方案:分布式鎖(zookeeper 和 redis 都可以實(shí)現(xiàn)分布式鎖)。(如果不存在 Redis 的并發(fā)競爭 Key 問題,不要使用分布式鎖,這樣會(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é)點(diǎn)中序號(hào)最小的一個(gè)。當(dāng)釋放鎖的時(shí)候,只需將這個(gè)瞬時(shí)節(jié)點(diǎn)刪除即可。同時(shí),其可以避免服務(wù)宕機(jī)導(dǎo)致的鎖無法釋放,而產(chǎn)生的死鎖問題。完成業(yè)務(wù)流程后,刪除對(duì)應(yīng)的子節(jié)點(diǎn)釋放鎖。
在實(shí)踐中,當(dāng)然是從以可靠性為主。所以首推Zookeeper。
參考:https://www.jianshu.com/p/8bddd381de06
32、分布式Redis是前期做還是后期規(guī)模上來了再做好?為什么?
既然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ù)用戶來說這操作起來可能比較麻煩,但是從長久來看做這點(diǎn)犧牲是值得的。
這樣的話,當(dāng)你的數(shù)據(jù)不斷增長,需要更多的Redis服務(wù)器時(shí),你需要做的就是僅僅將Redis實(shí)例從一臺(tái)服務(wù)遷移到另外一臺(tái)服務(wù)器而已(而不用考慮重新分區(qū)的問題)。一旦你添加了另一臺(tái)服務(wù)器,你需要將你一半的Redis實(shí)例從第一臺(tái)機(jī)器遷移到第二臺(tái)機(jī)器。
33、什么是 RedLock
Redis 官方站提出了一種權(quán)威的基于 Redis 實(shí)現(xiàn)分布式鎖的方式名叫 Redlock,此種方式比原先的單節(jié)點(diǎ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ǔ)全一些,大家也可以來我的網(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
