Redis相關

Redis

導航

  • 一. Redis簡介
  • 二. Redis常用的數據類型
  • 三. 如何通過Redis實現分佈式鎖
  • 四. Redis的持久化方式–RDB
  • 五. 持久化方式-- AOF及混合模式
  • 六. Pipeline及主從同步
  • 七. Redis的集羣原理

一. Redis簡介

1.1 主流應用架構

在這裏插入圖片描述

解讀: 首先使用緩存可以快速查找所需要的內容,不必每次都請求數據庫;1-2則表示直接查詢緩存;如果緩存中沒有的話,就從數據庫中查詢到內容,然後返回存入Redis中再返回給客戶端,這樣下次請求此數據的時候就可以直接從緩存中查詢了–>3,4,5,6 ; 而熔斷7,8則表示直接對外提供一個服務,當存儲器掛掉了,那麼也可以直接從緩存中取值,然後直接返回結果;

1.2 緩存中間件–Memcache和Redis的區別?

  • Memcache: 代碼層次類似Hash

    • 支持簡單數據類型
    • 不支持數據持久化存儲
    • 不支持主從
    • 不支持分片
  • Redis:

    • 數據類型豐富
    • 支持數據磁盤持久化存儲
    • 支持主從

1.3 爲什麼Redis能這麼快?

  • 完全基於內存,絕大部分請求是純粹的內存操作,執行效率高;
  • 數據結構簡單,對數據操作也簡單;
  • 採用單線程,單線程也能處理高併發請求,想多核也可啓動多實例
  • 使用多路I/O複用模型,非阻塞IO
    • Redis採用I/O多路複用函數: epoll/kqueue/evport/select?
      • 因地制宜
      • 優先選擇時間複雜度爲O(1)的I/O多路複用函數作爲底層實現
      • 以時間複雜度爲O(n)的select 作爲保底
      • 基於react設計模式監聽I/O事件-

二. Redis常用的數據類型

2.1 常用數據類型

  • String : 最基本的數據類型,二進制安全
  • Hash: String元素組成的字典,適合用於存儲對象
  • List: 列表,按照String 元素插入順序排序
  • Set:String元素組成的無序集合,通過哈希表實現,不允許重複
  • Sorted Set: 通過分數來爲集合中的成員進行從小到大的排序
  • 用於計數的HyperLogLog,用於支持存儲地理位置信息的Geo等;

存入相同的key,但value不同的時候,會被重寫,value值爲最新輸入的value值;

2.2 從海量Key裏查詢出某一固定前綴的Key

  • 留意細節:
    • 摸清數據規模,即問清楚邊界
  • KEYS pattern: 查找所有給定模式pattern的Key
    • KEYS指令一次性返回所有匹配的key
    • 鍵的數量過大會使服務卡頓
  • SCAN cursor [MATCH pattern] [COUNT count]
    • 基於遊標的迭代器,需要基於上一次的遊標延續之前的迭代過程
    • 以0作爲遊標開始一次新的迭代,直到命令返回遊標0完成一次遍歷
    • 不保證每次執行都返回某個給定數量的元素,支持模糊查詢
    • 一次返回的數量不可控,只能是大概率符合count參數

可能會獲取到重複數據,需要每次添加新的遊標

三. 如何通過Redis實現分佈式鎖

3.1 分佈式鎖需要解決的問題

  • 互斥性
  • 安全性
  • 死鎖
  • 容錯

3.2 分佈式鎖的嘗試:

在這裏插入圖片描述

可以使用setnx來實現分佈式鎖;當獲取到鎖的時候返回1,沒有獲取到則返回0,通過get還能確定是否爲這個鎖;

  • 如何解決SETNX長期有效的問題:
    • EXPIRE key seconds
      • 設置key的生存時間,當key過期時(生存時間爲0),會被自動刪除
    • 缺點:
      • 原子性得不到滿足
RedisService redisService= SpringUtils.getBean(RedisService.class);
long status=redisService.setnx(key,"1");
if(status==1){
	redisService.expire(key,expire);
	//執行獨佔資源邏輯
	doOcuppiedWork();
}

使用過期時間,可以當一個線程獲取到了鎖之後,給它設置一個過期時間,在這個時間內執行方法;過期後就相當於釋放鎖;
存在的風險: 在設置過期時間前被掛掉的話,這個線程可能就會一直佔用此線程

