面試官問Redis總是很迷惑?看看這6個Redis面試題剖析!

1、在項目中緩存是如何使用的?為什麼要用緩存?緩存使用不當會造成什麼後果?

(1)面試官心理分析

這個問題,互聯網公司必問,要是一個人連緩存都不太清楚,那確實比較尷尬。

只要問到緩存,上來第一個問題,肯定是先問問你項目哪裡用了緩存?為啥要用?不用行不行?如果用了以後可能會有什麼不良的後果?

這就是看看你對緩存這個東西背後有沒有思考,如果你就是傻乎乎的瞎用,沒法給面試官一個合理的解答,那面試官對你印象肯定不太好,覺得你平時思考太少,就知道幹活兒。

(1)面試題剖析

項目中緩存是如何使用的?

這個,需要結合自己項目的業務來。

為什麼要用緩存?

用緩存,主要有兩個用途:高性能、高併發。

高性能

假設這麼個場景,你有個操作,一個請求過來,吭哧吭哧你各種亂七八糟操作 mysql,半天查出來一個結果,耗時 600ms。但是這個結果可能接下來幾個小時都不會變了,或者變了也可以不用立即反饋給用戶。那麼此時咋辦?

緩存啊,折騰 600ms 查出來的結果,扔緩存裡,一個 key 對應一個 value,下次再有人查,別走 mysql折騰 600ms 了,直接從緩存裡,通過一個 key 查出來一個 value,2ms 搞定。性能提升 300 倍。

就是說對於一些需要複雜操作耗時查出來的結果,且確定後面不怎麼變化,但是有很多讀請求,那麼直接將查詢出來的結果放在緩存中,後面直接讀緩存就好。

高併發

所以要是你有個系統,高峰期一秒鐘過來的請求有 1 萬,那一個 mysql 單機絕對會死掉。你這個時候就只能上緩存,把很多數據放緩存,別放 mysql。緩存功能簡單,說白了就是 key-value 式操作,單機支撐的併發量輕鬆一秒幾萬十幾萬,支撐高併發 so easy。單機承載併發量是 mysql 單機的幾十倍。

緩存是走內存的,內存天然就支撐高併發。

用了緩存之後會有什麼不良後果?

常見的緩存問題有以下幾個:

緩存與數據庫雙寫不一致 、緩存雪崩、緩存穿透、緩存併發競爭後面再詳細說明。

面試官問Redis總是很迷惑?看看這6個Redis面試題剖析!

2、redis 和 memcached 有什麼區別?redis 的線程模型是什麼?為什麼 redis 單線程卻能支撐高併發?

(1)面試官心理分析

這個是問 redis 的時候,最基本的問題吧,redis 最基本的一個內部原理和特點,就是 redis 實際上是個單線程工作模型,你要是這個都不知道,那後面玩兒 redis 的時候,出了問題豈不是什麼都不知道?

還有可能面試官會問問你 redis 和 memcached 的區別,但是 memcached 是早些年各大互聯網公司常用的緩存方案,但是現在近幾年基本都是 redis,沒什麼公司用 memcached 了。

(2)面試題剖析

redis 和 memcached 有啥區別?

redis 支持複雜的數據結構

redis 相比 memcached 來說,擁有更多的數據結構,能支持更豐富的數據操作。如果需要緩存能夠支持更復雜的結構和操作, redis 會是不錯的選擇。

redis 原生支持集群模式

在 redis3.x 版本中,便能支持 cluster 模式,而 memcached 沒有原生的集群模式,需要依靠客戶端來

實現往集群中分片寫入數據。

性能對比

由於 redis 只使用單核,而 memcached 可以使用多核,所以平均每一個核上 redis 在存儲小數據時比memcached 性能更高。而在 100k 以上的數據中,memcached 性能要高於 redis。雖然 redis 最近也在存儲大數據的性能上進行優化,但是比起 memcached,還是稍有遜色。

