一、優勢和缺點
優點:
單執行緒
性能高
和
抗并發
缺點:
1.快取和資料庫db雙寫一致性問題
2.快取擊穿
3.快取雪崩
4.快取并發競爭問題
二、為什么會這么快
1、
純記憶體操作:記憶體相對于磁盤有天然的性能優勢,
類似于
HashMap
,
HashMap
的優勢就是查找和操作的時間復雜度都是O(1);
2、
單執行緒:避免過多的背景關系切換開銷;
避免同步機制的開銷(死鎖,鎖粒度等問題);簡單可維護(作者代碼間接性理念)
;
3、
I/O
多路復用
:基于epoll/select/kqueue等I/O多路復用技術,實作高吞吐的網路I/O
4、
資料結構簡單,對資料操作也簡單,
Redis
中的資料結構是專門進行設計的,
使用底層模型不同,它們之間底層實作方式以及與客戶端之間通信的應用協議不一樣,
Redis
直接自己構建了
VM 機制
,因為一般的系統呼叫系統函式的話,會浪費一定的
時間去移動和請求
;
見下圖:
備注:以上epoll evport kqueue對應不同的作業系統復用函式
4、Redis
底層封裝了unix系統的select/epoll/poll等os命令,作為
檔案事件分派器,當redis客戶端進行讀取和寫入操作時,網路io會先經過os,os內部會作為
“中介傳話者”,告訴redis服務器的有哪些socket需要處理,然后redis底層封裝i/o多路復用程式然后分發到不通的
處理器上
Redis的執行流程:
1.客戶端接收用戶的命令請求,比如set key value,對命令請求按照指定的協議格式進行封裝
2.客戶端和服務端建立socket連接
3.服務端采用的是
io多路復用
,底層有select或者poll epoll幾種,服務端可以同時接受多個客戶端發送過來的套接字,會將所有的套接字一個接一個的寫入到
檔案事件派發器
中
(相當于一個佇列)
4.檔案事件派發器
出佇列
交給
檔案事件處理器
去執行(包括連接 命令執行 命令回復等事件處理器)
5.服務端接收到請求的引數后,進行協議的決議后拿到引數后,封裝存盤到該redis client對應的query_buf中包括argv陣列 此時argv[0]=set argv[1]=key argv[2]=value,argc屬性:引數的個數即3 cmd屬性:代表這個操作對應的命令即set對應的redis的命令表(相當于定義好的常量)對應的指標
6.判斷cmd屬性是否為空,為空即不支持該命令;同時判斷cmd指令對應的引數和argc的值是否符合,不符合則說明語法錯誤
7.當語法校驗都成功后,會進行權限,記憶體是否夠用等驗證
8.執行命令,同時會
根據是否開啟aof操作
,慢日志操作,主從同步等操作進行相關資訊存盤
9.最后將命令執行的結果寫入到redisClient對應的
回復緩沖區
最后由
命令回復處理器
來將結果回傳給客戶端比如回傳 \n OK!
10.客戶端接收到回應資料后進行協議格式解析后回傳給用戶,即OK
三、資料型別的使用
在redis中key實際是byte[](string),value資料結構有以下幾種
(一)String(value最大為512M)
常規的set/get操作,value可以是String也可以是數字,一般做
一些復雜的計數功能的快取,
(二)hash
這里value存放的是結構化的物件,方便的就是操作其中的某個欄位,博主在做
單點登錄
的時候,就是用這種資料結構存盤用戶資訊,以cookieId作為key,設定30分鐘為快取過期時間,能很好的模擬出類似session的效果,
(三)list
使用List的資料結構,按照
插入順序排序
,可以
做簡單的
訊息佇列的功能
,也可以利用lrange命令,
做基于redis的分頁功能
,性能極佳,用戶體驗好,
例如:redisTemplate.opsForList().range(key,startindex,endstartindex)
(四)set
因為set堆放的是一堆
不重復值
的集合,所以可以做
全域去重的功能,做一些交并差操作
,為什么不用JVM自帶的Set進行去重?因為我們的系統一般都是集群部署,使用JVM自帶的Set,比較麻煩,難道為了一個做一個全域去重,再起一個公共服務,太麻煩了,
另外,就是利用交集、并集、差集等操作,可以
計算共同喜好,全部的喜好,自己獨有的喜好等功能
,
(五)sorted set(
資料結構:元素個數少于128個并且元素長度小于64位元組時使用壓縮串列,其他使用
跳躍表
)
sorted set多了一個權重引數
score,
集合中的元素能夠按score進行排列,可以做
排行榜應用,取TOP N操作
,另外,參照另一篇
《分布式之延時任務方案決議》
,該文指出了sorted set可以用來做
延時任務
,最后一個應用就是可以做
范圍查找
,
備注:
1、以下為高級資料結構(加分項)
HyperLogLog、Geo、Pub/Sub,
BloomFilter
1,
bloomfilter2
,RedisSearch,Redis-ML
2、跳躍表:鏈表上增加
四、過期策略
定期洗掉+惰性洗掉
定期:默認100ms隨機抽取設定過期時間的key,檢查是否過期,則洗掉
-
周期性輪詢redis庫中的時效性資料,采用隨機抽取的策略,利用過期資料占比的方式控制洗掉頻度
-
特點1:CPU性能占用設定有峰值,檢測頻度可自定義設定
-
特點2:記憶體壓力不是很大,長期占用記憶體的冷資料會被持續清理
-
總結:周期性抽查存盤空間 (隨機抽查,重點抽查)
-
hz 調大將會提高Redis主動淘汰的頻率,如果你的Redis存盤中包含很多冷資料占用記憶體過大的話,可以考慮將這個值調大,但Redis作者建議這個值不要超過100,將這個值調大到100,觀察到CPU會增加2%左右,但對冷資料的記憶體釋放速度確實有明顯的提高(通過觀察keyspace個數和used_memory大小),
timelimit和server.hz是一個倒數的關系,也就是說hz配置越大,timelimit就越小,換句話說是每秒鐘期望的主動淘汰頻率越高,則每次淘汰最長占用時間就越短,這里每秒鐘的最長淘汰占用時間是固定的250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰頻率和每次淘汰的最長時間是通過hz引數控制的
惰性:如果隨機一直不到并且設定了過期時間,則在查詢時發現已經過期回傳空并洗掉該key
如果定期沒有刪,并且一直也沒有查詢,導致很多key:
五、記憶體淘汰機制
-
noeviction策略,不進行資料淘汰的策略
-
allkeys-random 策略,從所有鍵值對中隨機選擇并洗掉資料
-
allkeys-lru 策略,使用 LRU 演算法在所有資料中進行篩選
-
allkeys-lfu 策略,使用 LFU 演算法在所有資料中進行篩選
-
volatile-ttl 在篩選時,會針對設定了過期時間的鍵值對,根據過期時間的先后進行洗掉,越早過期的越先被洗掉
-
volatile-random 在設定了過期時間的鍵值對中,進行隨機洗掉
-
volatile-lru 會使用 LRU 演算法篩選設定了過期時間的鍵值對 【Renault 默認淘汰策略】
-
volatile-lfu 會使用 LFU 演算法選擇設定了過期時間的鍵值對
六、事務
本質是通過MULTI、EXEC、WATCH等一組命令的集合,事務支持一次執行多個命令,一個事務中所有命令都會被序列化,在事務執行程序,會按照順序串行化執行佇列中的命令,其他客戶端提交的命令請求不會插入到事務執行命令序列中,
總結說:redis事務就是
一次性、
順序性、
排他性的執行一個佇列中的一系列命令,
特性:
1、事務不支持回滾,其中一旦失敗,后面繼續執行;
2、事務支持一致性、隔離性(
單行程)和持久性(
僅限開啟aof 持久化模式,
appendfsync引數為
always模型)
3、redis命令支持原子性,但是事務不支持原子性
需要支持事務:
1、lua腳本,redis保證一個腳本下的命令一次性執行完畢;(
推薦)
2、基于中間標記變數,通過外部標記變數來標識事務是否完成,讀取資料時先獲取到外部變數標記是否執行完畢;
七、高可用機制
-
持久化(rdb、aop和兩者混合機制(4.0版本之后))
-
主從復制(主從和從從模式,主掛,手動將從作為主)
-
哨兵機制(自動切換主從服務器,監控)
重點在于高可用,主掛 從主動變主
-
cluster集群(3.0版本以后,多個主從集群,多個主實作資料分片,16384)
重點著重于可擴展性,單個節點記憶體不夠時,
使用Cluster進行分片存盤
RDB(默認)
概念:RDB,redis database,是在
某個時間(默認五分鐘)點將資料寫入一個
臨時檔案(快照),持久化結束后,用這個臨時檔案替換上次持久化的檔案,達到資料恢復,
優點:
fork一個
單獨子行程來進行持久化,主行程不會進行任何IO操作,
保證redis的高性能
缺點:
全量增加,RDB是間隔一段時間進行持久化,如果持久化之間redis發生故障,會發生資料丟失,
應用場景:適合
資料要求不嚴謹的集群,更多適合做冷備份
AOF(
mysql中binlog、
apache.log、zookeeper中txn-log
)
Append-only file,將“
操作 + 資料”以
格式化指令的方式
追加到
操作日志檔案的
尾部(只是追加,沒有磁盤尋址開銷)
執行步驟主要兩步:
第一步:redis執行寫命令時,根據保存模式(
AOF_FSYNC_EVERYSEC)呼叫系統函式write寫入AOF檔案中;
第二步:根據刷盤策略,呼叫系統函式fsync將記憶體資料刷到磁盤上;
保存模式:
-
AOF_FSYNC_NO :不保存,
丟失系統上次一次aof之后的資料
-
AOF_FSYNC_EVERYSEC :每一秒鐘保存一次,后臺子執行緒執行不會阻塞,丟失一秒到兩秒的資料
-
AOF_FSYNC_ALWAYS :每執行一個命令保存一次,保存操作未主執行緒執行,append期間不能接受其他請求,丟失一個命令的時間
刷盤策略(syn配置
):
-
每條指令重繪一次,不會丟失資料
-
定時重繪 每秒一次(
丟失一秒資料)
-
不重繪,根據os機制來操作
AOF Rewrite機制:當aof檔案大小超過配置后,主行程會fork一個子行程重寫當前快取中的資料指令,相當于aof瘦身版本,例如set a=1,set a=2,rewrite后只會記錄最后一次寫指令;在此期間的寫入資訊會存盤主行程的一個aof重寫緩沖區中,rewrite完成之后主行程在將緩沖區資訊寫入到新的aof檔案中;
aof- rewrite- percentage=2 rewrite的比例
aof- rewrite-min size=64M rewrite的最小記憶體大小
備注:當aof檔案大小為64M時,觸發第一次rewrite,rewrite之后檔案大小10M,當aof檔案大小繼續增大至64M時再次rewrite;當rewrite后大小為40M,繼續追加到80M時又一次rewrite;即同時滿足aof檔案大于64M和重寫比例大于原先的兩倍
優點:
缺點:
AOF檔案比RDB檔案大,且
恢復速度慢,由于記錄了所有資料操作歷史記錄
兩者區別:
1、rdb適合做冷備,aop適合做熱備
2、rdb恢復更快,因為只是fork一個行程去操作
3、rdb沒有aof資料全
混合模式(4.0之后)
redis4.0因為減少aof檔案的大小,采取rdb和aof結合的混合模式,即當aof rewrite時,redis先以rdb格式在aof檔案中寫入一個快照,然后在此期間發生的寫操作會append到aof檔案后面,因為rdb檔案是以二進制壓縮形式存盤所以aof檔案會更小些
目的:
為了使在部分節點失敗或者大部分節點無法通信的情況下集群仍然可用,所以集群使用了主從復制模型,每個節點都會有N-1個復制品,
形式:只能有一個主服務器,多個從服務器,分別有讀寫和讀的操作
使用場景:集群,讀寫分離,日志備份,高可用,
更多適合做熱備份
原理步驟:
1、
從服務器
配置指向
主服務器的
ip和埠,啟動時會
向主服務器
發送psync命令包,如果是這個slave第一次連接到master,master就會啟動一個執行緒,生成全量
RDB快照,同時把新的寫請求都快取在
記憶體中,
RDB檔案生成后,master會將這個
RDB發送給slave的,slave會先寫進本地的磁盤,然后加載進記憶體,最后master會把記憶體快取的那些新命名都發給slave,其中傳輸程序中如果斷網或者服務器掛了,會自動重連,并且鏈接后會把缺少的資料補上的;
2、
主服務器收到
寫命令操作時會保存資料快照檔案記錄,當資料快照檔案發生變化時,會同步向從服務器發送通知;
結構:
附錄:
1,
2
架構:
哨兵至少需要 3 個實體,來保證自己的健壯性,
哨兵 + redis 主從的部署架構,是
不保證資料零丟失的,只能保證 redis 集群的
高可用性,
Redis的哨兵(sentinel) 系統用于管理多個 Redis 服務器,該系統執行以下三個任務:
-
監控(Monitoring): 哨兵(sentinel) 會不斷地檢查你的Master和Slave是否運作正常,
-
提醒(Notification):當被監控的某個 Redis出現問題時, 哨兵(sentinel) 可以通過 API 向管理員或者其他應用程式發送通知,
-
自動故障遷移(Automatic failover):當一個Master不能正常作業時,哨兵(sentinel) 會開始一次自動故障遷移操作,它會將失效Master的其中一個Slave升級為新的Master, 并讓失效Master的其他Slave改為復制新的Master; 當客戶端試圖連接失效的Master時,集群也會向客戶端回傳新Master的地址,使得集群可以使用Master代替失效Master,
備注:
-
哨兵(sentinel) 是一個
分布式系統,多個哨兵行程之間使用
流言協議(gossipprotocols)來接收關于Master是否下線的資訊,并使用
投票協議(agreement protocols)來決定是否執行自動故障遷移,以及選擇哪個Slave作為新的Master.
-
主觀認為宕機:每個哨兵(sentinel) 會向其它哨兵(sentinel)、master、slave定時發送訊息,以確認對方是否”活”著,如果發現對方在指定時間(可配置)內未回應,則暫時認為對方已掛;
-
客觀宕機:當
多個sentinel,都報告某一
master沒回應,系統才認為該master"徹底死亡",通過
vote演算法,從剩下的slave節點中,選一臺提升為master,然后自動修改相關配置,
-
官方Redis Cluster 方案(服務端路由查詢)
分布式資料庫:
分布式資料庫指把整個資料庫按照
磁區規則
映射到多個節點上,即把資料劃分到多個節點上,每個節點負責資料的一個子集,
redis如何磁區:
1、集群共有
16384個槽(slot)采用
哈希磁區
詳情2的
虛擬槽磁區,所有key值根據hash函式
(CRC16[key]&16383)映射到0-16383槽內,集群中每個主節點都會負責一部分槽以及該槽所映射的鍵值資料
2、集群中所有節點資訊(ip,port,slot),都會
定期通過節點之間
Gossip
協議交換更新,所以集群中所有的節點都保存這個其他節點資訊
3、當get key時,key不在原先的主節點服務器里,集群內部也會通過
重定向,指向正確的節點上
使用
虛擬槽磁區原因在于:
解耦
資料和節點
的關系,節點自身維護槽映射關系,資料
分布式存盤
集群缺點:
a,鍵的
批量操作支持有限,比如mset, mget,
多個鍵映射在不同的槽不支持批量操作(
pipeline管道操作
)
b, 同一節點資料支持事務,
多個節點之間key不支持事務;
c,鍵是資料磁區的最小粒度,不能將一個很大的鍵值對映射到不同的節點
d,
不支持多資料庫,只有0,select 0
e,主從復制結構只支持單層結構,
不支持樹型結構,
備注:
例如:在某臺節點上執行set name =1234
1.節點先計算卡槽位置:slot = CRC16(name) & 16384 ,比如計算的卡槽值為12000
2、發現槽為12000的分在節點2上,這時節點1會執行重定向命令到節點2上
每一個藍色的圈都代表著一個redis的服務器節點,它們任何兩個節點之間都是相互連通的,客戶端可以與任何一個節點相連接,然后就可以訪問集群中的任何一個節點,對其進行存取和其他操作
集群通信:
1.
節點之間采用
Gossip
協議通信,
Gossip
協議就是指節點彼此之間不斷通信交換資訊
當主從角色變化或新增節點,彼此通過ping/pong
進行通信知道全部節點的最新狀態并達到集群同步
常見協議命令有:
-
meet訊息:用于通知新節點增加,訊息發送者會通知接受者加入到當前集群中,meet訊息結束后,接受節點會加入到集群中,并進行周期性的ping/pong資訊交換
-
ping訊息:集群內
每個節點每秒都會
向其他節點發送ping資訊,資訊包括
自身和其他節點的狀態資訊,用于檢測其他節點是否在線和其他節點的資訊
-
pong訊息:當節點接受到meet和ping訊息后,作為回應,發送自身和其他節點資訊給發送者
-
fail訊息:當某個節點發現集群內部某個節點下線時,會向集群內廣播一個fail訊息
其中訊息主要包括訊息頭,訊息體,訊息頭包含發送者自身資訊包括:
節點id,槽映射,節點角色和是否下線資訊
集群擴容:
準備好新節點,加入集群后,遷移槽和映射的資料
主節點下線:
確定下線節點是否存在槽(是否為主節點),如果有,則先將槽遷移到其他主節點上,保證整個集群槽映射的完整性
如果下線節點為從節點,或者沒有槽時,可以通知集群內其他節點下線該節點
故障轉移:
redis集群實作了高可用,當集群內少量節點出現故障時,通過故障轉移可以保證集群正常對外提供服務,
當集群里某個節點出現了問題,redis集群內的
節點通過ping pong訊息發現節點是否健康,是否有故障,其實主要環節也包括了
主觀下線和
客觀下線;
主觀下線:指某個節點認為另一個節點不可用,即下線狀態,當然這個狀態不是最終的故障判定,只能代表這個節點自身的意見,也有可能存在誤判;
客觀下線:
指真正的下線,集群內多個節點都認為該節點不可用,達成共識,將它下線,如果下線的節點為主節點,還要對它進行故障轉移
假如節點
a
標記節點
b
為主觀下線,一段時間后節點
a
通過訊息把節點
b
的狀態發到其它節點,當節點
c
接受到訊息并決議出訊息體時,會發現節點
b
的也發現
fail
狀態時,會觸發客觀下線流程;
當下線為主節點時,此時
redis
集群會統計持有槽的主節點投票數是否達到一半,當下線報告統計數大于一半時,被標記為客觀下線狀態,
故障恢復:
故障主節點下線后,如果下線節點的是主節點,則需要在它的從節點中選一個替換它,保證集群的高可用;轉移程序如下:
1,資格檢查:檢查該
從節點是否有資格替換故障主節點,如果此從節點與主節點斷開過通信,那么當前從節點不具體故障轉移;
2,準備選舉時間:當從節點符合故障轉移資格后,
更新觸發故障選舉時間,只有到達該時間后才能執行后續流程;
3,發起選舉:當到達故障選舉時間時,進行選舉;
4,選舉投票:只有持
有槽的主節點才有票,會處理故障選舉訊息,投票程序其實是一個領導者選舉(選舉從節點為領導者)的程序,每個主節點只能投一張票給從節點,當
從節點收集到足夠的選票(大于N/2+1)后,觸發替換主節點操作,撤銷原故障主節點的槽,委派給自己,并廣播自己的委派訊息,通知集群內所有節點,