3.3 分佈式鎖的優化:

  • SET key value [EX seconds] [PX milliseconds] [NX|XX]

    • EX second: 設置鍵 的過期時間爲second 秒
    • PX millisecond: 設置鍵的過期時間爲millisecond 毫秒
    • NX: 只在鍵不存在時,纔對鍵進行設置操作
    • XX: 只在鍵已經存在時,纔對鍵進行設置操作
    • SET 操作成功完成時,返回OK,否則返回nil
  • 操作代碼如下:

RedisService redisService =SpringUtils.getBean(RedisService.class);
String result=redisService.set(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);
if("OK",equals(result)){
	//執行獨佔資源邏輯
	doOcuppiedWork()
}

3.4 大量的Key同時過期的注意事項

  • 集中過期,由於清除大量的Key很耗時,會出現短暫的卡頓現象
    • 解決方案: 在設置key的過期時間的時候,給每個Key加上隨機值;

3.5 如何使用Reids做異步隊列

  • 使用List作爲隊列,PRUSH生產消息,LPOP消費消息

    • 缺點:沒有等待隊列裏有值就直接消費
    • 彌補:可以通過在應用層引入Sleep機制去調用LPOP重試
  • BLPOP key [key…] timeout: 阻塞直到隊列有消息或者超時

    • 缺點:只能供一個消費者消費
  • pub/sub: 主題訂閱模式:

    • 發送者[pub]發送消息,訂閱者(sub)接收消息

    • 訂閱者可以訂閱任意數量的頻道

    • 圖示:
      在這裏插入圖片描述

Redis Client1:
subscribe myTopic

Redis Client2: 
subscribe myTopic

Redis Client3:
subscribe anotherTopic

Redis Client4:
publish myTopic "Hello"


//publish這個返回值爲訂閱者數量,在這裏面clent4推送的消息只有1,2能收到,3因爲訂閱的內容不同,所以無法收到

消息的發佈是無狀態的,無法保證可達

四. Redis的持久化方式–RDB

4.1 RDB持久化

  • RDB(快照)持久化: 保存某個時間點的全量數據快照

    • SAVE:阻塞Redis的服務器進程,直到RDB文件被創建完畢
    • BGSAVE: Fork出一個子進程來創建RDB文件,不阻塞服務器進程
  • 手動觸發操作:

ls dump.rbd				# 顯示dump.rdb
rm -f dump.rdb			# 刪除所有dump.rdb
./redis-cli				#連接Redis客戶端
save					#保存RDB持久化   會卡頓一段時間
exit					#退出
ls dump.rbd				...
rm-f dump.rdb			...
ls dump.rbd				# 已經被刪除了,會提示:No such file or directory
./redis-cli				...
lastsave				# 快照最後一次保存的時間
bgsave					# 保存RDB持久化[單獨開一個線程]  提示: Background saving started    不會卡頓
lastsave				# 最後一次的執行時間,這裏會被更新;因爲使用了bgsave
exit
  • 自動化觸發RDB持久化的方式
    • 根據redis.conf 配置裏的SAVE m n 定時觸發(用的是BGSAVE)
    • 主從複製時,主節點自動觸發
    • 執行Debug Reload
    • 執行Shutdown 且沒有開啓AOF持久化

4.2 BGSAVE原理

  • 示意圖:
    在這裏插入圖片描述

系統調用fork(): 創建進程,實現了Copy-on-Write

4.3 Copy-on-Write

  • 概述:
    • 如果有多個調用者同時要求相同資源(如內存或磁盤上的數據存儲),他們會共同獲取相同的指針指向相同的資源,直到某個調用者試圖修改資源的內容時,系統纔會真正賦值一份專用副本給該調用者,而其他調用者所見到的最初的資源仍然保持不變;

4.4 RDB 持久化的缺點:

  • 內存數據的全量同步,數據量大會由於I/O而嚴重影響性能
  • 可能會因爲Redis掛掉而丟失從當前至最近一次快照期間的數據

五. 持久化方式-- AOF及混合模式

5.1 概述:

  • AOF(Append-Only-File)持久化: 保存寫狀態
    • 記錄下除了查詢以外的所有變更數據庫狀態的指令
    • 以append的形式追加保存到AOF文件中(增量)

appendonly.aof[默認名字] 它是默認關閉的
vim redis.conf -> 將appendonly no 改爲appendonly yes