redis 的線程模型

redis 內部使用文件事件處理器 file event handler,這個文件事件處理器是單線程的,所以 redis 才

叫做單線程的模型。它採用 IO 多路複用機制同時監聽多個 socket,將產生事件的 socket 壓入內存隊列中,事件分派器根據 socket 上的事件類型來選擇對應的事件處理器進行處理。

文件事件處理器的結構包含 4 個部分:

  • 多個 socket
  • IO 多路複用程序
  • 文件事件分派器
  • 事件處理器(連接應答處理器、命令請求處理器、命令回覆處理器)

多個 socket 可能會併發產生不同的操作,每個操作對應不同的文件事件,但是 IO 多路複用程序會監聽多個 socket,會將產生事件的 socket 放入隊列中排隊,事件分派器每次從隊列中取出一個 socket,根據 socket 的事件類型交給對應的事件處理器進行處理。

來看客戶端與 redis 的一次通信過程:

面試官問Redis總是很迷惑?看看這6個Redis面試題剖析!

要明白,通信是通過 socket 來完成的,不懂的同學可以先去看一看 socket 網絡編程。

客戶端 socket01 向 redis 進程的 server socket 請求建立連接,此時 server socket 會產生一個AE_READABLE 事件,IO 多路複用程序監聽到 server socket 產生的事件後,將該 socket 壓入隊列中。

文件事件分派器從隊列中獲取 socket,交給連接應答處理器。連接應答處理器會創建一個能與客戶端通信的 socket01,並將該 socket01 的 AE_READABLE 事件與命令請求處理器關聯。

假設此時客戶端發送了一個 set key value 請求,此時 redis 中的 socket01 會產生 AE_READABLE 事件,IO 多路複用程序將 socket01 壓入隊列,此時事件分派器從隊列中獲取到 socket01 產生的AE_READABLE 事件,由於前面 socket01 的 AE_READABLE 事件已經與命令請求處理器關聯,因此事件分派器將事件交給命令請求處理器來處理。命令請求處理器讀取 socket01 的 key value 並在自己內存中完成 key value 的設置。操作完成後,它會將 socket01 的 AE_WRITABLE 事件與命令回覆處理器關聯。

如果此時客戶端準備好接收返回結果了,那麼 redis 中的 socket01 會產生一個 AE_WRITABLE 事件,同樣壓入隊列中,事件分派器找到相關聯的命令回覆處理器,由命令回覆處理器對 socket01 輸入本次操作的一個結果,比如 ok,之後解除 socket01 的 AE_WRITABLE 事件與命令回覆處理器的關聯。

這樣便完成了一次通信。

為啥 redis 單線程模型也能效率這麼高?

  • 純內存操作
  • 核心是基於非阻塞的 IO 多路複用機制
  • 單線程反而避免了多線程的頻繁上下文切換問題

3、redis 都有哪些數據類型?分別在哪些場景下使用比較合適?

(1)面試官心理分析

除非是面試官感覺看你簡歷,是工作 3 年以內的比較初級的同學,可能對技術沒有很深入的研究,面試官才會問這類問題。否則,在寶貴的面試時間裡,面試官實在不想多問。

其實問這個問題,主要有兩個原因:

  • 看看你到底有沒有全面的瞭解 redis 有哪些功能,一般怎麼來用,啥場景用什麼,就怕你別就會最簡單的 KV 操作;
  • 看看你在實際項目裡都怎麼玩兒過 redis。

要是你回答的不好,沒說出幾種數據類型,也沒說什麼場景,你完了,面試官對你印象肯定不好,覺得你平時就是做個簡單的 set 和 get。

(2)面試題剖析

redis 主要有以下幾種數據類型:

  • string
  • hash
  • list
  • set
  • Zset(sorted set)

string

這是最簡單的類型,就是普通的set和get,做簡單的KV緩存。

hash

