緩存面試準備

一、基本說明

說到緩存,可能大家首先想到的就是redis了,這也是目前業內使用的最廣泛、最普遍的分布式緩存架構了。關于這一塊的問題,互聯網公司必問,要是你連緩存都不太清楚,那確實比較尷尬。你說你不會消息隊列,或者你說你沒接觸過搜索引擎還情有可原,但是你要是說你不懂redis,基本上就拜拜了~~

二、緩存的應用

1、面試題

在項目中緩存是如何使用的?緩存如果使用不當會造成什么后果?

2、面試官心里分析

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

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

3、面試題剖析

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

這個,你結合你自己項目的業務來,你如果用了那恭喜你,你如果沒用那不好意思,你硬加也得加一個場景吧。

為啥在項目里要用緩存呢?

用緩存,主要是倆用途,高性能和高并發。

高性能

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

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

就是把你一些復雜操作耗時查出來的結果,如果確定后面不咋變了,然后但是馬上還有很多讀請求,那么直接結果放緩存,后面直接讀緩存就好了。

高并發

mysql這么重的數據庫,壓根兒設計不是讓你玩兒高并發的,雖然也可以玩兒,但是天然支持不好。mysql單機支撐到2000qps也開始容易報警了。

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


結合這倆場景考慮一下,你為啥要用緩存?

可能你的項目里沒啥高并發場景,那就別折騰了,直接用高性能那個場景吧,就思考有沒有可以緩存結果的復雜查詢場景,后續可以大幅度提升性能,優化用戶體驗,有,就說這個理由,沒有?那你也得編一個出來吧,不然你不是在搞笑么。

用了緩存之后會有啥不良的后果?

你要是沒考慮過這個問題,那你就尷尬了,面試官會覺得你頭腦簡單,四肢也不發達。你別光是傻用一個東西,多考慮考慮背后的一些事兒。常見的緩存問題有仨(當然其實有很多,我這里就說仨)

1)緩存與數據庫雙寫不一致

2)緩存雪崩

3)緩存穿透

這仨問題是常見面試題,后面會講,但是人要是問你,你至少自己能說出來,并且給出對應的解決方案。

三、redis的線程模型

1、面試題

redis和memcached有什么區別?redis的線程模型是什么?為什么單線程的redis比多線程的memcached效率要高得多(為什么redis是單線程的但是還可以支撐高并發)?

2、面試官心里分析

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

3、面試題剖析

1.redis和memcached有啥區別

這個事兒吧,你可以比較出N多個區別來,但是主要也就是下面這幾個區別:

Redis支持服務器端的數據操作:Redis相比Memcached來說,擁有更多的數據結構和并支持更豐富的數據操作,通常在Memcached里,你需要將數據拿到客戶端來進行類似的修改再set回去。這大大增加了網絡IO的次數和數據體積。在Redis中,這些復雜的操作通常和一般的GET/SET一樣高效。所以,如果需要緩存能夠支持更復雜的結構和操作,那么Redis會是不錯的選擇。

內存使用效率對比:使用簡單的key-value存儲的話,Memcached的內存利用率更高,而如果Redis采用hash結構來做key-value存儲,由于其組合式的壓縮,其內存利用率會高于Memcached。

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

集群模式:memcached沒有原生的集群模式,需要依靠客戶端來實現往集群中分片寫入數據;但是redis目前是原生支持cluster模式的,redis官方就是支持redis cluster集群模式的,比memcached來說要更好。

2.redis的線程模型

文件事件處理器

redis基于reactor模式開發了網絡事件處理器,這個處理器叫做文件事件處理器,file event handler。這個文件事件處理器,是單線程的,redis才叫做單線程的模型,采用IO多路復用機制同時監聽多個socket,根據socket上的事件來選擇對應的事件處理器來處理這個事件。

如果被監聽的socket準備好執行accept、read、write、close等操作的時候,跟操作對應的文件事件就會產生,這個時候文件事件處理器就會調用之前關聯好的事件處理器來處理這個事件。

文件事件處理器是單線程模式運行的,但是通過IO多路復用機制監聽多個socket,可以實現高性能的網絡通信模型,又可以跟內部其他單線程的模塊進行對接,保證了redis內部的線程模型的簡單性。

文件事件處理器的結構包含4個部分:多個socket,IO多路復用程序,文件事件分派器,事件處理器(命令請求處理器、命令回復處理器、連接應答處理器,等等)。

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

然后一個socket的事件處理完之后,IO多路復用程序才會將隊列中的下一個socket給事件分派器。文件事件分派器會根據每個socket當前產生的事件,來選擇對應的事件處理器來處理。

文件事件

當socket變得可讀時(比如客戶端對redis執行write操作,或者close操作),或者有新的可以應答的sccket出現時(客戶端對redis執行connect操作),socket就會產生一個AE_READABLE事件。

當socket變得可寫的時候(客戶端對redis執行read操作),socket會產生一個AE_WRITABLE事件。

IO多路復用程序可以同時監聽AE_REABLE和AE_WRITABLE兩種事件,要是一個socket同時產生了AE_READABLE和AE_WRITABLE兩種事件,那么文件事件分派器優先處理AE_REABLE事件,然后才是AE_WRITABLE事件。

