《吊打面試官》系列-Redis終章_凜冬將至、FPX_新王登基

  • 2019 年 11 月 11 日
  • 筆記

你知道的越多,你不知道的越多

點贊再看,養成習慣

前言

Redis在互聯網技術存儲方面使用如此廣泛,幾乎所有的後端技術面試官都要在Redis的使用和原理方面對小夥伴們進行360°的刁難。作為一個在互聯網公司面一次拿一次offer的面霸(請允許我使用一下誇張的修辭手法),打敗了無數競爭對手,每次都只能看到無數落寞的身影失望的離開,略感愧疚,在一個寂寞難耐的夜晚,我痛定思痛,決定開始寫《吊打面試官》系列,希望能幫助各位讀者以後面試勢如破竹,對面試官進行360°的反擊,吊打問你的面試官,讓一同面試的同僚瞠目結舌,瘋狂收割大廠offer!

絮叨

男兒何不帶吳鉤,收取關山五十州 FPX ?B,LPL兩年連冠? ?B!

看着金色的雨落下,我到窗邊,發現天有點藍,風有點綿,我的眼角又濕了!

最近雙十一講道理有點忙的說,直接肝爆,就是這樣作為暖男的我,還是給你們擠出時間搞出終章,忍不住給自己點贊?

放個雙十一照片證明真的忙,希望別取關!!!

現在你們在看的時候,我應該還在睡覺哈哈。困?

之前跟你們說的,限流降級,是不是在雙十一又應驗了,下單接口其實沒掛,犧牲部分用戶體驗,保住服務器,你多點幾下是可以成功的,等流量高峰過去了,所有的用戶全部都恢復正常訪問,服務器也沒啥事。

去年退款接口被打崩了,今年阿里明顯也聰明了很多。

正文

上幾期吊打系列我們提到了Redis的很多知識,還沒看的小夥伴可以回顧一下

那提到Redis我相信各位在面試,或者實際開發過程中對基本類型的使用場景,並發競爭帶來的問題,以及緩存數據庫雙寫入一致性的問題等,我們有請下一位受害者。

面試開始

一個大腹便便,穿着格子襯衣的中年男子,拿着一個滿是劃痕的mac向你走來,看着快禿頂的頭髮,心想着肯定是尼瑪頂級架構師吧!但是我們腹有詩書氣自華,虛都不虛。(這不是第一篇文章的面試官么?)

小夥子,你還記得我在第一章裏面問過你,Redis有幾種基礎數據類型么?

嗯嗯,帥氣的面試官,我肯定記得,沒齒難忘!!!

我特么謝謝你,都四面了還不給Offer!

那你能說一下他們的特性,還有分別的使用場景么?

行吧,那我先從String說起。

String:

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

但是真實的開發環境中,很多仔可能會把很多比較複雜的結構也統一轉成String去存儲使用,比如有的仔他就喜歡把對象或者List轉換為JSONString進行存儲,拿出來再反序列話啥的。

我在這裡就不討論這樣做的對錯了,但是我還是希望大家能在最合適的場景使用最合適的數據結構,對象找不到最合適的但是類型可以選最合適的嘛,之後別人接手你的代碼一看這麼規範,誒這小夥子有點東西呀,看到你啥都是用的String垃圾!

好了這些都是題外話了,道理還是希望大家記在心裏,習慣成自然嘛,小習慣成就你。

String的實際應用場景比較廣泛的有:

  • 緩存功能:String字符串是最常用的數據類型,不僅僅是Redis,各個語言都是最基本類型,因此,利用Redis作為緩存,配合其它數據庫作為存儲層,利用Redis支持高並發的特點,可以大大加快系統的讀寫速度、以及降低後端數據庫的壓力。

  • 計數器:許多系統都會使用Redis作為系統的實時計數器,可以快速實現計數和查詢的功能。而且最終的數據結果可以按照特定的時間落地到數據庫或者其它存儲介質當中進行永久保存。

  • 共享用戶Session:用戶重新刷新一次界面,可能需要訪問一下數據進行重新登錄,或者訪問頁面緩存Cookie,但是可以利用Redis將用戶的Session集中管理,在這種模式只需要保證Redis的高可用,每次用戶Session的更新和獲取都可以快速完成。大大提高效率。

Hash:

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

但是這個的場景其實還是多少單一了一些,因為現在很多對象都是比較複雜的,比如你的商品對象可能裏面就包含了很多屬性,其中也有對象。我自己使用的場景用得不是那麼多。

List:

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

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

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

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

List本身就是我們在開發過程中比較常用的數據結構了,熱點數據更不用說了。

  • 消息隊列:Redis的鏈表結構,可以輕鬆實現阻塞隊列,可以使用左進右出的命令組成來完成隊列的設計。比如:數據的生產者可以通過Lpush命令從左邊插入數據,多個數據消費者,可以使用BRpop命令阻塞的「搶」列表尾部的數據。

  • 文章列表或者數據分頁展示的應用。

    比如,我們常用的博客網站的文章列表,當用戶量越來越多時,而且每一個用戶都有自己的文章列表,而且當文章多時,都需要分頁展示,這時可以考慮使用Redis的列表,列表不但有序同時還支持按照範圍內獲取元素,可以完美解決分頁查詢功能。大大提高查詢效率。

Set:

Set 是無序集合,會自動去重的那種。

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

可以基於 Set 玩兒交集、並集、差集的操作,比如交集吧,我們可以把兩個人的好友列表整一個交集,看看倆人的共同好友是誰?對吧。

反正這些場景比較多,因為對比很快,操作也簡單,兩個查詢一個Set搞定。

Sorted Set:

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

有序集合的使用場景與集合類似,但是set集合不是自動有序的,而Sorted set可以利用分數進行成員間的排序,而且是插入時就排序好。所以當你需要一個有序且不重複的集合列表時,就可以選擇Sorted set數據結構作為選擇方案。

  • 排行榜:有序集合經典使用場景。例如視頻網站需要對用戶上傳的視頻做排行榜,榜單維護可能是多方面:按照時間、按照播放量、按照獲得的贊數等。

  • Sorted Sets來做帶權重的隊列,比如普通消息的score為1,重要消息的score為2,然後工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。

    微博熱搜榜,就是有個後面的熱度值,前面就是名稱

小結

Redis基礎類型有五種,這個我在基礎裏面也有提到了,這個問題其實一般都是對P6以下,也就是1-3年左右的小夥伴可能是會問得比較多的問題。

能回答出來五種我想大家都可以,但是不知道大家是否知道,五種類型具體的使用場景,以及什麼時候用什麼類型最合適呢?

要是你回答的不好,沒說出幾種數據類型,也沒說什麼場景,你完了,面試官對你印象肯定不好,覺得你平時就是做個簡單的 set 和 get。所以看似很簡單的面試題實則最容易看出你的深淺了,大家都要注意打好基礎

你有沒有考慮過,如果你多個系統同時操作(並發)Redis帶來的數據問題?

嗯嗯這個問題我以前開發的時候遇到過,其實並發過程中確實會有這樣的問題,比如下面這樣的情況

系統A、B、C三個系統,分別去操作Redis的同一個Key,本來順序是1,2,3是正常的,但是因為系統A網絡突然抖動了一下,B,C在他前面操作了Redis,這樣數據不就錯了么。

就好比下單,支付,退款三個順序你變了,你先退款,再下單,再支付,那流程就會失敗,那數據不就亂了?你訂單還沒生成你卻支付,退款了?明顯走不通了,這在線上是很恐怖的事情。

那這種情況怎麼解決呢?

我們可以找個管家幫我們管理好數據的嘛!

某個時刻,多個系統實例都去更新某個 key。可以基於 Zookeeper 實現分佈式鎖。每個系統通過 Zookeeper 獲取分佈式鎖,確保同一時間,只能有一個系統實例在操作某個 Key,別人都不允許讀和寫。

你要寫入緩存的數據,都是從 MySQL 里查出來的,都得寫入 MySQL 中,寫入 MySQL 中的時候必須保存一個時間戳,從 MySQL 查出來的時候,時間戳也查出來。

每次要寫之前,先判斷一下當前這個 Value 的時間戳是否比緩存里的 Value 的時間戳要新。如果是的話,那麼可以寫,否則,就不能用舊的數據覆蓋新的數據。

你只要用緩存,就可能會涉及到緩存與數據庫雙存儲雙寫,你只要是雙寫,就一定會有數據一致性的問題,那麼你如何解決一致性問題?

一般來說,如果允許緩存可以稍微的跟數據庫偶爾有不一致的情況,也就是說如果你的系統不是嚴格要求 「緩存+數據庫」 必須保持一致性的話,最好不要做這個方案,即:讀請求和寫請求串行化,串到一個內存隊列里去。

串行化可以保證一定不會出現不一致的情況,但是它也會導致系統的吞吐量大幅度降低,用比正常情況下多幾倍的機器去支撐線上的一個請求。

把一些列的操作都放到隊列裏面,順序肯定不會亂,但是並發高了,這隊列很容易阻塞,反而會成為整個系統的弱點,瓶頸

你了解最經典的KV、DB讀寫模式么?

最經典的緩存+數據庫讀寫的模式,就是 Cache Aside Pattern

  • 讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然後取出數據後放入緩存,同時返迴響應。
  • 更新的時候,先更新數據庫,然後再刪除緩存

為什麼是刪除緩存,而不是更新緩存?

原因很簡單,很多時候,在複雜點的緩存場景,緩存不單單是數據庫中直接取出來的值。

比如可能更新了某個表的一個字段,然後其對應的緩存,是需要查詢另外兩個表的數據並進行運算,才能計算出緩存最新的值的。

另外更新緩存的代價有時候是很高的。是不是說,每次修改數據庫的時候,都一定要將其對應的緩存更新一份?也許有的場景是這樣,但是對於比較複雜的緩存數據計算的場景,就不是這樣了。如果你頻繁修改一個緩存涉及的多個表,緩存也頻繁更新。但是問題在於,這個緩存到底會不會被頻繁訪問到?

舉個栗子:一個緩存涉及的表的字段,在 1 分鐘內就修改了 20 次,或者是 100 次,那麼緩存更新 20 次、100 次;但是這個緩存在 1 分鐘內只被讀取了 1 次,有大量的冷數據

實際上,如果你只是刪除緩存的話,那麼在 1 分鐘內,這個緩存不過就重新計算一次而已,開銷大幅度降低。用到緩存才去算緩存。

其實刪除緩存,而不是更新緩存,就是一個 Lazy 計算的思想,不要每次都重新做複雜的計算,不管它會不會用到,而是讓它到需要被使用的時候再重新計算。

MybatisHibernate,都有懶加載思想。查詢一個部門,部門帶了一個員工的 List,沒有必要說每次查詢部門,都裏面的 1000 個員工的數據也同時查出來啊。80% 的情況,查這個部門,就只是要訪問這個部門的信息就可以了。先查部門,同時要訪問裏面的員工,那麼這個時候只有在你要訪問裏面的員工的時候,才會去數據庫裏面查詢 1000 個員工。

Redis 和 Memcached 有啥區別,為啥選擇用Redis作為你們的緩存中間件?

Redis 支持複雜的數據結構:

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

Redis 原生支持集群模式:

在 redis3.x 版本中,便能支持 Cluster 模式,而 Memcached 沒有原生的集群模式,需要依靠客戶端來實現往集群中分片寫入數據。

性能對比:

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

Tip:其實面試官這麼問,是想看你知道為啥用這個技術棧么?你為啥選這個技術棧,你是否做過技術選型的對比,優缺點你是否了解,你啥都不知道,只是為了用而用,那你可能就差點意思了。

Redis 的線程模型了解么?

Redis 內部使用文件事件處理器 file event handler,這個文件事件處理器是單線程的,所以 Redis 才叫做單線程的模型。它採用 IO 多路復用機制同時監聽多個 Socket,根據 Socket 上的事件來選擇對應的事件處理器進行處理。

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

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

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

面試結束

小夥子對你面試了四輪,你說話有理有據,邏輯清晰,來公司後肯定是一把好手,我想要不你來當我的Leader吧,哈哈?