這個是類似 map 的一種結構,這個一般就是可以將結構化的數據,比如一個對象(前提是這個對象沒嵌套其他的對象)給緩存在 redis 裡,然後每次讀寫緩存的時候,可以就操作 hash 裡的某個字段。

<code>hset person name bingo
hset person age 20
hset person id 1 hget person name
person = {
 "name": "bingo",
 "age": 20,
 "id": 1
}/<code>

list

list 是有序列表,這個可以玩兒出很多花樣。

比如可以通過 list 存儲一些列表型的數據結構,類似粉絲列表、文章的評論列表之類的東西。

比如可以通過 lrange 命令,讀取某個閉區間內的元素,可以基於 list 實現分頁查詢,這個是很棒的一個功能,基於 redis 實現簡單的高性能分頁,可以做類似微博那種下拉不斷分頁的東西,性能高,就一頁一頁走。

# 0 開始位置,-1 結束位置,結束位置為-1 時,表示列表的最後一個位置,即查看所有。 lrange mylist 0 -1

比如可以搞個簡單的消息隊列,從 list 頭懟進去,從 list 尾巴那裡弄出來。

<code>lpush mylist 1
lpush mylist 2
lpush mylist 3 4 5
# 1
rpop mylist/<code>

set

set 是無序集合,自動去重。

直接基於 set 將系統裡需要去重的數據扔進去,自動就給去重了,如果你需要對一些數據進行快速的全局去重,你當然也可以基於 jvm 內存裡的 HashSet 進行去重,但是如果你的某個系統部署在多臺機器上呢?

得基於 redis 進行全局的 set 去重。

把兩個大 V 的粉絲都放在兩個 set 中,對兩個 set 做交集。

<code>#-------操作一個 set-------
# 添加元素 sadd mySet 1
# 查看全部元素 smembers mySet
# 判斷是否包含某個值 sismember mySet 3
# 刪除某個/些元素 srem mySet 1 srem mySet 2 4
# 查看元素個數 scard mySet
# 隨機刪除一個元素 spop mySet
#-------操作多個 set-------
# 將一個 set 的元素移動到另外一個 set
smove yourSet mySet 2
# 求兩 set 的交集 sinter yourSet mySet
# 求兩 set 的並集 sunion yourSet mySet
# 求在 yourSet 中而不在 mySet 中的元素 sdiff yourSet mySet/<code>

sorted set

sorted set 是排序的 set,去重但可以排序,寫進去的時候給一個分數,自動根據分數排序。

<code>zadd board 85 zhangsan
zadd board 72 lisi zadd board 96 wangwu
zadd board 63 zhaoliu
# 獲取排名前三的用戶(默認是升序,所以需要 rev 改為降序) zrevrange board 0 3
# 獲取某用戶的排名 zrank board zhaoliu/<code>
面試官問Redis總是很迷惑?看看這6個Redis面試題剖析!

4、redis 的過期策略都有哪些?內存淘汰機制都有哪些?手寫一下LRU代碼實現?

面試官心理分析

如果你連這個問題都不知道,上來就懵了,回答不出來,那線上你寫代碼的時候,想當然的認為寫進 redis的數據就一定會存在,後面導致系統各種 bug,誰來負責?

常見的有兩個問題:

(1)往 redis 寫入的數據怎麼沒了?

可能有同學會遇到,在生產環境的 redis 經常會丟掉一些數據,寫進去了,過一會兒可能就沒了。我的天,同學,你問這個問題就說明 redis 你就沒用對啊。redis 是緩存,你給當存儲了是吧?

啥叫緩存?用內存當緩存。內存是無限的嗎,內存是很寶貴而且是有限的,磁盤是廉價而且是大量的。可能一臺機器就幾十個 G 的內存,但是可以有幾個 T 的硬盤空間。redis 主要是基於內存來進行高性能、高併發的讀寫操作的。

