乾貨分享!redis原理總結

概述

總結一下redis的原理,適合大家以後裝逼!

乾貨分享!redis原理總結


redis單點吞吐量

單點TPS達到8萬/秒,QPS達到10萬/秒。

redis的5種存儲類型

string、list、set、map(hash)、stored-set

redis的string類型

1、能表達3種類型:字符串、整數和浮點數。根據場景相互間自動轉型,並且根據需要選取底層的承載方式

2、value內部以int、sds作為結構存儲。int存放整型數據,sds存放字節/字符串和浮點型數據

redis的list類型

1、list類型的value對象內部以linkedlist或ziplist承載。當list的元素個數和單個元素的長度較小時,redis會採用ziplist實現以減少內存佔用,否則採用linkedlist結構

2、linkedlist內部實現是雙向鏈表。在list中定義了頭尾元素指針和列表的長度,是的pop/push操作、llen操作的複雜度為O(1)。由於是鏈表,lindex類的操作複雜度仍然是O(N)

3、ziplist的內部結構

所有內容被放置在連續的內存中。其中zlbytes表示ziplist的總長度,zltail指向最末元素,zllen表示元素個數,entry表示元素自身內容,zlend作為ziplist定界符

rpush、rpop、llen,複雜度為O(1);lpush/pop操作由於涉及全列表元素的移動,複雜度為O(N)

redis的map類型

1、map又叫hash。map內部的key和value不能再嵌套map了,只能是string類型:整形、浮點型和字符串

2、map主要由hashtable和ziplist兩種承載方式實現,對於數據量較小的map,採用ziplist實現

3、hashtable內部結構

主要分為三層,自底向上分別是dictEntry、dictht、dict

dictEntry:管理一個key-value對,同時保留同一個桶中相鄰元素的指針,一次維護哈希桶的內部連

dictht:維護哈希表的所有桶鏈

dict:當dictht需要擴容/縮容時,用於管理dictht的遷移

redis是單線程處理請求,遷移和訪問的請求在相同線程內進行,所以不會存在併發性問題

4、ziplist內部結構

和list的ziplist實現類似。不同的是,map對應的ziplist的entry個數總是2的整數倍,奇數存放key,偶數存放value

redis的set類型

1、set以intset或hashtable來存儲。hashtable中的value永遠為null,當set中只包含整數型的元素時,則採用intset

2、intset的內部結構

2.1、核心元素是一個字節數組,從小到大有序存放著set的元素

2.2、由於元素有序排列,所以set的獲取操作採用二分查找方式實現,複雜度O(log(N))。進行插入時,首先通過二分查找得到本次插入的位置,再對元素進行擴容,再將預計插入位置之後的所有元素向右移動一個位置,最後插入元素,插入複雜度為O(N)。刪除類似

redis的sorted-set類型

1、類似map是一個key-value對,但是有序的。value是一個浮點數,稱為score,內部是按照score從小到大排序

2、內部結構以ziplist或skiplist+hashtable來實現

redis通過watch機制實現樂觀鎖流程

1、將本次事務涉及的所有key註冊為觀察模式

2、執行只讀操作

3、根據只讀操作的結果組裝寫操作命令併發送到服務器端入隊

4、發送原子化的批量執行命令EXEC試圖執行連接的請求隊列中的命令

5、如果前面註冊為觀察模式的key中有一個貨多個,在EXEC之前被修改過,則EXEC將直接失敗,拒絕執行;否則順序執行請求隊列中的所有請求

6、redis沒有原生的悲觀鎖或者快照實現,但可通過樂觀鎖繞過。一旦兩次讀到的操作不一樣,watch機制觸發,拒絕了後續的EXEC執行

redis的持久化機制

redis主要提供了兩種持久化機制:RDB和AOF;

1、RDB

默認開啟,會按照配置的指定時間將內存中的數據快照到磁盤中,創建一個dump.rdb文件,redis啟動時再恢復到內存中。

