Redis學習筆記
單線程架構
redis使用了單線程架構和I/O多路複用模型來實現高性能的內存數據庫服務
爲什麼單線程還能這麼快
-
純內存訪問,Redis將所有數據放在內存中,內存的響應時長大約爲100ns
-
非阻塞I/O,Redis使用epoll作爲I/O多路複用技術的實現,再加上Redis自身的事件處理模型將epoll中的連接、讀寫、關閉都轉換爲事件,不在網絡I/O上浪費過多時間
-
單線程避免了線程切換和競態產生的消耗
單線程問題:對於每個命令的執行時間有要求,如果某個命令執行過長,會造成其他命令的阻塞,所以Redis是面向快速執行場景的數據庫。
redis數據結構
字符串
內部編碼
-
int 8個字節的長整型
-
embstr:小於等於39個字節的字符串
-
raw:大於39個字節的字符串
redis會根據當前值得類型和長度決定使用哪種內部編碼實現
使用場景
-
緩存功能 合理的健名設計,業務名:對象名:id:【屬性】
-
計數
-
session共享
-
限速
哈希
內部編碼
ziplist(壓縮列表) 當哈希類型元素小於512 、同時所有值都小於64字節時,Redis會使用ziplist作爲哈希的內部實現
hashtable(哈希表)
列表
列表中的元素是有序的 列表中的元素是可以重複的
內部編碼
ziplist ziplist(壓縮列表) 當列表元素小於512 、同時所有值都小於64字節時,Redis會使用ziplist作爲列表的內部實現
linkedlist 當列表類型無法滿足ziplist的條件時,redis會使用linkedlist作爲表的內部實現
使用場景:消息隊列 (lpush+brpop) 文章列表
集合 不允許重複元素 並行集合中的元素是無序的,無法通過索引下標獲取元素,一個集合最多可以存儲2*32-1個元素,集合支持多個結合取交集、並集、差集
內部編碼
intset(整數集合) 當集合中的元素都是整數且元素個數小於512 redis使用inset來作爲集合的內部實現
有序集合 元素不能重複且有序
內部編碼
ziplist 有序集合個數小於128 同時元素的值都笑餘4字節,redis會用ziplist來作爲有序集合的內部實現
使用場景 添加用戶贊數
skiplist
hashtable
使用場景:給用戶添加標籤、給標籤添加用戶、
持久化
RDB持久化:把當前進程數據生成快照保存到硬盤的過程,觸發RDB持久化過程分爲手動觸發和自動車發
手動觸發分別對應save和bgsave命令
save命令:阻塞當前redis服務器,直到RDB過程完成爲止
bgsave命令:redis進程執行fork操作創建子進程,RDB持久化由子進程負責,完成後自動結束,阻塞只發生在fork階段,一般時間很短
RDB優點:
-
RDB是一個緊湊壓縮的二進制文件,非常適用於備份,全量複製等場景
-
redis加載RDB恢復數據遠遠快於AOF的方式
RDB缺點:
RDB方式數據沒辦法做到實時持久化/秒級持久化
存在老版本redis服務無法兼容新版RDB格式的問題
AOF(append only file):以獨立日誌的方式記錄每次寫命令,重啓時在重新執行AOF文件中的命令達到恢復數據的目的
AOF工作流程操作:命令寫入(append)、文件同步(sync)、文件重寫(rewrite)、重啓加載(load)
AOF命令寫入的內容直接是文本協議格式
AOF緩衝區同步文件策略
-
always
-
everysec
-
no
redis複製
建立複製
配置複製的方式
-
在配置文件中加入slaveof{masterHost}{masterPort}隨redis啓動生效
-
在redis-server啓動命令後加入slaveof{masterHost}{masterPort}隨redis啓動生效
-
直接使用命令slaveof{masterHost}{masterPort}隨redis啓動生效生效
斷開復制:slaverof no one命令
斷開復制流程:
斷開與主節點複製關係
從節點晉升爲主節點
傳輸延遲
repl-disable-tcp-nodelay參數用於控制是否關閉TCP_NODELAY
複製拓撲結構
-
一主一從
-
一主多從
-
樹狀主從
複製過程
-
保存主節點信息
-
主從建立socket鏈接
-
發送ping命令
-
權限驗證
-
同步數據集 使用psync命令完成主從數據同步,同步過程分爲全量複製和部分複製 全量複製:一般用於初次複製場景 部分複製:用於處理在主從複製中因網絡閃斷等原因造成的數據丟失場景,當從節點再次連上主節點後,如果條件允許,主節點會補發丟失數據給從節點
-
命令持續複製
複製風暴:
大量從節點對同一主節點或者對同一臺機器的多個主節點短時間發起全量複製的過程。規避複製風暴的方式有如下幾個:
單主節點複製風暴:減少主節點掛載的從節點數量,或者採用樹狀複製結構,加入中間層從節點來保護從節點
單機器複製風暴:把主節點儘量分散在多臺機器上,避免在單臺機器部署過多的主節點
redis內存消耗劃分
自身內存、對象內存、緩衝內存、內存碎片
緩衝內存包括:客戶端緩衝、複製積壓緩衝區、AOF緩衝區
AOF緩衝區:用於在Redis重寫期間保存最近的寫入命令
複製積壓緩衝區:Redis在2.8版本之後提供了一個可重用的固定大小緩衝區用於實現部分複製功能。
redis內存管理
限制內存目的:
-
用於緩存場景,當超出內存上限時使用LRU等刪除策略釋放內存空間
-
防止所用內存超過物理內存空間
內存回收策略
惰性刪除:用於客戶端讀取帶有超時屬性的健,如果已經超過健設置的過期時間,會執行刪除操作並返回空,這種方式存儲內存泄漏的問題
定式任務刪除:Redis內部維護一個定時任務,默認每秒運行10次,定時任務刪除過期健邏輯採用了自適應算法,根據健的過期比例、使用快慢兩種速率模式回收健
內存溢出控制策略:redis所用內存達到maxmemory上限時會觸發相應的溢出控制策略
-
noevicition:默認策略,不會刪除任何數據,拒絕所有寫入策略,只響應讀操作
-
volatile-lru:根據LRU算法刪除設置了超時屬性(expire)的健,直到騰出足夠空間爲止,如果沒有可刪除的健對象,回退noeviction策略
-
allkey-lru:根據LRU算法刪除鍵,不管數據有沒有設置超時屬性,直到騰出足夠空間爲止
-
allkeys-random:隨機刪除所有鍵,知道騰出足夠空間爲止
-
volatile-random:隨機刪除過期鍵,知道騰出足夠空間爲止
-
vloatile-ttl:根據鍵值對象的ttl屬性,刪除最近將要過期的數據,如果沒有,回退noevicition策略
redis內存優化
redis存儲的所有值對象在內部定義爲redisObject結構體
type字段:表示當前對象使用的數據類型
enoding:表示redis內部編碼類型
lru字段:記錄對象最後一次被訪問的時間
refcount字段:記錄當前對象被引用的次數,用於通過引用次數回收內存
*ptr字段:與對象的數據內容相關,如果是整數,直接存儲數據;否則表示指向數據的指針。
優化建議:
縮減鍵和值的長度,在完整面熟業務的情況下,鍵值越短越好,value長度:把業務對象序列化成二進制數組放入redis。
使用共享對象池
字符串優化:
儘量減少字符串頻繁修改操作如append、setrange,改爲直接使用set修改字符串,降低預分配帶來的內存浪費和內存碎化
字符串重構,像json這樣的數據可以使用hash結構,
字符串結構:redis自己實現了字符串結構,內部簡單動態字符串
特點:
O(1)時間複雜度獲取:字符串長度、已用長度、未用長度
可用於保存字節數組,支撐安全的二進制數據存儲
內部實現空間預分配機制,降低內存再分配次數
惰性刪除機制,字符串縮減後的空間不釋放,作爲預分配空間保留
編碼優化
編碼類型轉換在redis寫入數據時自動完成,這個轉換過程是不可逆,轉換規則只能從小內存編碼向大內存編碼轉換
控制鍵的數量
redis sentinel
主從複製優點:
從節點可以擴展主節點的讀能力
從節點作爲主節點的備份,一旦主節點出現故障不可達的情況,從節點可以作爲後備份頂上來,,並且保證數據儘量不丟失(主從複製是最終一致性)
缺點:
一旦主節點出現故障,需要手動將一個從節點晉升爲主節點,同時需要修改應用方的主節點地址,還需要命令其他從節點去複製新的主節點,整個過程需要人工干預
主節點的寫能力受到單機限制
主節點的存儲能力受到單機限制
當主節點出現故障時,Redis Sentinel能自動完成故障發現和故障轉移,並通知應用方,從而實現真正的高可用
Redis Sentinel功能:
監控:Sentinel節點會定期檢測Redis數據節點、其餘Sentinel節點是否可達
通知:Sentinel節點會定期檢測Redis數據節點、其餘Sentinel節點是否可達
通知:Sentinel節點會將故障轉移的結果通知給應用發方
主節點故障轉移:實現從節點晉升爲主節點並維護後續正確的主從關係
配置提供者:在Redis Sentinel結構中,客戶端在初始化的時候鏈接的是Sentinel節點集合,從中獲取主節點信息
實現原理
三個定時任務
每隔10秒,每個Sentinel節點會向主節點和從節點發送info命令獲取最新的拓撲結構
每隔2秒,每個Sentinel節點向Redis數據節點的_sentinel_:hello頻道上發送改Sentinel節點對於主節點的判斷以及當前Sentinel節點的信息,同時每個Sentinel節點也會訂閱該頻道來了解其他Sentinel節點以及他們對主節點的判斷,這個定時任務完成兩個工作
發現新的sentinel節點
Sentinel節點之間交換主節點的狀態,作爲後面客觀下線以及領導者選舉的依據
每隔1秒,每個Sentiinel節點會向主節點、從節點、其餘sentinel節點發送一天ping命令做一次心態檢測,來確定這些節點是否可達
客觀下線主觀下線
領導者sentinel節點選舉
Redis使用Raft算法實現領導者選舉,過程如下
每個在線的Sentinel節點都有資格成爲領導者,當它確認主觀節點主觀下線時候,會想其他Sentinel節點發送Sentinel is-master-down-by-addr命令,要求將自己設置爲領導者
收到命令的Sentinel節點,如果沒有同意過其他Sentinel節點的sentinel-is-master-down-by-addr命令,將同意該請求,否則拒絕
如果該Sentinel節點發現自己的票數已經大於等於max(quorum,num(sentinels)/2+1),那麼它將成爲領導者
如果此過程沒有選舉出其他領導者,將進入下一次選舉
故障轉移具體步驟
-
在從節點列表中選出一個節點作爲新的主節點,選擇方法如下
-
過濾"不健康"(主觀下線、斷線)\5秒內沒有回覆過Sentinel節點ping響應、與主節點失聯超過down-afte-milliseconds*10秒
-
選擇slave-priority(從節點優先級)最高的從節點列表,如果存在則返回,不存在則繼續
-
選擇複製偏移量最大的從節點(複製的最完整),如果存在則返回,不存在則繼續
-
選擇runid最小的從節點
-
-
Sentinel領導者會對一地不選出來的從節點執行slaveof no one命令讓其成爲主節點
-
Sentinel領導者節點會向剩餘的從節點發送命令,讓他們成爲新主節點的從節點,複製規則和parallel-syncs參數有關
-
Sentinel節點集合會將原來的主節點更新爲從節點,並保持對其關注,當其恢復後命令它去複製新的主節點