那既然內存是有限的,比如 redis 就只能用 10G,你要是往裡面寫了 20G 的數據,會咋辦?當然會幹掉10G 的數據,然後就保留 10G 的數據了。那幹掉哪些數據?保留哪些數據?當然是幹掉不常用的數據,保留常用的數據了。

(2)數據明明過期了,怎麼還佔用著內存?

這是由 redis 的過期策略來決定。

面試題剖析

redis 過期策略

redis 過期策略是:定期刪除+惰性刪除。

所謂定期刪除,指的是 redis 默認是每隔 100ms 就隨機抽取一些設置了過期時間的 key,檢查其是否過期,如果過期就刪除。

假設 redis 裡放了 10w 個 key,都設置了過期時間,你每隔幾百毫秒,就檢查 10w 個 key,那 redis 基本上就死了,cpu 負載會很高的,消耗在你的檢查過期 key 上了。注意,這裡可不是每隔 100ms 就遍歷所有的設置過期時間的 key,那樣就是一場性能上的災難。實際上 redis 是每隔 100ms 隨機抽取一些key 來檢查和刪除的。

但是問題是,定期刪除可能會導致很多過期 key 到了時間並沒有被刪除掉,那咋整呢?所以就是惰性刪除了。這就是說,在你獲取某個 key 的時候,redis 會檢查一下 ,這個 key 如果設置了過期時間那麼是否過期了?如果過期了此時就會刪除,不會給你返回任何東西。

獲取 key 的時候,如果此時 key 已經過期,就刪除,不會返回任何東西。

答案是:走內存淘汰機制。

內存淘汰機制

redis 內存淘汰機制有以下幾個:

  • noeviction: 當內存不足以容納新寫入數據時,新寫入操作會報錯,這個一般沒人用吧,實在是太噁心了。
  • allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的 key(這個是最常用的)。
  • allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個 key,這個一般沒人用吧,為啥要隨機,肯定是把最近最少使用的 key 給幹掉啊。
  • volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的 key(這個一般不太合適)。
  • volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個 key。
  • volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的 key 優先移除。

手寫一個 LRU 算法

你可以現場手寫最原始的 LRU 算法,那個代碼量太大了,似乎不太現實。

不求自己純手工從底層開始打造出自己的 LRU,但是起碼要知道如何利用已有的 JDK 數據結構實現一個Java 版的 LRU。

<code>class LRUCache extends LinkedHashMap {
	private final int CACHE_SIZE;
	/**
* 傳遞進來最多能緩存多少數據 *
* @param cacheSize 緩存大小 */
	public LRUCache(int cacheSize) {
		// true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的 放在尾部。
		super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
		CACHE_SIZE = cacheSize;
	}
	@Override
	protected Boolean removeEldestEntry(Map.Entry eldest) {
		// 當 map 中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據。
		return size() > CACHE_SIZE;
	}
}/<code>

5、如何保證 redis 的高併發和高可用?redis 的主從複製原理能介紹一下麼?redis 的哨兵原理能介紹一下麼?

其實問這個問題,主要是考考你,redis 單機能承載多高併發?如果單機扛不住如何擴容扛更多的併發?redis 會不會掛?既然 redis 會掛那怎麼保證 redis 是高可用的?

其實針對的都是項目中你肯定要考慮的一些問題,如果你沒考慮過,那確實你對生產系統中的問題思考太少。

面試題剖析

如果你用 redis 緩存技術的話,肯定要考慮如何用 redis 來加多臺機器,保證 redis 是高併發的,還有就是如何讓 redis 保證自己不是掛掉以後就直接死掉了,即 redis 高可用。

由於此節內容較多,因此,會分為兩個小節進行講解。 - redis 主從架構 - redis 基於哨兵實現高可用redis 實現高併發主要依靠主從架構,一主多從,一般來說,很多項目其實就足夠了,單主用來寫入數據,單機幾萬 QPS,多從用來查詢數據,多個從實例可以提供每秒 10w 的 QPS。