如果是客戶端要連接redis,那么會為socket關聯連接應答處理器;如果是客戶端要寫數據到redis,那么會為socket關聯命令請求處理器;如果是客戶端要從redis讀數據,那么會為socket關聯命令回復處理器。

客戶端與redis通信的一次流程

在redis啟動初始化的時候,redis會將連接應答處理器跟AE_READABLE事件關聯起來,接著如果一個客戶端跟redis發起連接,此時會產生一個AE_READABLE事件,然后由連接應答處理器來處理跟客戶端建立連接,創建客戶端對應的socket,同時將這個socket的AE_READABLE事件跟命令請求處理器關聯起來。

當客戶端向redis發起請求的時候(不管是讀請求還是寫請求,都一樣),首先就會在socket產生一個AE_READABLE事件,然后由對應的命令請求處理器來處理。這個命令請求處理器就會從socket中讀取請求相關數據,然后進行執行和處理。

接著redis這邊準備好了給客戶端的響應數據之后,就會將socket的AE_WRITABLE事件跟命令回復處理器關聯起來,當客戶端這邊準備好讀取響應數據時,就會在socket上產生一個AE_WRITABLE事件,會由對應的命令回復處理器來處理,就是將準備好的響應數據寫入socket,供客戶端來讀取。

命令回復處理器寫完之后,就會刪除這個socket的AE_WRITABLE事件和命令回復處理器的關聯關系。

3.為啥redis單線程模型也能效率這么高

純內存操作

核心是基于非阻塞的IO多路復用機制

單線程反而避免了多線程的頻繁上下文切換問題(百度)

四、redis的數據類型

1、面試題

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

2、面試官心里分析

問這個問題,除非是看你簡歷,感覺是個新手,對技術沒有很深入的研究過,才會問這類問題。其實問這個問題呢,主要就倆原因:

第一,看看你到底有沒有全面的了解redis有哪些功能,一般怎么來用,啥場景用什么,就怕你別就會最簡單的kv操作

第二,看看你在實際項目里都怎么玩兒過redis

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

3、面試題剖析

1.string

這是最基本的類型了,沒啥可說的,就是普通的set和get,做簡單的kv緩存。

2.hash

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

hash類的數據結構,主要是用來存放一些對象,把一些簡單的對象給緩存起來,后續操作的時候,你可以直接僅僅修改這個對象中的某個字段的值。

value={

? “id”: 150,

? “name”: “zhangsan”,

? “age”: 21

}

3.list

有序列表,這個是可以玩兒出很多花樣的。微博,某個大v的粉絲,就可以以list的格式放在redis里去緩存。

key=某大v

value=[zhangsan, lisi, wangwu]

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

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

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

4.set

無序集合,自動去重。

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

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

可以基于set玩兒交集、并集、差集的操作,比如交集吧,可以把兩個人的粉絲列表整一個交集,看看倆人的共同好友是誰?對吧。把兩個大v的粉絲都放在兩個set中,對兩個set做交集。

5.sorted set

排序的set,去重但是可以排序,寫進去的時候給一個分數,自動根據分數排序,這個可以玩兒很多的花樣,最大的特點是有個分數可以自定義排序規則。

比如說你要是想根據時間對數據排序,那么可以寫入進去的時候用某個時間作為分數,人家自動給你按照時間排序了。

排行榜:將每個用戶以及其對應的什么分數寫入進去,zadd board score username,接著zrevrange board 0 99,就可以獲取排名前100的用戶;zrank board username,可以看到用戶在排行榜里的排名。

zadd board 85 zhangsan

zadd board 72 wangwu

zadd board 96 lisi

zadd board 62 zhaoliu

比如要獲取排名前3的用戶

zrevrange board 0 3

96 lisi

85 zhangsan

72 wangwu

再比如說要獲取“zhaoliu”的排名

zrank board zhaoliu

4

五、redis的過期策略

1、面試題

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

2、面試官心里分析

1)我往redis里寫的數據怎么沒了?

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

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

所以說,這是緩存的一個最基本的概念,數據是會過期的,要么是你自己設置個過期時間,要么是redis自己給干掉。

2)我的數據明明都過期了,怎么還占用著內存?。?/p>

還有一種就是如果你設置好了一個過期時間,你知道redis是怎么給你弄成過期的嗎?什么時候刪除掉?為啥好多數據明明應該過期了,結果發現redis內存占用還是很高?那是因為你不知道redis是怎么刪除那些過期key的。

redis 內存一共是10g,你現在往里面寫了5g的數據,結果這些數據明明你都設置了過期時間,要求這些數據1小時之后都會過期,結果1小時之后,你回來一看,redis機器,怎么內存占用還是50%呢?5g數據過期了,我從redis里查,是查不到了,結果過期的數據還占用著redis的內存。

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

3、面試題剖析

1.設置過期時間

我們set key的時候,都可以給一個expire time,就是過期時間,指定這個key比如說只能存活1個小時?10分鐘?這個很有用,我們自己可以指定緩存到期就失效。