ls appendonly.aof			# 判斷是否已經存在,如果不存在則繼續,假設不存在
config set appendonly yes
set aofTest "hehe"
exit
ls appendonly.aof			# 這時已經能正常顯示了;

5.2 日誌重寫解決AOF文件大小不斷增大的問題,原理如下;

  • 調用fork(),創建一個子進程
  • 子進程把新的AOF寫到一個臨時文件裏,不依賴原來的AOF文件
  • 主進程持續將新的變動同時寫到內存和原來的AOF裏
  • 主進程獲取子進程重寫AOF的完成信號,往新AOF同步增量變動
  • 使用新的AOF文件替換掉舊的AOF文件

5.3 RDB和AOF文件共存情況下的恢復流程:

在這裏插入圖片描述

5.4 RDB和AOF的優缺點

  • RDB

    • 優點:全量數據快照,文件小,恢復快
    • 缺點: 無法保存最近一次快照之後的數據
  • AOF:

    • 優點:可讀性高,適合保存增量數據,數據不易丟失
    • 缺點: 文件體積大,恢復時間長

5.5 RDB-AOF混合持久化方式:

  • 概述:
    • 細細想來aofrewrite時也是先寫一份全量數據到新AOF文件中再追加增量只不過全量數據是以redis命令的格式寫入。那麼是否可以先以RDB格式寫入全量數據再追加增量日誌呢這樣既可以提高aofrewrite和恢復速度也可以減少文件大小還可以保證數據的完畢性整合RDB和AOF的優點那麼現在4.0實現了這一特性——RDB-AOF混合持久化。
  • 方式:BGSAVE做鏡像全量持久化,AOF做增量持久化
  • 操作: 通過aof-use-preamble配置項可以打開混合開關

六. Pipeline及主從同步

6.1 使用Pipeline的好處:

  • Pipeline和Linux的管道類似
  • Redis基於請求/響應模型,單個請求處理需要一一應答
  • Pipeline批量執行指令,節省多次IO往返的時間
  • 有順序依賴的指令建議分批發送

6.2 Redis全同步過程:

  • Salve發送sync命令到Master
  • Master啓動一個後臺進程,將Redis中的數據快照保存到文件中
  • Master 將保存數據快照期間接收到的寫命令緩存起來
  • Master完成寫文件操作後,將該文件發送給Salve
  • 使用新的AOF文件替換掉舊的AOF文件
  • Master將這期間收集的增量寫命令發送給Salve端;

6.3 增量同步過程

  • Master 接收到用戶的操作指令,判斷是否需要傳播到Slave
  • 將操作記錄追加到AOF文件
  • 將操作傳播到其他Slave: 1. 對其主從庫 2. 往響應緩存寫入指令 3. 將緩存中的數據發送給Slave

6.4 解決主從同步Master宕機後的主從切換問題:

  • 監控: 檢查主從服務器是否運行正常
  • 提醒: 通過API向管理員或者其他應用程序發送故障通知
  • 自動故障遷移: 主從切換

6.5流言協議:Gossip

  • 在雜亂無章中尋求一致:
    • 每個節點都隨機地與對方通信,最終所有節點的狀態達成一致
    • 種子節點定期隨機向其他節點發送節點列表以及需要傳播的消息
    • 不保證信息一定會傳遞給所有節點,但是最終會趨於一致

七. Redis的集羣原理

7.1 如何從海量數據裏快速找到所需?

  • 分片:按照某種規則去劃分數據,分散存儲在多個節點上
  • 常規的按照哈希劃分無法實現節點的動態增減

7.2 一致性哈希算法:

  • 概述: 對2^32取模,將哈希值空間組織成虛擬的圓環
  • 方式: 將數據key使用相同的函數Hash計算出哈希值:
    在這裏插入圖片描述

好處: 一個節點宕機後,其他的Node節點不會受到影響

7.3 Hash環的數據傾斜問題

  • 概述: 當節點數比較少的時候,比如只有2個的時候,當數據計算出來都存入了節點A,只有較小部分的數據存入了節點B,則會造成數據傾斜,數據沒有平均的分配到各節點上,若A節點宕機則也會造成較大的影響;
  • 解決: 引入虛擬節點解決數據傾斜的問題:
    在這裏插入圖片描述

引入了虛擬節點,將本來只有A、B兩個節點變成了A#1,A#2,A#3,B#1,B#2,B#3等六個節點,解決了節點少而造成數據分佈不均勻的問題;

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