如果想要在實現高併發的同時,容納大量的數據,那麼就需要 redis 集群,使用 redis 集群之後,可以提供每秒幾十萬的讀寫併發。

redis 高可用,如果是做主從架構部署,那麼加上哨兵就可以了,就可以實現,任何一個實例宕機,可以進行主備切換。

6、redis 的持久化有哪幾種方式?不同的持久化機制都有什麼 優缺點?持久化機制具體底層是如何實現的?

面試官心理分析

redis 如果僅僅只是將數據緩存在內存裡面,如果 redis 宕機了再重啟,內存裡的數據就全部都弄丟了啊。

你必須得用 redis 的持久化機制,將數據寫入內存的同時,異步的慢慢的將數據寫入磁盤文件裡,進行持久化。

如果 redis 宕機重啟,自動從磁盤上加載之前持久化的一些數據就可以了,也許會丟失少許數據,但是至少不會將所有數據都弄丟。

這個其實一樣,針對的都是 redis 的生產環境可能遇到的一些問題,就是 redis 要是掛了再重啟,內存裡的數據不就全丟了?能不能重啟的時候把數據給恢復了?

面試題剖析

持久化主要是做災難恢復、數據恢復,也可以歸類到高可用的一個環節中去,比如你 redis 整個掛了,然後 redis 就不可用了,你要做的事情就是讓 redis 變得可用,儘快變得可用。

重啟 redis,儘快讓它對外提供服務,如果沒做數據備份,這時候 redis 啟動了,也不可用啊,數據都沒了。

很可能說,大量的請求過來,緩存全部無法命中,在 redis 里根本找不到數據,這個時候就死定了,出現緩存雪崩問題。所有請求沒有在redis命中,就會去mysql數據庫這種數據源頭中去找,一下子mysql承接高併發,然後就掛了…

如果你把 redis 持久化做好,備份和恢復方案做到企業級的程度,那麼即使你的 redis 故障了,也可以通過備份數據,快速恢復,一旦恢復立即對外提供服務。

redis 持久化的兩種方式

  • RDB:RDB 持久化機制,是對 redis 中的數據執行週期性的持久化。
  • AOF:AOF 機制對每條寫入命令作為日誌,以 append-only 的模式寫入一個日誌文件中,在 redis重啟的時候,可以通過回放 AOF 日誌中的寫入指令來重新構建整個數據集。

通過 RDB 或 AOF,都可以將 redis 內存中的數據給持久化到磁盤上面來,然後可以將這些數據備份到別的地方去,比如說阿里雲等雲服務。

如果 redis 掛了,服務器上的內存和磁盤上的數據都丟了,可以從雲服務上拷貝回來之前的數據,放到指定的目錄中,然後重新啟動 redis,redis 就會自動根據持久化數據文件中的數據,去恢復內存中的數據,繼續對外提供服務。

如果同時使用 RDB 和 AOF 兩種持久化機制,那麼在 redis 重啟的時候,會使用 AOF 來重新構建數據,因為 AOF 中的數據更加完整。

RDB 優缺點

  • RDB 會生成多個數據文件,每個數據文件都代表了某一個時刻中 redis 的數據,這種多個數據文件的方式,非常適合做冷備,可以將這種完整的數據文件發送到一些遠程的安全存儲上去,比如說 Amazon的 S3 雲服務上去,在國內可以是阿里雲的 ODPS 分佈式存儲上,以預定好的備份策略來定期備份 redis中的數據。
  • RDB 對 redis 對外提供的讀寫服務,影響非常小,可以讓 redis 保持高性能,因為 redis 主進程只需要 fork 一個子進程,讓子進程執行磁盤 IO 操作來進行 RDB 持久化即可。 ·
  • 相對於 AOF 持久化機制來說,直接基於 RDB 數據文件來重啟和恢復 redis 進程,更加快速。
  • 如果想要在 redis 故障時,儘可能少的丟失數據,那麼 RDB 沒有 AOF 好。一般來說,RDB 數據快照文件,都是每隔 5 分鐘,或者更長時間生成一次,這個時候就得接受一旦 redis 進程宕機,那麼會丟失最近 5 分鐘的數據。
  • RDB 每次在 fork 子進程來執行 RDB 快照數據文件生成的時候,如果數據文件特別大,可能會導致對客戶端提供的服務暫停數毫秒,或者甚至數秒。