如果假設你設置一個一批key只能存活1個小時,那么接下來1小時后,redis是怎么對這批key進行刪除的?答案是:定期刪除+惰性刪除

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

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

并不是key到時間就被刪除掉,而是你查詢這個key的時候,redis再懶惰的檢查一下

通過上述兩種手段結合起來,保證過期的key一定會被干掉。

很簡單,就是說,你的過期key,靠定期刪除沒有被刪除掉,還停留在內存里,占用著你的內存呢,除非你的系統去查一下那個key,才會被redis給刪除掉。

但是實際上這還是有問題的,如果定期刪除漏掉了很多過期key,然后你也沒及時去查,也就沒走惰性刪除,此時會怎么樣?如果大量過期key堆積在內存里,導致redis內存塊耗盡了,咋整?答案是:走內存淘汰機制。

2.內存淘汰

如果redis的內存占用過多的時候,此時會進行內存淘汰,有如下一些策略,redis 10個key,現在已經滿了,redis需要刪除掉5個key

1個key,最近1分鐘被查詢了100次

1個key,最近10分鐘被查詢了50次

1個key,最近1個小時倍查詢了1次

noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯,這個一般沒人用吧,實在是太惡心了

allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最常用的)

allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,這個一般沒人用吧,為啥要隨機,肯定是把最近最少使用的key給干掉啊

volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key(這個一般不太合適)

volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key

volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除

六、redis的高可用

1、面試題

如何保證Redis的高并發和高可用?redis的主從復制原理能介紹一下么?redis的哨兵原理能介紹一下么?

2、面試官心里分析

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

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

3、面試題剖析

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

關于redis高可用這一塊的內容太多了,想讓redis高可用,持久化你要做吧,主從復制你要做吧,哨兵你要知道吧,還有redis cluster。。。這些內容都非常重要,要掌握相關細節,而且比較多,可以參考redis系列的文章詳細看看。

《redis系列——企業級持久化方案》

《redis系列——主從復制》

《redis系列——生產環境Redis集群詳解》

七、緩存雪崩和穿透

1、面試題

了解什么是redis的雪崩和穿透?redis崩潰之后會怎么樣?系統該如何應對這種情況?如何處理redis的穿透?

2、面試官心里分析

其實這是問到緩存必問的,因為緩存雪崩和穿透,那是緩存最大的兩個問題,要么不出現,一旦出現就是致命性的問題。所以面試官一定會問你。

3、面試題剖析

要解決問題,我們總要先知道什么是緩存雪崩吧,來吧,看看下面這張圖

既然知道雪崩的現象了,再來解決就好辦了,關于解決緩存雪崩事件,可以大概分為事前、事中和事后三個解決方案:

事前:redis高可用,主從+哨兵,redis cluster,避免全盤崩潰

事中:本地ehcache緩存 + hystrix限流&降級,避免MySQL被打死

事后:redis持久化,快速恢復緩存數據

第二個問題,緩存穿透,緩存穿透其實就是,緩存中沒有數據,MySQL中也沒有數據。這樣的話,就會導致每次請求都直接穿透所有層的緩存,然后大量的高并發請求打到MySQL上去了,執行各種SQL語句,但是查詢出來的數據每次都是空。這種情況,如果不好好處理的話,可能會導致高并發訪問大量的進入MySQL上,打死MySQL。

我們的緩存穿透的解決方案,其實非常的簡單,就是說每次如果從數據庫查詢到的數據是空,就說明這個數據根本就不存在。那么如果這個數據不存在的話,我們不要不往redis中寫入數據,我們給寫入一個空的數據,或者默認數據。

八、生產環境的redis集群的部署架構

1、面試題

生產環境中的redis是怎么部署的?

2、面試官心里分析

看看你了解不了解你們公司的redis生產集群的部署架構,如果你不了解,那么確實你就很失職了,你的redis是主從架構?集群架構?用了哪種集群方案?有沒有做高可用保證?有沒有開啟持久化機制確保可以進行數據恢復?線上redis給幾個G的內存?設置了哪些參數?壓測后你們redis集群承載多少QPS?

3、面試題剖析

redis cluster,10臺機器,5臺機器部署了redis主實例,另外5臺機器部署了redis的從實例,每個主實例掛了一個從實例,5個節點對外提供讀寫服務,每個節點的讀寫高峰qps可能可以達到每秒5萬,5臺機器最多是25萬讀寫請求/s。

機器是什么配置?32G內存+8核CPU+1T磁盤,但是分配給redis進程的是10g內存,一般線上生產環境,redis的內存盡量不要超過10g,超過10g可能會有問題。

5臺機器對外提供讀寫,一共有50g內存。因為每個主實例都掛了一個從實例,所以是高可用的,任何一個主實例宕機,都會自動故障遷移,redis從實例會自動變成主實例繼續提供讀寫服務。

你往內存里寫的是什么數據?每條數據的大小是多少?商品數據,每條數據是10kb。100條數據是1mb,10萬條數據是1g。常駐內存的是200萬條商品數據,占用內存是20g,僅僅不到總內存的50%。目前高峰期每秒就是3500左右的請求量。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。