redis會單獨創建fork()一個子進程,將當前父進程的數據庫數據複製到子進程的內存中,然後由子進程寫入到臨時文件中,持久化的過程結束了,再用這個臨時文件替換上次的快照文件,然後子進程退出,內存釋放。

需要注意的是,每次快照持久化都會將主進程的數據庫數據複製一遍,導致內存開銷加倍,若此時內存不足,則會阻塞服務器運行,直到複製結束釋放內存;都會將內存數據完整寫入磁盤一次,所以如果數據量大的話,而且寫操作頻繁,必然會引起大量的磁盤I/O操作,嚴重影響性能,並且最後一次持久化後的數據可能會丟失;

2、AOF

以日誌的形式記錄每個寫操作(讀操作不記錄),只需追加文件但不可以改寫文件,redis啟動時會根據日誌從頭到尾全部執行一遍以完成數據的恢復工作。包括flushDB也會執行。

主要有兩種方式觸發:有寫操作就寫、每秒定時寫(也會丟數據)。

因為AOF採用追加的方式,所以文件會越來越大,針對這個問題,新增了重寫機制,就是當日志文件大到一定程度的時候,會fork出一條新進程來遍歷進程內存中的數據,每條記錄對應一條set語句,寫到臨時文件中,然後再替換到舊的日誌文件(類似rdb的操作方式)。默認觸發是當aof文件大小是上次重寫後大小的一倍且文件大於64M時觸發;

3、當兩種方式同時開啟時,數據恢復redis會優先選擇AOF恢復。一般情況下,只要使用默認開啟的RDB即可,因為相對於AOF,RDB便於進行數據庫備份,並且恢復數據集的速度也要快很多。

4、開啟持久化緩存機制,對性能會有一定的影響,特別是當設置的內存滿了的時候,更是下降到幾百reqs/s。所以如果只是用來做緩存的話,可以關掉持久化。

redis集群(redis cluster)

1、redis3以後,節點之間提供了完整的sharding(分片)、replication(主備感知能力)、failover(故障轉移)的特性

2、配置一致性:每個節點(Node)內部都保存了集群的配置信息,存儲在clusterState中,通過引入自增的epoch變量來使得集群配置在各個節點間保持一致

3、sharding數據分片

將所有數據劃分為16384個分片(slot),每個節點會對應一部分slot,每個key都會根據分佈算法映射到16384個slot中的一個,分佈算法為slotId=crc16(key)%16384

當一個client訪問的key不在對應節點的slots中,redis會返回給client一個moved命令,告知其正確的路由信息從而重新發起請求。client會根據每次請求來緩存本地的路由緩存信息,以便下次請求直接能夠路由到正確的節點

分片遷移:分片遷移的觸發和過程控制由外部系統完成,redis只提供遷移過程中需要的原語支持。主要包含兩種:一種是節點遷移狀態設置,即遷移錢標記源、目標節點;另一種是key遷移的原子化命令

4、failover故障轉移

故障發現:節點間兩兩通過TCP保持連接,週期性進行PING、PONG交互,若對方的PONG相應超時未收到,則將其置為PFAIL狀態,並傳播給其他節點

故障確認:當集群中有一半以上的節點對某一個PFAIL狀態進行了確認,則將起改為FAIL狀態,確認其故障

slave選舉:當有一個master掛掉了,則其slave重新競選出一個新的master。主要根據各個slave最後一次同步master信息的時間,越新表示slave的數據越新,競選的優先級越高,就更有可能選中。競選成功之後將消息傳播給其他節點。

5、集群不可用的情況:

集群中任意master掛掉,且當前master沒有slave。

集群中超過半數以上master掛掉。


上面很多內容毫無意義,平時不會怎麼用到,也記不住,但是卻是大家需要了解的...

說句實在,還是值得收藏的!

後期會分享更多運維DBA和devops內容,感興趣的朋友可以關注下!

乾貨分享!redis原理總結


分享到:


相關文章: