Redis 的基礎資料型別
十年河東,十年河西,莫欺少年窮
學無止境,精益求精
正文如下
那你能說一下他們的特性,還有分別的使用場景麼?
行吧,那我先從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資料結構作為選擇方案。
小結
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 計算的思想,不要每次都重新做複雜的計算,不管它會不會用到,而是讓它到需要被使用的時候再重新計算。
像Mybatis,Hibernate,都有懶載入思想。查詢一個部門,部門帶了一個員工的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產生的事件放入佇列中排隊,事件分派器每次從佇列中取出一個事件,把該事件交給對應的事件處理器進行處理。