2021年最新Redis面試題匯總(3)
1、Redis 怎么保證高可用、有哪些集群模式
主從復制、哨兵模式、集群模式。
2、主從復制
在當前最新的 Redis 6.0 中,主從復制的完整過程如下:
1)開啟主從復制
通常有以下三種方式:
- 在 slave 直接執(zhí)行命令:
slaveof
<masterip
> <masterport
> - 在 slave 配置文件中加入:
slaveof
<masterip
> <masterport
> - 使用啟動命令:--
slaveof
<masterip
> <masterport
>
注:在 Redis 5.0 之后,slaveof 相關(guān)命令和配置已經(jīng)被替換成 replicaof,例如 replicaof <masterip> <masterport>。為了兼容舊版本,通過配置的方式仍然支持 slaveof,但是通過命令的方式則不行了。
2)建立套接字(socket)連接
slave 將根據(jù)指定的 IP 地址和端口,向 master 發(fā)起套接字(socket)連接,master 在接受(accept) slave 的套接字連接之后,為該套接字創(chuàng)建相應的客戶端狀態(tài),此時連接建立完成。
3)發(fā)送PING命令
slave 向 master 發(fā)送一個 PING 命令,以檢査套接字的讀寫狀態(tài)是否正常、 master 能否正常處理命令請求。
4)身份驗證
slave 向 master 發(fā)送 AUTH password 命令來進行身份驗證。
5)發(fā)送端口信息
在身份驗證通過后后, slave 將向 master 發(fā)送自己的監(jiān)聽端口號, master 收到后記錄在 slave 所對應的客戶端狀態(tài)的 slave_listening_port 屬性中。
6)發(fā)送IP地址
如果配置了 slave_announce_ip,則 slave 向 master 發(fā)送 slave_announce_ip 配置的 IP 地址, master 收到后記錄在 slave 所對應的客戶端狀態(tài)的 slave_ip 屬性。
該配置是用于解決服務器返回內(nèi)網(wǎng) IP 時,其他服務器無法訪問的情況??梢酝ㄟ^該配置直接指定公網(wǎng) IP。
7)發(fā)送CAPA
CAPA 全稱是 capabilities,這邊表示的是同步復制的能力。slave 會在這一階段發(fā)送 capa 告訴 master 自己具備的(同步)復制能力, master 收到后記錄在 slave 所對應的客戶端狀態(tài)的 slave_capa 屬性。
8)數(shù)據(jù)同步
slave 將向 master 發(fā)送 PSYNC 命令, master 收到該命令后判斷是進行部分重同步還是完整重同步,然后根據(jù)策略進行數(shù)據(jù)的同步。
9)命令傳播
當完成了同步之后,就會進入命令傳播階段,這時 master 只要一直將自己執(zhí)行的寫命令發(fā)送給 slave ,而 slave 只要一直接收并執(zhí)行 master 發(fā)來的寫命令,就可以保證 master 和 slave 一直保持一致了。
以部分重同步為例,主從復制的核心步驟流程圖如下:
3、哨兵
哨兵(Sentinel) 是 Redis 的高可用性解決方案:由一個或多個 Sentinel 實例組成的 Sentinel 系統(tǒng)可以監(jiān)視任意多個主服務器,以及這些主服務器屬下的所有從服務器。
Sentinel 可以在被監(jiān)視的主服務器進入下線狀態(tài)時,自動將下線主服務器的某個從服務器升級為新的主服務器,然后由新的主服務器代替已下線的主服務器繼續(xù)處理命令請求。
1)哨兵故障檢測
檢查主觀下線狀態(tài)
在默認情況下,Sentinel 會以每秒一次的頻率向所有與它創(chuàng)建了命令連接的實例(包括主服務器、從服務器、其他 Sentinel 在內(nèi))發(fā)送 PING 命令,并通過實例返回的 PING 命令回復來判斷實例是否在線。
如果一個實例在 down-after-miliseconds 毫秒內(nèi),連續(xù)向 Sentinel 返回無效回復,那么 Sentinel 會修改這個實例所對應的實例結(jié)構(gòu),在結(jié)構(gòu)的 flags 屬性中設置 SRI_S_DOWN 標識,以此來表示這個實例已經(jīng)進入主觀下線狀態(tài)。
檢查客觀下線狀態(tài)
當 Sentinel 將一個主服務器判斷為主觀下線之后,為了確定這個主服務器是否真的下線了,它會向同樣監(jiān)視這一服務器的其他 Sentinel 進行詢問,看它們是否也認為主服務器已經(jīng)進入了下線狀態(tài)(可以是主觀下線或者客觀下線)。
當 Sentinel 從其他 Sentinel 那里接收到足夠數(shù)量(quorum,可配置)的已下線判斷之后,Sentinel 就會將服務器置為客觀下線,在 flags 上打上 SRI_O_DOWN 標識,并對主服務器執(zhí)行故障轉(zhuǎn)移操作。
2)哨兵故障轉(zhuǎn)移流程
當哨兵監(jiān)測到某個主節(jié)點客觀下線之后,就會開始故障轉(zhuǎn)移流程。核心流程如下:
發(fā)起一次選舉,選舉出領頭 Sentinel領頭 Sentinel 在已下線主服務器的所有從服務器里面,挑選出一個從服務器,并將其升級為新的主服務器。領頭 Sentinel 將剩余的所有從服務器改為復制新的主服務器。領頭 Sentinel 更新相關(guān)配置信息,當這個舊的主服務器重新上線時,將其設置為新的主服務器的從服務器。
4、集群模式
哨兵模式最大的缺點就是所有的數(shù)據(jù)都放在一臺服務器上,無法較好的進行水平擴展。
為了解決哨兵模式存在的問題,集群模式應運而生。在高可用上,集群基本是直接復用的哨兵模式的邏輯,并且針對水平擴展進行了優(yōu)化。
集群模式具備的特點如下:
- 采取去中心化的集群模式,將數(shù)據(jù)按槽存儲分布在多個 Redis 節(jié)點上。集群共有 16384 個槽,每個節(jié)點負責處理部分槽。
- 使用 CRC16 算法來計算 key 所屬的槽:crc16(key,keylen) & 16383。
- 所有的 Redis 節(jié)點彼此互聯(lián),通過 PING-PONG 機制來進行節(jié)點間的心跳檢測。
- 分片內(nèi)采用一主多從保證高可用,并提供復制和故障恢復功能。在實際使用中,通常會將主從分布在不同機房,避免機房出現(xiàn)故障導致整個分片出問題,下面的架構(gòu)圖就是這樣設計的。
- 客戶端與 Redis 節(jié)點直連,不需要中間代理層(proxy)??蛻舳瞬恍枰B接集群所有節(jié)點,連接集群中任何一個可用節(jié)點即可。
集群的架構(gòu)圖如下所示:
5、集群選舉
故障轉(zhuǎn)移的第一步就是選舉出新的主節(jié)點,以下是集群選舉新的主節(jié)點的方法:
1)當從節(jié)點發(fā)現(xiàn)自己正在復制的主節(jié)點進入已下線狀態(tài)時,會發(fā)起一次選舉:將 currentEpoch(配置紀元)加1,然后向集群廣播一條 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 消息,要求所有收到這條消息、并且具有投票權(quán)的主節(jié)點向這個從節(jié)點投票。
2)其他節(jié)點收到消息后,會判斷是否要給發(fā)送消息的節(jié)點投票,判斷流程如下:
- 當前節(jié)點是 slave,或者當前節(jié)點是 master,但是不負責處理槽,則當前節(jié)點沒有投票權(quán),直接返回。
- 請求節(jié)點的 currentEpoch 小于當前節(jié)點的 currentEpoch,校驗失敗返回。因為發(fā)送者的狀態(tài)與當前集群狀態(tài)不一致,可能是長時間下線的節(jié)點剛剛上線,這種情況下,直接返回即可。
- 當前節(jié)點在該 currentEpoch 已經(jīng)投過票,校驗失敗返回。
- 請求節(jié)點是 master,校驗失敗返回。
- 請求節(jié)點的 master 為空,校驗失敗返回。
- 請求節(jié)點的 master 沒有故障,并且不是手動故障轉(zhuǎn)移,校驗失敗返回。因為手動故障轉(zhuǎn)移是可以在 master 正常的情況下直接發(fā)起的。
- 上一次為該master的投票時間,在cluster_node_timeout的2倍范圍內(nèi),校驗失敗返回。這個用于使獲勝從節(jié)點有時間將其成為新主節(jié)點的消息通知給其他從節(jié)點,從而避免另一個從節(jié)點發(fā)起新一輪選舉又進行一次沒必要的故障轉(zhuǎn)移
- 請求節(jié)點宣稱要負責的槽位,是否比之前負責這些槽位的節(jié)點,具有相等或更大的 configEpoch,如果不是,校驗失敗返回。
如果通過以上所有校驗,那么主節(jié)點將向要求投票的從節(jié)點返回一條 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,表示這個主節(jié)點支持從節(jié)點成為新的主節(jié)點。
3)每個參與選舉的從節(jié)點都會接收 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,并根據(jù)自己收到了多少條這種消息來統(tǒng)計自己獲得了多少個主節(jié)點的支持。
4)如果集群里有N個具有投票權(quán)的主節(jié)點,那么當一個從節(jié)點收集到大于等于N/2+1 張支持票時,這個從節(jié)點就會當選為新的主節(jié)點。因為在每一個配置紀元里面,每個具有投票權(quán)的主節(jié)點只能投一次票,所以如果有 N個主節(jié)點進行投票,那么具有大于等于 N/2+1 張支持票的從節(jié)點只會有一個,這確保了新的主節(jié)點只會有一個。
5)如果在一個配置紀元里面沒有從節(jié)點能收集到足夠多的支持票,那么集群進入一個新的配置紀元,并再次進行選舉,直到選出新的主節(jié)點為止。
這個選舉新主節(jié)點的方法和選舉領頭 Sentinel 的方法非常相似,因為兩者都是基于 Raft 算法的領頭選舉(leader election)方法來實現(xiàn)的。
6、如何保證集群在線擴容的安全性?(Redis 集群要增加分片,槽的遷移怎么保證無損)
例如:集群已經(jīng)對外提供服務,原來有3分片,準備新增2個分片,怎么在不下線的情況下,無損的從原有的3個分片指派若干個槽給這2個分片?
Redis 使用了 ASK 錯誤來保證在線擴容的安全性。
在槽的遷移過程中若有客戶端訪問,依舊先訪問源節(jié)點,源節(jié)點會先在自己的數(shù)據(jù)庫里面査找指定的鍵,如果找到的話,就直接執(zhí)行客戶端發(fā)送的命令。
如果沒找到,說明該鍵可能已經(jīng)被遷移到目標節(jié)點了,源節(jié)點將向客戶端返回一個 ASK 錯誤,該錯誤會指引客戶端轉(zhuǎn)向正在導入槽的目標節(jié)點,并再次發(fā)送之前想要執(zhí)行的命令,從而獲取到結(jié)果。
ASK錯誤
在進行重新分片期間,源節(jié)點向目標節(jié)點遷移一個槽的過程中,可能會出現(xiàn)這樣一種情況:屬于被遷移槽的一部分鍵值對保存在源節(jié)點里面,而另一部分鍵值對則保存在目標節(jié)點里面。
當客戶端向源節(jié)點發(fā)送一個與數(shù)據(jù)庫鍵有關(guān)的命令,并且命令要處理的數(shù)據(jù)庫鍵恰好就屬于正在被遷移的槽時。源節(jié)點會先在自己的數(shù)據(jù)庫里面査找指定的鍵,如果找到的話,就直接執(zhí)行客戶端發(fā)送的命令。
否則,這個鍵有可能已經(jīng)被遷移到了目標節(jié)點,源節(jié)點將向客戶端返回一個 ASK 錯誤,指引客戶端轉(zhuǎn)向正在導入槽的目標節(jié)點,并再次發(fā)送之前想要執(zhí)行的命令,從而獲取到結(jié)果。
7、Redis 事務的實現(xiàn)
一個事務從開始到結(jié)束通常會經(jīng)歷以下3個階段:
1)事務開始:multi 命令將執(zhí)行該命令的客戶端從非事務狀態(tài)切換至事務狀態(tài),底層通過 flags 屬性標識。
2)命令入隊:當客戶端處于事務狀態(tài)時,服務器會根據(jù)客戶端發(fā)來的命令執(zhí)行不同的操作:
- exec、discard、watch、multi 命令會被立即執(zhí)行
- 其他命令不會立即執(zhí)行,而是將命令放入到一個事務隊列,然后向客戶端返回 QUEUED 回復。
3)事務執(zhí)行:當一個處于事務狀態(tài)的客戶端向服務器發(fā)送 exec 命令時,服務器會遍歷事務隊列,執(zhí)行隊列中的所有命令,最后將結(jié)果全部返回給客戶端。
不過 redis 的事務并不推薦在實際中使用,如果要使用事務,推薦使用 Lua 腳本,redis 會保證一個 Lua 腳本里的所有命令的原子性。
8、Redis 的 Java 客戶端有哪些?官方推薦哪個?
Redis 官網(wǎng)展示的 Java 客戶端如下圖所示,其中官方推薦的是標星的3個:Jedis
、Redisson
和 lettuce
。
9、Redis 里面有1億個 key,其中有 10 個 key 是包含 java,如何將它們?nèi)空页鰜恚?/h2>
1)keys *java* 命令,該命令性能很好,但是在數(shù)據(jù)量特別大的時候會有性能問題
2)scan 0 MATCH *java* 命令,基于游標的迭代器,更好的選擇
SCAN 命令是一個基于游標的迭代器(cursor based iterator): SCAN 命令每次被調(diào)用之后, 都會向用戶返回一個新的游標, 用戶在下次迭代時需要使用這個新游標作為 SCAN 命令的游標參數(shù), 以此來延續(xù)之前的迭代過程。
當 SCAN 命令的游標參數(shù)被設置為 0 時, 服務器將開始一次新的迭代, 而當服務器向用戶返回值為 0 的游標時, 表示迭代已結(jié)束。
10、使用過 Redis 做消息隊列么?
Redis 本身提供了一些組件來實現(xiàn)消息隊列的功能,但是多多少少都存在一些缺點,相比于市面上成熟的消息隊列,例如 Kafka、Rocket MQ 來說并沒有優(yōu)勢,因此目前我們并沒有使用 Redis 來做消息隊列。
關(guān)于 Redis 做消息隊列的常見方案主要有以下:
1)Redis 5.0 之前可以使用 List(blocking)、Pub/Sub 等來實現(xiàn)輕量級的消息發(fā)布訂閱功能組件,但是這兩種實現(xiàn)方式都有很明顯的缺點,兩者中相對完善的 Pub/Sub 的主要缺點就是消息無法持久化,如果出現(xiàn)網(wǎng)絡斷開、Redis 宕機等,消息就會被丟棄。
2)為了解決 Pub/Sub 模式等的缺點,Redis 在 5.0 引入了全新的 Stream,Stream 借鑒了很多 Kafka 的設計思想,有以下幾個特點:
- 提供了消息的持久化和主備復制功能,可以讓任何客戶端訪問任何時刻的數(shù)據(jù),并且能記住每一個客戶端的訪問位置,還能保證消息不丟失。
- 引入了消費者組的概念,不同組接收到的數(shù)據(jù)完全一樣(前提是條件一樣),但是組內(nèi)的消費者則是競爭關(guān)系。
Redis Stream 相比于 pub/sub 已經(jīng)有很明顯的改善,但是相比于 Kafka,其實沒有優(yōu)勢,同時存在:尚未經(jīng)過大量驗證、成本較高、不支持分區(qū)(partition)、無法支持大規(guī)模數(shù)據(jù)等問題。
11、Redis 和 Memcached 的比較
1)數(shù)據(jù)結(jié)構(gòu):memcached 支持簡單的 key-value 數(shù)據(jù)結(jié)構(gòu),而 redis 支持豐富的數(shù)據(jù)結(jié)構(gòu):String、List、Set、Hash、SortedSet 等。
2)數(shù)據(jù)存儲:memcached 和 redis 的數(shù)據(jù)都是全部在內(nèi)存中。
網(wǎng)上有一種說法 “當物理內(nèi)存用完時,Redis可以將一些很久沒用到的 value 交換到磁盤,同時在內(nèi)存中清除”,這邊指的是 redis 里的虛擬內(nèi)存(Virtual Memory)功能,該功能在 Redis 2.0 被引入,但是在 Redis 2.4 中被默認關(guān)閉,并標記為廢棄,而在后續(xù)版中被完全移除。
3)持久化:memcached 不支持持久化,redis 支持將數(shù)據(jù)持久化到磁盤
4)災難恢復:實例掛掉后,memcached 數(shù)據(jù)不可恢復,redis 可通過 RDB、AOF 恢復,但是還是會有數(shù)據(jù)丟失問題
5)事件庫:memcached 使用 Libevent 事件庫,redis 自己封裝了簡易事件庫 AeEvent
6)過期鍵刪除策略:memcached 使用惰性刪除,redis 使用惰性刪除+定期刪除
7)內(nèi)存驅(qū)逐(淘汰)策略:memcached 主要為 LRU 算法,redis 當前支持8種淘汰策略,見本文第16題
8)性能比較
- 按“CPU 單核” 維度比較:由于 Redis 只使用單核,而 Memcached 可以使用多核,所以在比較上:在處理小數(shù)據(jù)時,平均每一個核上 Redis 比 Memcached 性能更高,而在 100k 左右的大數(shù)據(jù)時, Memcached 性能要高于 Redis。
- 按“實例”維度進行比較:由于 Memcached 多線程的特性,在 Redis 6.0 之前,通常情況下 Memcached 性能是要高于 Redis 的,同時實例的 CPU 核數(shù)越多,Memcached 的性能優(yōu)勢越大。
- 至于網(wǎng)上說的 redis 的性能比 memcached 快很多,這個說法就離譜。
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
SpringBoot大學心理服務系統(tǒng)實現(xiàn)流程分步講解
本系統(tǒng)主要論述了如何使用JAVA語言開發(fā)一個大學生心理服務系統(tǒng) ,本系統(tǒng)將嚴格按照軟件開發(fā)流程進行各個階段的工作,采用B/S架構(gòu),面向?qū)ο缶幊趟枷脒M行項目開發(fā)2022-09-09一文帶你掌握Java?LinkedBlockingQueue
LinkedBlockingQueue?是一個可選有界阻塞隊列,這篇文章主要為大家詳細介紹了Java中LinkedBlockingQueue的實現(xiàn)原理與適用場景,感興趣的可以了解一下2023-04-04MyBatis SELECT基本查詢實現(xiàn)方法詳解
這篇文章主要介紹了MyBatis SELECT基本查詢實現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08