AOF 優缺點

  • AOF 可以更好的保護數據不丟失,一般 AOF 會每隔 1 秒,通過一個後臺線程執行一次 fsync 操作,最多丟失 1 秒鐘的數據。
  • AOF 日誌文件以 append-only 模式寫入,所以沒有任何磁盤尋址的開銷,寫入性能非常高,而且文件不容易破損,即使文件尾部破損,也很容易修復。
  • AOF 日誌文件即使過大的時候,出現後臺重寫操作,也不會影響客戶端的讀寫。因為在 rewrite log的時候,會對其中的指令進行壓縮,創建出一份需要恢復數據的最小日誌出來。在創建新日誌文件的時候,老的日誌文件還是照常寫入。當新的merge後日志文件ready的時候,在交換新老日誌文件即可。
  • AOF 日誌文件的命令通過非常可讀的方式進行記錄,這個特性非常適合做災難性的誤刪除的緊急恢復。比如某人不小心用 flushall 命令清空了所有數據,只要這個時候後臺 rewrite 還沒有發生,那麼就可以立即拷貝 AOF 文件,將最後一條 flushall 命令給刪了,然後再將該 AOF 文件放回去,就可以通過恢復機制,自動恢復所有數據。
  • 對於同一份數據來說,AOF 日誌文件通常比 RDB 數據快照文件更大。
  • AOF 開啟後,支持的寫 QPS 會比 RDB 支持的寫 QPS 低,因為 AOF 一般會配置成每秒 fsync 一次日誌文件,當然,每秒一次 fsync,性能也還是很高的。(如果實時寫入,那麼 QPS 會大降,redis 性 能會大大降低)
  • 以前 AOF 發生過 bug,就是通過 AOF 記錄的日誌,進行數據恢復的時候,沒有恢復一模一樣的數據出來。所以說,類似 AOF 這種較為複雜的基於命令日誌 / merge / 回放的方式,比基於 RDB 每次持久化一份完整的數據快照文件的方式,更加脆弱一些,容易有 bug。不過 AOF 就是為了避免 rewrite 過程導致的 bug,因此每次 rewrite 並不是基於舊的指令日誌進行 merge 的,而是基於當時內存中的數據進行指令的重新構建,這樣健壯性會好很多。

RDB 和 AOF 到底該如何選擇

  • 不要僅僅使用 RDB,因為那樣會導致你丟失很多數據;
  • 也不要僅僅使用 AOF,因為那樣有兩個問題:第一,你通過 AOF 做冷備,沒有 RDB 做冷備來的恢復速度更快;第二,RDB 每次簡單粗暴生成數據快照,更加健壯,可以避免 AOF 這種複雜的備份和恢復機制的 bug;

私信回覆 資料 領取一線大廠Java面試題總結+各知識點學習思維導+一份300頁pdf文檔的Java核心知識點總結!

這些資料的內容都是面試時面試官必問的知識點,篇章包括了很多知識點,其中包括了有基礎知識、Java集合、JVM、多線程併發、spring原理、微服務、Netty 與RPC 、Kafka、日記、設計模式、Java算法、數據庫、Zookeeper、分佈式緩存、數據結構等等。

最後

歡迎大家一起交流,喜歡文章記得關注我點贊轉發喲,感謝支持!


分享到:


相關文章: