redis架構設計

一、 簡介

  1. 高性能(目前已知性能最快)
    1. 讀速度:110000 次 /s
    2. 寫速度:81000 次 /s
  2. key-value(單個value的最大限制是1GB)類型的內存數據庫
  3. 數據庫在內存中進行操作
  4. 支持數據持久化
    1. 定期異步操作將數據庫數據flush到硬盤上
  5. 支持string,list,set,sorted set,hash
  6. 操作都是原子性
  7. 支持事務
    1. 對數據的更改要麼全部執行,要麼全部不執行
    2. 事務中任意命令執行失敗,其餘命令依然被執行(Redis 事務不保證原子性,也不支持回滾)
    3. 事務中的多條命令被一次性發送給服務器,服務器在執行命令期間,不會去執行其他客戶端的命令請求
  8. 支持數據備份( master - slave 模式的數據備份)
  9. 數據庫容量受到物理內存的限制,不能用作海量數據的高性能讀寫
    1. 適合的場景侷限在較小數據量的高性能操作和運算上

二、 可實現的功能

2.1 消息隊列服務

用List來做FIFO雙向鏈表(前一個元素和後一個元素),實現一個輕量級的高性能消息隊列服務

2.2 tag系統

Set可以做高性能的tag系統

2.3 緩存

高併發情況下,將用戶基本信息放到redis中,減少對數據庫的訪問,直接做法就是建立用戶的內存模型

存儲用戶會話緩存信息

 

 

2.4 消息(支持 publish/subscribe 通知)

 

2.5 排行榜/計數器

計數器:Redis在內存中對數字進行遞增或遞減的操作實現的非常好。

排行榜:有序集合(Sorted Set)通過分數來實現

 

 

三、淘汰策略

3.1從已經設置過期時間的數據集中

volatile-lru: 挑選最近最少使用的數據淘汰
volatile-ttl: 挑選即將要過期的數據淘汰
volatile-random: 隨機挑選數據淘汰

3.2從所有的數據集中

allkeys-lru: 挑選最近最少使用的數據淘汰
allkeys-random:,隨機挑選數據淘汰

3.3 no-enviction:禁止淘汰數據
 

四、過期鍵的策略

4.1定時刪除

緩存過期時間到就刪除,創建timer耗CPU

4.2惰性刪除

獲取的時候檢查,不獲取一直留在內存,對內存不友好

4.3定期刪除

CPU和內存的折中方案

 

五、數據類型分析

5.1 string

最基本的數據類型。
二進制安全的,可以包含任何數據。
最大能存儲 512 MB。

5.2 hash

  • 一個鍵值對(key - value)集合。
  • 一個 string 類型的 key 和 value 的映射表,
  • 適合用於存儲對象
  • 可以對對象某一項屬性值進行存儲、讀取、修改等操作。

 

5.3 list

  • 是簡單的字符串列表(集合)。
  • 按照插入順序排序。我們可以網列表的左邊或者右邊添加元素。
  • 元素是可重複的。
  • 適用做消息隊列或最新消息排行等功能。

 

5.4 set

  • 無序
  • 通過哈希表實現,因此添加、刪除、查找的複雜度都是 O(1)。
  • 是一個 key 對應着多個字符串類型的 value的集合
  • 集合元素不能重複
  • 可以統計訪問網站的所有獨立ip。

5.5 Zset

  • 和set 一樣
  • 不同的是zset 每個元素都會關聯一個 double 類型的分數,通過分數來爲集合中的成員進行從小到大的排序。
  • value元素是唯一的,但是分數(score)卻可以重複。
  • 可用作排行榜等場景。

 

六、Redis高可用架構

6.1、持久化

 

6.1.1、RDB

  • 定期將存儲的數據生成快照並存儲到磁盤上,
  • 可以將快照複製到其他服務器從而創建具有相同數據的服務器副本。
  • 系統故障,會丟失最後一次創建快照之後的數據。
  • 數據量大,保存快照的時間會很長。

6.1.2、AOF

  • 將redis執行過的所有寫指令記錄下來,在下次redis重新啓動時,只要把這些寫指令從前到後再執行一遍,就可以實現數據恢復。
  • 將寫命令添加到 AOF 文件(append only file)末尾。
  • 對文件進行寫入並不會馬上將內容同步到磁盤上,而是先存儲到緩衝區,然後由操作系統決定什麼時候同步到磁盤。
  • AOF有重寫的特性,去除AOF文件中的冗餘寫命令。
  • redis重啓的話,則會優先採用AOF方式來進行數據恢復(aop數據更完整)
  • 需要設置同步選項,從而確保寫命令同步到磁盤文件上的時機。

6.1.2.1、同步選項

always

  • 每個寫命令都同步
  • 會嚴重減低服務器的性能

eyerysec

  • 每秒同步一次
  • 選項比較合適,可以保證系統崩潰時只會丟失一秒左右的數據,且每秒執行一次同步對服務器幾乎沒有任何影響。

no

  • 讓操作系統來決定何時同步
  • 並不能給服務器性能帶來多大的提升
  • 會增加系統崩潰時數據丟失的數量

 

6.2、複製

爲了解決單點數據庫問題(主從和主備兩種模式),會把數據複製多個副本部署到其他節點上, 實現Redis的高可用性, 保證數據和服務的高度可靠性。


主備(keepalived)模式


主機備機對外提供同一個虛擬IP,客戶端通過虛擬IP進行數據操作,正常期間主機一直對外提供服務,宕機後VIP自動漂移到備機上。

主從模式


當Master宕機後,通過選舉算法從slave中選舉出新Master繼續對外提供服務,主機恢復後以slave的身份重新加入,此模式下可以使用讀寫分離,如果數據量比較大,不希望過多浪費機器,還希望在宕機後,做一些自定義的措施,比如報警、記日誌、數據遷移等操作,推薦使用主從方式,因爲和主從搭配的一般還有個管理監控中心(哨兵)。

 

6.3、 數據通過過程

6.3.1 Redis2.8之前同步

 

 

  • ①從數據庫向主數據庫發送sync(數據同步)命令。
  • ②主數據庫接收同步命令後,會保存快照,創建一個RDB文件。
  • ③當主數據庫執行完保持快照後,會向從數據庫發送RDB文件,而從數據庫會接收並載入該文件。
  • ④主數據庫將緩衝區的所有寫命令發給從服務器執行。
  • ⑤以上處理完之後,之後主數據庫每執行一個寫命令,都會將被執行的寫命令發送給從數據庫。可以同步發送也可以異步發送,同步發送可以不用每臺都同步,可以配置一臺master,一臺slave,同時這臺salve又作爲其他slave的master。異步方式無法保證數據的完整性,比如在異步同步過程中主機突然宕機了,也稱這種方式爲數據弱一致性。
  • 注意:在Redis2.8之後,主從斷開重連後會根據斷開之前最新的命令偏移量進行增量複製。

6.3.2 Redis2.8之後同步

 

6.4 、哨兵

 當主節點出現故障時,由哨兵自動完成故障發現和轉移,並通知應用方,實現高可用性。

6.4.1 Redis哨兵主要功能

集羣監控:負責監控Redis master和slave進程是否正常工作
消息通知:如果某個Redis實例有故障,那麼哨兵負責發送消息作爲報警通知給管理員
故障轉移:如果master node掛掉了,會自動轉移到slave node上
配置中心:如果故障轉移發生了,通知client客戶端新的master地址

6.4.2 Redis哨兵的高可用

原理

  • 哨兵機制建立了多個哨兵節點(進程),共同監控數據節點的運行狀況。
  • 同時哨兵節點之間也互相通信,交換對主從節點的監控狀況。

哨兵用來判斷節點是否正常的依據

每隔1秒每個哨兵會向整個集羣:Master主服務器+Slave從服務器+其他Sentinel(哨兵)進程,發送一次ping命令做一次心跳檢測。

主節點down掉依據(主觀下線和客觀下線

主觀下線

一個哨兵節點判定主節點down掉是主觀下線

客觀下線

只有半數哨兵節點都判定主觀下線,就會會判定主節點客觀下線

補充

基本上哪個哨兵節點最先判斷出這個主節點客觀下線,就會在各個哨兵節點中發起投票機制Raft算法(選舉算法),最終被投爲領導者的哨兵節點完成主從自動化切換的過程。

 

6.5 、 集羣

高可用性:在主機掛掉後,自動故障轉移,使前端服務對用戶無影響。

讀寫分離:將主機讀壓力分流到從機上。

6.5.1、小數據到大數據過程

 

問題

緩存數據量不斷增加時,單機內存不夠使用

解決方案

把數據切分不同部分,分佈到多臺服務器上。 可在客戶端對數據進行分片,數據分片算法詳見一致性Hash詳解、虛擬桶分片。

 

問題

數據量持續增加時,越來越多的客戶端直接訪問Redis服務器難以管理,而造成風險

解決方案

根據不同場景下的業務申請對應的分佈式集羣。

加入了代理服務(Codis和Twemproxy),通過代理訪問真實的Redis服務器進行讀寫

代理服務(Codis和Twemproxy)

在代理層做安全措施,比如限流、授權、分片,避免客戶端越來越多的邏輯代碼,不但臃腫升級還比較麻煩。
代理層無狀態,可任意擴展節點,對於客戶端來說,訪問代理跟訪問單機Redis一樣。

 

 

 

6.5.2Redis官網的集羣架構

 

 

6.5.2.1、原理

客戶端與Redis(Master)節點直連,不需要中間Proxy層,根據公式HASH_SLOT=CRC16(key) mod 16384,計算出映射到哪個分片上,然後Redis會去相應的節點進行操作

 

6.5.2.2、優點

  • 無需Sentinel哨兵監控,如果Master掛了,自動將Slave切換Master
  • 可以進行水平擴容
  • 支持自動化遷移
    • 當出現某個Slave宕機了,那麼就只有Master了,這時候的高可用性就無法很好的保證了,萬一Master也宕機了,咋辦呢? 針對這種情況,如果說其他Master有多餘的Slave ,集羣自動把多餘的Slave遷移到沒有Slave的Master 中。

6.5.2.3、缺點

  • 批量操作是個坑,不同的key會劃分到不同的slot中,因此直接使用mset或者mget等操作是行不通的。
    • 解決方案:使用Hashtag保證這些key映射到同一臺Redis節點上。
  • 資源隔離性較差,容易出現相互影響的情況。
     

 

七、Redis高併發及熱key解決之道

7.1、併發設置key及分佈式鎖

問題

集羣環境下的定時任務,存在A服務器執行任務t1,B服務器也執行了任務t1,如果t1是創建訂單,那麼就會出現重複訂單。

解決方案

  • 分佈式鎖
  • 使用消息隊列,把並行讀寫進行串行化。

 

7.2、熱key問題

問題

瞬間有幾十萬的請求去訪問某個固定的key,從而壓垮緩存服務的情況

熱key判斷依據

  • 憑藉業務經驗,進行預估哪些是熱key
  • 在客戶端進行收集
  • 在Proxy層做收集
  • 用redis自帶命令(monitor命令、hotkeys參數)


解決方案:

  1. 利用二級緩存,比如一個HashMap。在你發現熱key以後,把熱key加載到系統的JVM中。查找的時候先去本地jvm查詢,查不到再去redis查詢
  2. 備份熱key,不要讓key走到同一臺redis上。我們把這個key,在多個redis上都存一份。可以用HOTKEY加上一個隨機數(N,集羣分片數)組成一個新key。
  3. 熱點數據儘量不要設置過期時間,在數據變更時同步寫緩存,防止高併發下重建緩存的資源損耗。


7.3、緩存穿透

 指查詢一個根本不存在的數據,緩存層和存儲層都不會命中,但是出於容錯的考慮,如果從存儲層查不到數據則不寫入緩存層。

 7.3.1、影響

將導致不存在的數據每次請求都要到存儲層去查詢,失去了緩存保護後端存儲的意義。

 7.3.2、緩存穿透原因


 業務自身代碼或者數據出現問題,
 一些惡意攻擊、爬蟲等造成大量空命中

 7.3.3、解決緩存穿透方案

1)緩存空對象

緩存空對象會有兩個問題

  1. 空值做了緩存,意味着緩存層中存了更多的鍵,需要更多的內存空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類數據設置一個較短的過期時間,讓其自動剔除。
  2. 緩存層和存儲層的數據會有一段時間窗口的不一致,例如過期時間設置爲1分鐘,如果此時存儲層添加了這個數據,那此段時間就會出現緩存層和存儲層數據的不一致,更新數據的時候清除掉緩存層中的空對象。

2)布隆過濾器攔截

如下圖所示,在訪問緩存層和存儲層之前,將存在的 key 用布隆過濾器提前保存起來,做第一層攔截。如果布隆過濾器認爲key不存在,那麼就不會訪問存儲層,在一定程度保護了存儲層。

可以參考: 布隆過濾器,可以利用 Redis 的 Bitmaps 實現布隆過濾器redis bitmaps實現布隆過濾器

 7.3.4、緩存空對象和布隆過濾器方案對比


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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