緩存面試準備

一、基本說明

說到緩存,可能大家首先想到的就是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個部分:多個socketIO多路複用程序文件事件分派器事件處理器(命令請求處理器、命令回覆處理器、連接應答處理器,等等)。

多個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)我的數據明明都過期了,怎麼還佔用着內存啊?

還有一種就是如果你設置好了一個過期時間,你知道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左右的請求量。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章