面試官別跟我開玩笑了,我跟您這樣日積月累的技術專家還是有很多差距的,您的經驗和技術上的深度,沒有很長時間的磨練是無法達到的,我還得多跟您學習。

好的,小夥子有點東西,你年少有為不自卑,知道什麼是珍貴,就是你了來上班吧。

好的面試官,不過我想我在Java基礎,MQ,Dubbo等等領域還有好多知識點您沒問我,要不下次繼續面我?

強行,為吊打下一期埋伏筆哈哈,下期寫啥你們定!!!

能撐到最後,你自己都忍不住自己給自己點個贊了!

暗示點贊,每次都看了不點贊,你們想白嫖我么?你們好壞喲,不過我喜歡)。


《吊打面試官》Redis系列 —- 全劇終


總結

既然都說了是Redis的終章我最後也做個Redis方面常見面試題,題目的總結,答案大家要去思考我前面的文章基本上都提到了,結果可以去我公眾號回復【答案】獲取,不過我還是希望大家能看到題目就能想到答案,並且記在心中,教大家怎麼回答只是幫大家組織下語言,真正的場景解決方案還是要大家理解的。

(周三以後出答案,我先睡會)

  • 0、在集群模式下,Redis 的 Key 是如何尋址的?分佈式尋址都有哪些算法?了解一致性 Hash 算法嗎?
  • 1、使用Redis有哪些好處?
  • 2、Redis相比Memcached有哪些優勢?
  • 3、Redis常見性能問題和解決方案
  • 4、MySQL里有2000w數據,Redis中只存20w的數據,如何保證Redis中的數據都是熱點數據?
  • 5、Memcache與Redis的區別都有哪些?
  • 6、Redis 常見的性能問題都有哪些?如何解決?
  • 7、在什麼樣的場景下可以充分的利用Redis的特性,大大提高Redis的效率?
  • 8、Redis的緩存雪崩、穿透、擊穿了解么?有什麼異同點?分別怎麼解決?
  • 9、Redis的基本類型有哪些?他們的使用場景了解么?比較高級的用法你使用過么?
  • 10、Redis主從怎麼同步數據的?集群的高可用怎麼保證?持久化機制了解么?
  • 11、為什麼 redis 單線程卻能支撐高並發?
  • 12、如何保證緩存和數據庫數據的一致性?
  • 13、項目中是怎麼用緩存的,用了緩存之後會帶來什麼問題?

絮叨+

最後我想說的就是,我這四章只是介紹到了一些Redis面試比較常見的問題,其實還有很多點我都沒回答到,大家如果為了對付面試可能是夠用了,但是我們技術人員還是要保持對技術的敬畏心,你不能淺嘗即止,還是要深究的。

你永遠只會用,不去考慮用了會帶來的問題,以及出現問題之後的解決方案,我覺得你大概率會停滯不前,既然入都入了這行了,為啥不武裝一下自己。

其實學習技術是個反哺的過程,學習的時候可能你只是感覺知識廣度、深度上去了,一個知識點你這樣,兩個、三個知識點你都這樣,最後你發現你的技術已經跟身邊一樣P6的仔不一樣了,這樣你可能在團隊重大項目的貢獻都上去了,那P7的晉陞幾率是不是大了,錢是不是上去了,女朋友是不是好看了,房子是不是大了。

End

好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是人才,我後面會每周都更新幾篇《吊打面試官》系列和Java技術棧相關的文章。如果你有什麼想知道的,也可以留言給我,或者去公眾號加我微信,我一有時間就會寫出來,我們共同進步。

非常感謝人才們能看到這裡,如果這個文章寫得還不錯,覺得「敖丙」有點東西的話 求點贊? 求關注❤️ 求分享? 求留言? 對暖男我來說非常有用

各位的支持和認可,就是我創作的最大動力,我們下篇文章見!

敖丙 | 文 【原創】【轉載請聯繫本人】


《吊打面試官》系列每周持續更新,可以關注我的公眾號「 JavaFamily 」第時間閱讀和催更(公眾號比博客早一到兩天喲),裏面也有我個人微信有什麼問題也可以直接滴滴我,我也是個新人,不過不影響我們一起進步,作為渣男,我給不了你幸福,還給不了你溫暖嘛?