1、主從集群
1、構建主從集群
單節點Redis的并發能力是有上限的,要進一步提高Redis的并發能力,就需要搭建主從集群,實現讀寫分離。主寫從讀,主可以讀也可以寫,從只能讀
利用docker-compose文件來構建主從集群:
步驟:
- 首先通過docker-compose文件來構建容器,導入資料中準備的文件以及tar包。
- 使用docker load -i redis.tar命令將tar包加載成鏡像。
- 執行docker compose up -d來創建并啟動所有容器。 -d代表在后臺。此時已經建好集群但沒有關聯。
由于采用的是host模式,我們看不到端口映射。不過能直接在宿主機通過ps命令查看到Redis進程:ps -ef | grep redis。
輸入docker exec -it r1 redis-cli --port 7001進入到容器內
docker exec -it 容器名 bash?
進入容器? -it (添加一個可輸入終端,可以交互了)容器名? bash(表示命令行交互類型)? ? 輸入exit 退出
- 此時可以輸入?info replication會發現角色是master(role:master),同理進入r2,r3都是master
- 分別進入r2,r3輸入?SLAVEOF 192.168.100.128 7001,其就會變成“從”(7001就是r1的端口,相當于就是認r1為主)
- 至此主從集群搭建成功,可以使用:
set num 123get num
來測試r1,r2,r3是否可以讀寫。
2、主從同步原理
當主從第一次同步連接或斷開連接時,從節點都會發送psyn請求,嘗試數據同步:
replicationID:每一個master節點都有自己的唯一id,簡稱replid。當一個節點變成另一個節點的從節點時,他們的replid會改變,并且一樣,這樣就可以通過判斷replid是否相同來判斷是不是第一次來。
offset:repl_backlog中寫入過的數據長度,寫操作越多,offset值越大,主從的offset一致代表數據一致。
主從集群優化:
- 在master中配置repl-diskless-sync yes啟用無磁盤復制,避免全量同步時的磁盤IO。
- Redis單節點上的內存占用不要太大,減少RDB導致的過多磁盤IO。
- 適當提高repl_backlog的大小,發現slave宕機時盡快實現故障恢復,盡可能避免全量同步。
- 限制一個master上的slave節點數量,如果實在是太多slave,則可以采用主-從-從鏈式結構,減少master壓力。
二、哨兵集群
1、哨兵原理
Redis提供了哨兵機制來實現主從集群的自動故障恢復。哨兵的具體作用如下:
狀態監控:
Sentinel
會不斷檢查您的master
和slave
是否按預期工作故障恢復(failover):如果
master
故障,Sentinel
會將一個slave
提升為master
。當故障實例恢復后會成為slave
狀態通知:
Sentinel
充當Redis
客戶端的服務發現來源,當集群發生failover
時,會將最新集群信息推送給Redis
的客戶端
服務狀態監控
Sentinel
基于心跳機制監測服務狀態,每隔1秒向集群的每個節點發送ping命令,并通過實例的響應結果來做出判斷:
主觀下線(sdown):如果某sentinel節點發現某Redis節點未在規定時間響應,則認為該節點主觀下線。
客觀下線(odown):若超過指定數量(通過
quorum
設置)的sentinel都認為該節點主觀下線,則該節點客觀下線。quorum值最好超過Sentinel節點數量的一半,Sentinel節點數量至少3臺。
選舉新的master
一旦發現master故障,sentinel需要在salve中選擇一個作為新的master,選擇依據是這樣的:
首先會判斷slave節點與master節點斷開時間長短,如果超過
down-after-milliseconds * 10
則會排除該slave節點然后判斷slave節點的
slave-priority
值,越小優先級越高,如果是0則永不參與選舉(默認都是1)。如果
slave-prority
一樣,則判斷slave節點的offset
值,越大說明數據越新,優先級越高最后是判斷slave節點的
run_id
大小,越小優先級越高(通過info server可以查看run_id
)。
實現故障轉移
假如master發生故障,slave1當選。則故障轉移的流程如下:
1)sentinel
給備選的slave1
節點發送slaveof no one
命令,讓該節點成為master
2)sentinel
給所有其它slave
發送slaveof 192.168.150.101 7002
命令,讓這些節點成為新master
,也就是7002
的slave
節點,開始從新的master
上同步數據。
3)最后,當故障節點恢復后會接收到哨兵信號,執行slaveof 192.168.150.101 7002
命令,成為slave
:
2、搭建哨兵集群
參考講義
三、分片集群
主從模式可以解決高可用、高并發讀的問題。但依然有兩個問題沒有解決:
海量數據存儲
高并發寫
要解決這兩個問題就需要用到分片集群了。分片的意思,就是把數據拆分存儲到不同節點,這樣整個集群的存儲數據量就更大了。
Redis分片集群的結構如圖:
分片集群特征:
集群中有多個master,每個master保存不同分片數據 ,解決海量數據存儲問題
每個master都可以有多個slave節點 ,確保高可用
master之間通過ping監測彼此健康狀態 ,類似哨兵作用
客戶端請求可以訪問集群任意節點,最終都會被轉發到數據所在節點
搭建分片集群參考講義
四、Redis數據結構
1、RedisObject
Redis中的任意數據類型的鍵和值都會被封裝為一個RedisObject,也叫做Redis對象,源碼如下:
Redis中會根據存儲的數據類型不同,選擇不同的編碼方式,共包含12種不同類型:
Redis中的5種不同的數據類型采用的底層數據結構和編碼方式如下:
2、SkipList
SkipList(跳表)首先是鏈表,但與傳統鏈表相比有幾點差異:
元素按照升序排列存儲
節點可能包含多個指針,指針跨度不同。
傳統鏈表只有指向前后元素的指針,因此只能順序依次訪問。如果查找的元素在鏈表中間,查詢的效率會比較低。而SkipList則不同,它內部包含跨度不同的多級指針,可以讓我們跳躍查找鏈表中間的元素,效率非常高。
其結構如圖:
3、SortedSet
SorteSet數據結構的特點是:
- 每組數據都包含score和member
- member唯一
- 可根據score排序
SortedSet底層既有hash又有SkipList
面試題:Redis的
SortedSet
底層的數據結構是怎樣的?答:SortedSet是有序集合,底層的存儲的每個數據都包含element和score兩個值。score是得分,element則是字符串值。SortedSet會根據每個element的score值排序,形成有序集合。
它支持的操作很多,比如:
根據element查詢score值
按照score值升序或降序查詢element
要實現根據element查詢對應的score值,就必須實現element與score之間的鍵值映射。SortedSet底層是基于HashTable來實現的。
要實現對score值排序,并且查詢效率還高,就需要有一種高效的有序數據結構,SortedSet是基于跳表實現的。
加分項:因為SortedSet底層需要用到兩種數據結構,對內存占用比較高。因此Redis底層會對SortedSet中的元素大小做判斷。如果元素大小小于128且每個元素都小于64字節,SortedSet底層會采用ZipList,也就是壓縮列表來代替HashTable和SkipList
不過,
ZipList
存在連鎖更新問題,因此而在Redis7.0版本以后,ZipList
又被替換為Listpack(緊湊列表)。
五、Redis內存回收
1、過期KEY的處理
?面試題:Redis如何判斷KEY是否過期呢?
答:在Redis中會有兩個Dict,也就是HashTable,其中一個記錄KEY-VALUE鍵值對,另一個記錄KEY和過期時間。要判斷一個KEY是否過期,只需要到記錄過期時間的Dict中根據KEY查詢即可。
Redis是何時刪除過期KEY的呢?
Redis并不會在KEY過期時立刻刪除KEY,因為要實現這樣的效果就必須給每一個過期的KEY設置時鐘,并監控這些KEY的過期狀態。無論對CPU還是內存都會帶來極大的負擔。
Redis的過期KEY刪除策略有兩種:
惰性刪除,顧明思議就是過期后不會立刻刪除。那在什么時候刪除呢?
Redis會在每次訪問KEY的時候判斷當前KEY有沒有設置過期時間,如果有,過期時間是否已經到期。
周期刪除:顧明思議是通過一個定時任務,周期性的抽樣部分過期的key,然后執行刪除。
執行周期有兩種:
SLOW模式:Redis會設置一個定時任務
serverCron()
,按照server.hz
的頻率來執行過期key清理FAST模式:Redis的每個事件循環前執行過期key清理(事件循環就是NIO事件處理的循環)。
2、內存淘汰策略
Redis支持8種不同的內存淘汰策略:
對于某些特別依賴于Redis的項目而言,僅僅依靠過期KEY清理是不夠的,內存可能很快就達到上限。因此Redis允許設置內存告警閾值,當內存使用達到閾值時就會主動挑選部分KEY刪除以釋放更多內存。這叫做內存淘汰機制。
noeviction
: 不淘汰任何key,但是內存滿時不允許寫入新數據,默認就是這種策略。volatile
-ttl
: 對設置了TTL的key,比較key的剩余TTL值,TTL越小越先被淘汰allkeys
-random
:對全體key ,隨機進行淘汰。也就是直接從db->dict中隨機挑選volatile-random
:對設置了TTL的key ,隨機進行淘汰。也就是從db->expires中隨機挑選。allkeys-lru
: 對全體key,基于LRU算法進行淘汰volatile-lru
: 對設置了TTL的key,基于LRU算法進行淘汰allkeys-lfu
: 對全體key,基于LFU算法進行淘汰volatile-lfu
: 對設置了TTL的key,基于LFI算法進行淘汰
比較容易混淆的有兩個算法:
LRU(
L
east
R
ecently
U
sed
),最近最久未使用。用當前時間減去最后一次訪問時間,這個值越大則淘汰優先級越高。LFU(
L
east
F
requently
U
sed
),最少頻率使用。會統計每個key的訪問頻率,值越小淘汰優先級越高。? ? ??
六、緩存問題
參考另一個redis面試