1. 程式人生 > 其它 >為什麼分散式一定要有Redis?,Java面試必問的HashMap

為什麼分散式一定要有Redis?,Java面試必問的HashMap

為什麼分散式一定要有Redis?,Java面試必問的HashMap
  • 單執行緒操作,避免了頻繁的上下文切換

  • 採用了非阻塞 I/O 多路複用機制

題外話:我們現在要仔細的說一說 I/O 多路複用機制,因為這個說法實在是太通俗了,通俗到一般人都不懂是什麼意思。

打一個比方:小曲在 S 城開了一家快遞店,負責同城快送服務。小曲因為資金限制,僱傭了一批快遞員,然後小曲發現資金不夠了,只夠買一輛車送快遞。

經營方式一

客戶每送來一份快遞,小曲就讓一個快遞員盯著,然後快遞員開車去送快遞。

慢慢的小曲就發現了這種經營方式存在下述問題:

  • 幾十個快遞員基本上時間都花在了搶車上了,大部分快遞員都處在閒置狀態,誰搶到了車,誰就能去送快遞。

  • 隨著快遞的增多,快遞員也越來越多,小曲發現快遞店裡越來越擠,沒辦法僱傭新的快遞員了。

  • 快遞員之間的協調很花時間。

綜合上述缺點,小曲痛定思痛,提出了下面的經營方式。

經營方式二

小曲只僱傭一個快遞員。然後呢,客戶送來的快遞,小曲按送達地點標註好,然後依次放在一個地方。

最後,那個快遞員依次的去取快遞,一次拿一個,然後開著車去送快遞,送好了就回來拿下一個快遞。

上述兩種經營方式對比,是不是明顯覺得第二種,效率更高,更好呢?

在上述比喻中:

  • 每個快遞員→每個執行緒

  • 每個快遞→每個 Socket(I/O 流)

  • 快遞的送達地點→Socket 的不同狀態

  • 客戶送快遞請求→來自客戶端的請求

  • 小曲的經營方式→服務端執行的程式碼

  • 一輛車→CPU 的核數

於是我們有如下結論:

  • 經營方式一就是傳統的併發模型,每個 I/O 流(快遞)都有一個新的執行緒(快遞員)管理。

  • 經營方式二就是 I/O 多路複用。只有單個執行緒(一個快遞員),通過跟蹤每個 I/O 流的狀態(每個快遞的送達地點),來管理多個 I/O 流。

下面類比到真實的?Redis 執行緒模型,如圖所示:

簡單來說,就是我們的 redis-client 在操作的時候,會產生具有不同事件型別的 Socket。

在服務端,有一段?I/O 多路複用程式,將其置入佇列之中。然後,檔案事件分派器,依次去佇列中取,轉發到不同的事件處理器中。

需要說明的是,這個 I/O 多路複用機制,Redis 還提供了 select、epoll、evport、kqueue 等多路複用函式庫,大家可以自行去了解。

Redis 的資料型別,以及每種資料型別的使用場景

是不是覺得這個問題很基礎?我也這麼覺得。然而根據面試經驗發現,至少百分之八十的人答不上這個問題。

建議,在專案中用到後,再類比記憶,體會更深,不要硬記。基本上,一個合格的程式設計師,五種型別都會用到。

String

這個沒啥好說的,最常規的 set/get 操作,Value 可以是 String 也可以是數字。一般做一些複雜的計數功能的快取。

Hash

這裡 Value 存放的是結構化的物件,比較方便的就是操作其中的某個欄位。

我在做單點登入的時候,就是用這種資料結構儲存使用者資訊,以 CookieId 作為 Key,設定 30 分鐘為快取過期時間,能很好的模擬出類似 Session 的效果。

List

使用 List 的資料結構,可以做簡單的訊息佇列的功能。另外還有一個就是,可以利用 lrange 命令,做基於 Redis 的分頁功能,效能極佳,使用者體驗好。

Set

因為 Set 堆放的是一堆不重複值的集合。所以可以做全域性去重的功能。為什麼不用 JVM 自帶的 Set 進行去重?

因為我們的系統一般都是叢集部署,使用 JVM 自帶的 Set,比較麻煩,難道為了一個做一個全域性去重,再起一個公共服務,太麻煩了。

另外,就是利用交集、並集、差集等操作,可以計算共同喜好,全部的喜好,自己獨有的喜好等功能。

Sorted Set

Sorted Set多了一個權重引數 Score,集合中的元素能夠按 Score 進行排列。

可以做排行榜應用,取 TOP N 操作。Sorted Set 可以用來做延時任務。最後一個應用就是可以做範圍查詢。

Redis?的過期策略以及記憶體淘汰機制

這個問題相當重要,到底 Redis 有沒用到家,這個問題就可以看出來。

比如你?Redis 只能存 5G 資料,可是你寫了 10G,那會刪 5G 的資料。怎麼刪的,這個問題思考過麼?

還有,你的資料已經設定了過期時間,但是時間到了,記憶體佔用率還是比較高,有思考過原因麼?

