分散式為什麼使用Redis,以及他的好處

Undefind_object發表於2020-12-19

分散式為什麼使用Redis,以及他的好處
1.為什麼我們要使用Redis

在專案中使用Redis,主要考慮的就是兩個方面,一個是效能,還有一個是高併發,如果只是為了分散式鎖這些其他的功能,還有其他中介軟體代替,並非必須使用Redis,比如常見的Zookpeer。

效能:

如下圖所示,我們在碰到需要執行耗時特別久,且結果不頻繁變動的SQL,就特別適合將執行結果放入快取。這樣,後面的請求就回去快取中讀取,使請求快速響應。

特別是在秒殺系統中,同一時間,幾乎所有人都在點同一個商品,都在下單,執行的卻都是向資料庫查詢的同一操作。
在這裡插入圖片描述

在這裡插入圖片描述
根據互動效果的不同,響應時間沒有固定標準。在理想狀態下,我們的頁面跳轉需要在瞬間解決,對於頁內操作則需要在剎那間解決。

2.併發與Redis
如下圖所示,在大併發的情況下,所有的請求直接訪問資料庫,資料庫會出現連線異常。這個時候,就需要使用 Redis 做一個緩衝的操作,先讓請求訪問到 Redis,而不是直接訪問資料庫。
在這裡插入圖片描述

在這裡插入圖片描述

使用Redis的常見問題

快取和資料庫雙寫一致問題
快取雪崩問題
快取擊穿問題
快取的併發競爭問題
3.單執行緒的 Redis 為什麼這麼快
這個問題是對 Redis 內部機制的一個考察。很多人都不知道 Redis 是單執行緒工作模型。

原因主要是以下三點:

純記憶體操作
單執行緒操作,避免了頻繁的上下文切換
採用了非堵塞 I/O 多路複用機智
仔細說一說 I/O 多路複用機制,打一個比方,小明在 A 城開了一家外賣快餐店,並僱了一批配送員,配備一輛車

經營方式一:
客戶每下一份訂單,小明就讓一個配送員盯著,然後讓人開車去送。慢慢的小明發現這種經營方式有下述問題:

時間都花在了搶車上了,大部分配送員都處在閒置狀態,搶到車才能去送。
隨著下單的增多,配送員也越來越多,小明發現快遞店裡越來越擠,沒辦法僱傭新的快遞員了
配送員之間需要花很長時間來協調
經營方式二:
小明只需要固一個快遞員,一輛車,小明按照送達地點標註好,一次放在一個地方。最後,讓配送員依次開著車去送,送好一個就回來拿下一個

上述兩種方式對比,很明顯第二種經營方式高,在上述比喻中:

每個配送員相當於一個執行緒
每個訂單相當於每個socket(I/O流)
訂單的送達地點相當於socket的不同狀態
客戶送餐請求相當於來自客戶端的請求
明曲的經營方式相當於服務端執行的程式碼
一輛車相當於CPU的核數
於是有了如下結論:

經營方式一就是傳統的併發模型,每個I/O流(訂單)都有一個新的執行緒(配送員)管理。
經營方式二就是I/O多路複用。只有單個執行緒(一個配送員),通過跟蹤每個I/O流的狀態(每個配送員的送達地點),來管理多個 I/O 流。
下面類比真實的 Redis 執行緒模型,如圖所示:

在這裡插入圖片描述

Redis-client 在操作的時候,會產生具有不同事件型別的Socket。在服務端,有一段 I/O 多路複用程式,將其置於佇列之中。然後,檔案事件分派器,依次去佇列中取,轉發到不同的事件處理器中。

4.Redis 的資料型別及使用場景

一個合格的程式設計師,這五種型別都要會用

1.String

最常規的 set/get 操作,value 可以是 String 也可以是整形。一般做一些複雜的計數功能的快取等。

2.Hash

這裡 value 存放的是結構化的物件,比較方便的就是操作其中某個欄位。我在做單點登入的時候,就是用這種資料結構儲存資訊,以 CookieId 作為 key,設定 30 分鐘為快取過期的時間,能很好的模擬出類似的 Session 的效果。

3.List

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

4.Set

因為 Set 堆放的是一堆不重複值的集合。所以可以做全域性去重的功能。我們的系統一般都是叢集部署,使用 JVM 自帶的 Set 比較麻煩。另外,就是利用交集、並集、差集等操作,可以計算共同喜好,全部的喜好,自己獨有的喜歡功能等。

5.Sorted Set

Sorted Set 多了一個權重引數 Score,集合中的元素能夠按 Score 進行排列。可以做排行榜應用,取 TOP N 操作。Sorted Set 可以用來做延時任務。

5.Redis 的過期策略和記憶體淘汰機制

Redis 是否用到家,從這就可以看出來,比如你 Redis 只能存 5G 資料,可是你寫了10G,那會刪 5G 資料。怎麼刪的,這個問題你思考過麼?

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

為什麼不用定時刪除策略

定時刪除,用一個定時器來負責監視 Key,過期則自動刪除。雖然記憶體及時釋放,但是十分消耗 CPU 資源。在大併發請求下,CPU要用大部分時間來處理請求,而不是刪除 Key,因此沒有采用這一策略。

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

定期刪除,Redis 預設每過 100ms 檢查,有過期 Key 則刪除。需要說明的是,Redis 不是每過 100ms 會將所有的 Key 檢查一遍,而是隨機抽取檢查。如果只採用定期刪除策略,會導致很多 Key 到時間沒有刪除。於是,惰性刪除派上用場。

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

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

在 redis.conf 中有一行配置:
#maxmemory-policy volatile-lru(該配置就是配記憶體淘汰策略的)

noeviction:當記憶體不足以容納新寫入資料時,新寫入操作會報錯。
allkeys-lru:當記憶體不足以容納新寫入資料時,在鍵空間中,移除最近最少使用的key。(推薦使用,目前專案在用這種)(最近最久使用演算法)
allkeys-random:當記憶體不足以容納新寫入資料時,在鍵盤空間中,隨機移除某個 Key。(應該也沒人用吧,你不刪最少使用 Key,去隨機刪)
volatile-lru:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,移除最近最少使用的 key。(依然不推薦)
volatile-random:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,隨機移除某個 Key。(依然不推薦)
volatile-ttl:當記憶體不足以容納新寫入資料時,在設定了過期時間的鍵空間中,有更早過期時間的 Key 優先移除。(不推薦)
6.Redis 和資料庫雙寫一致性問題

一致性問題還可以再分為最終一致性和強一致性。資料庫和快取雙寫,就必然會存在不一致的問題。前提是如果對資料有強一致性要求,不能放快取。我們所做的一切,只能保證最終一致性。

另外,我們所做的方案從根本上來說,只能降低不一致發生的概率。因此,有強一致性要求的資料,不能放快取。首先採取正確更新策略,先更新資料庫,在刪快取。其次因為可能存在刪除快取失敗的問題,提供一個補償措施即可,例如利用訊息佇列。

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

這兩個問題,一般中小型傳統企業很難碰到。如果有大併發的專案,流量有幾百萬左右,這兩個問題一定要深刻考慮。快取穿透,即黑客故意去請求快取中不存在的資料,導致所以的請求都懟到資料庫上,從而資料庫連線異常。快取雪崩,即快取同一時間大面積的失效,這個時候又來了一波請求,結果請求都懟到資料庫上,從而導致資料庫連線異常。

快取穿透解決方案:

利用互斥鎖,快取失效的時候,先去獲得鎖,得到鎖了,再去請求資料庫。沒得到鎖,則休眠一段時間重試。
採用非同步更新策略,無論 key 是否取到值,都直接放回。value 值中維護一個快取失效時間,快取若果過期,非同步起一個執行緒去讀資料庫,跟新快取。需要做快取預熱(專案啟動前,先載入快取)操作。
提供一個能迅速判斷請求是否有效的攔截機制,比如,利用布隆過濾器,內部維護一系列合法有效的 key。迅速判斷出,請求所攜帶的 key 是否合法有效。如果不合法,則直接返回。
快取雪崩解決方案:

給快取的失效時間,加上一個隨機值,避免集體失效。
使用互斥鎖,但是該方案的吞吐量明顯下降了。
雙快取。我們有兩個快取,快取 A 和快取 B 。快取 A 的失效時間為 20 分鐘,快取 B 不捨者失效時間。自己做快取預熱操作。
然後細分以下幾個小點:從快取 A 讀資料庫,有則直接返回;A 沒有資料,直接從 B 讀資料,直接返回,並且非同步啟動一個更新執行緒,更新執行緒同時跟新快取 A 和快取 B。
8、如何解決 Redis 的併發競爭 Key 問題

這個問題大致就是,同時有多個子系統去 Set 一個 Key。這個時候要注意什麼呢?大家基本都是推薦用 Redis 事務機制。

但是我並不推薦使用 Redis 的事務機制。因為我們的生產環境,基本都是 Redis 叢集環境,做了資料分片操作。你一個事務中有涉及到多個 Key 操作的時候,這多個 Key 不一定都儲存在同一個 redis-server 上。因此,Redis 的事務機制,十分雞肋。

如果對這個 Key 操作,不要求順序

這種情況下,準備一個分散式鎖,大家去搶鎖,搶到鎖就做 set 操作即可,比較簡單。

如果對這個 Key 操作,要求順序

假設有一個 key1,系統 A 需要將 key1 設定為 valueA,系統 B 需要將 key1 設定為 valueB,系統 C 需要將 key1 設定為 valueC。

期望按照 key1 的 value 值按照 valueA > valueB > valueC 的順序變化。這種時候我們在資料寫入資料庫的時候,需要儲存一個時間戳。

假設時間戳如下:

系統 A key 1 {valueA 3:00}

系統 B key 1 {valueB 3:05}

系統 C key 1 {valueC 3:10}

那麼,假設系統 B 先搶到鎖,將 key1 設定為{valueB 3:05}。接下來系統 A 搶到鎖,發現自己的 valueA 的時間戳早於快取中的時間戳,那就不做 set 操作了,以此類推。其他方法,比如利用佇列,將 set 方法變成序列訪問也可以。

9.總結

Redis 在國內各大公司都能看到其身影,比如我們熟悉的新浪,阿里,騰訊,百度,美團,小米等。學習 Redis,這幾方面尤其重要:Redis 客戶端、Redis 高階功能、Redis 持久化和開發運維常用問題探討、Redis 複製的原理和優化策略、Redis 分散式解決方案等。

來源:姚登晏
http://www.cnblogs.com/yaodengyan/p/9717080.html

相關文章