回答:Redis 採用的是定期刪除+惰性刪除策略。

為什麼不用定時刪除策略

定時刪除,用一個定時器來負責監視 Key,過期則自動刪除。雖然記憶體及時釋放,但是十分消耗 CPU 資源。

在大併發請求下,CPU 要將時間應用在處理請求,而不是刪除 Key,因此沒有采用這一策略。

定期刪除+惰性刪除是如何工作

定期刪除,Redis 預設每個 100ms 檢查,是否有過期的 Key,有過期 Key 則刪除。

需要說明的是,Redis 不是每個 100ms 將所有的 Key 檢查一次,而是隨機抽取進行檢查(如果每隔 100ms,全部 Key 進行檢查,Redis 豈不是卡死)。

因此,如果只採用定期刪除策略,會導致很多 Key 到時間沒有刪除。於是,惰性刪除派上用場。

也就是說在你獲取某個 Key 的時候,Redis 會檢查一下,這個 Key 如果設定了過期時間,那麼是否過期了?如果過期了此時就會刪除。

採用定期刪除+惰性刪除就沒其他問題了麼?

不是的,如果定期刪除沒刪除 Key。然後你也沒即時去請求 Key,也就是說惰性刪除也沒生效。這樣,Redis的記憶體會越來越高。那麼就應該採用記憶體淘汰機制。

在 redis.conf 中有一行配置:

# maxmemory-policy volatile-lru

該配置就是配記憶體淘汰策略的(什麼,你沒配過?好好反省一下自己):

  • noeviction:當記憶體不足以容納新寫入資料時,新寫入操作會報錯。應該沒人用吧。

  • allkeys-lru:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的 Key。推薦使用,目前專案在用這種。

  • allkeys-random:當記憶體不足以容納新寫入資料時,在鍵空間中,隨機移除某個 Key。應該也沒人用吧,你不刪最少使用 Key,去隨機刪。

  • volatile-lru:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,移除最近最少使用的 Key。這種情況一般是把?Redis 既當快取,又做持久化儲存的時候才用。不推薦。

  • volatile-random:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,隨機移除某個 Key。依然不推薦。

  • volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的 Key 優先移除。不推薦。

PS:如果沒有設定 expire 的 Key,不滿足先決條件(prerequisites);那麼 volatile-lru,volatile-random 和 volatile-ttl 策略的行為,和 noeviction(不刪除) 基本上一致。

Redis?和資料庫雙寫一致性問題

一致性問題是分散式常見問題,還可以再分為最終一致性和強一致性。資料庫和快取雙寫,就必然會存在不一致的問題。

答這個問題,先明白一個前提。就是如果對資料有強一致性要求,不能放快取。我們所做的一切,只能保證最終一致性。

另外,我們所做的方案從根本上來說,只能說降低不一致發生的概率,無法完全避免。因此,有強一致性要求的資料,不能放快取。

回答:首先,採取正確更新策略,先更新資料庫,再刪快取。其次,因為可能存在刪除快取失敗的問題,提供一個補償措施即可,例如利用訊息佇列。

如何應對快取穿透和快取雪崩問題

這兩個問題,說句實在話,一般中小型傳統軟體企業,很難碰到這個問題。如果有大併發的專案,流量有幾百萬左右。這兩個問題一定要深刻考慮。

快取穿透,即黑客故意去請求快取中不存在的資料,導致所有的請求都懟到資料庫上,從而資料庫連線異常。

快取穿透解決方案:

  • 利用互斥鎖,快取失效的時候,先去獲得鎖,得到鎖了,再去請求資料庫。沒得到鎖,則休眠一段時間重試。

  • 採用非同步更新策略,無論 Key 是否取到值,都直接返回。Value 值中維護一個快取失效時間,快取如果過期,非同步起一個執行緒去讀資料庫,更新快取。需要做快取預熱(專案啟動前,先載入快取)操作。

  • 提供一個能迅速判斷請求是否有效的攔截機制,比如,利用布隆過濾器,內部維護一系列合法有效的 Key。迅速判斷出,請求所攜帶的 Key 是否合法有效。如果不合法,則直接返回。

快取雪崩,即快取同一時間大面積的失效,這個時候又來了一波請求,結果請求都懟到資料庫上,從而導致資料庫連線異常。

快取雪崩解決方案:

  • 給快取的失效時間,加上一個隨機值,避免集體失效。

  • 使用互斥鎖,但是該方案吞吐量明顯下降了。

  • 雙快取。我們有兩個快取,快取 A 和快取 B。快取 A 的失效時間為 20 分鐘,快取 B 不設失效時間。自己做快取預熱操作。

最後

小編利用空餘時間整理了一份《MySQL效能調優手冊》,初衷也很簡單,就是希望能夠幫助到大家,減輕大家的負擔和節省時間。

關於這個,給大家看一份學習大綱(PDF)檔案,每一個分支裡面會有詳細的介紹。

這裡都是以圖片形式展示介紹,如要下載原檔案以及更多的效能調優筆記(MySQL+Tomcat+JVM)可以直接【點選 “效能調優”】免費下載!