文章目錄
- redis資料結構原理
- redis持久化
- RDB持久化
- AOF持久化
- redis集群三種模式
- 主從模式(實作主從分離,提高吞吐,多機備份)
- 哨兵模式(主從高可用,自動化修復)
- 集群模式(資料分片,解決了寫操作無法負載均衡,單機容量存盤、并發問題)
- redis應用場景
- 分布式鎖
- redis架構思維
redis資料結構原理
待整理~
redis持久化
RDB持久化
- 執行流程
- 父行程執行fork操作創建子行程,這個程序中父行程是阻塞的,Redis不能執行來自客戶端的任何命令,
- 子行程創建RDB檔案,根據父行程記憶體快照生成臨時快照檔案,完成后對原有檔案進行原子替換,替換后子行程信號通知主行程
- rdb自動持久化配置:
時間策略要按照實際情況配置多條,資料的存盤時不均勻的,高峰期短時間間隔要存一次,低峰期長時間間隔要存一次,
# 檔案名稱
dbfilename dump.rdb
# 時間策略
save 900 1
save 300 10
save 60 10000
# 檔案保存路徑
dir /etc/redis/data/
# 如果持久化出錯,主行程是否停止寫入
stop-writes-on-bgsave-error yes
# 是否壓縮
rdbcompression yes
# 匯入時是否檢查
rdbchecksum yes
- rdb策略將記憶體中的資料生成快照保存到磁盤,是全量存盤,記憶體大的話會比較耗時,大量磁盤io,
- 持久化的觸發方式:
- save命令:client向server發送save命令,同步阻塞,
- bgsave命令:server中執行命令,異步子行程執行,
- 自動持久化,通過save配置項完成,
- rdb檔案恢復:如果開啟了aof,則不生效,如果沒開啟aof,則尋找dir配置下的rdb檔案,
AOF持久化
- aof同步步驟:
- Redis收到寫命令后首先會追加到AOF緩沖區aof_buf(write)
- 通過一定時機(配置決定),呼叫系統函式 fsync() 把AOF緩沖區的資料刷到磁盤
- 落盤時機配置(fsync):
always: 命令寫入aof緩沖區后立即呼叫系統fsync操作同步到AOF檔案,資料絕對安全,
no:有作業系統決定何時呼叫fsync()
everysec:每秒調fsync()一次, 若損失資料只損失1秒,對于大多數系統來說足夠了, - 檔案重寫rewrite
- 由父行程fork子行程進行重寫
- 使用寫時重寫技術,在重寫完成期間,Redis的寫命令同時追加到aof_buf和aof_rewirte_buf兩個緩沖區,
- 重寫檔案完成后,將aof_rewirte_buf資料輸入新檔案
- 用新aof檔案替換老aof檔案,
redis集群三種模式
主從模式(實作主從分離,提高吞吐,多機備份)
從資料庫一般都是只讀的,并且接收主資料庫同步過來的資料
要點:
- 主從復制還是哨兵和集群能夠實施的基礎,主從復制是Redis高可用的基礎,
- 再看CAP,由于redis復制是異步復制,會導致短時的資料不一致,所以無法滿足一致性C,但可以保證當網路磁區發生時,各個節點依舊可用,redis主從模式是CP,而不是AP,redis會使用最終一致性策略,保證主從同步資料一致,
- 主從復制實作原理:
- 6大步驟: 1. 設定主節點ip及埠、2. 建立套接字socket、 3. 發送ping命令、 4. 權限驗證 、 5. 同步 6. 命令傳播
- 設定主節點ip及埠: 使用slaveof命令,可組態檔使用、可命令列使用
- 復制階段:從節點向主節點發送psync或sync命令(2.8版本之前),可以實作全量復制與增量復制
- 命令傳播:當通過復制階段后,主從節點進入命令傳播階段,主節點將自己執行的寫命令發送給從節點,從節點接收命令并執行,從而保證主從節點資料的一致性,命令傳播是異步的,主節點并不會等從節點同步執行完命令,這樣會導致“延遲不一致”現象,
- 延遲不一致現象:網路波動、寫命令頻率過高,會導致資料延遲不一致,同時,repl-disable-tcp-nodelay配置也會影響,設定為yes,會將代發資料合并發送,延遲大概40ms(取決于系統),如果設定為no,寫命令實時發送同步,
- 全量復制和部分復制
- 全量復制:用于初次復制或其他無法進行部分復制的情況,將主節點中的所有資料都發送給從節點,是一個非常重型的操作
- 部分復制:用于網路中斷等情況后的復制,只同步網路斷開期間的快取寫資料,如果網路中斷時間過長,導致主節點沒有能夠完整地保存中斷期間執行的寫命令,則無法進行部分復制,仍使用全量復制,
- 全量復制原理剖析:
fork子行程,開始執行bgsave,生成RDB檔案,同時開辟緩沖區記錄從現在開始的寫命令,2. 將rdb檔案傳輸給從服務器,從服務器執行完畢后,主節點將緩沖區寫資料同步到從節點,保證最終一致性,3. 如果從節點開啟了AOF,會觸發bgrewriteof,從而保證從節點的aof檔案最新, - 部分復制原理剖析:
- 復制偏移量:與mysql類似,主從節點各自維護offset欄位,不一致則復制
- 復制積壓緩沖區:底層結構是定長、先進先出FIFO的佇列,可以repl-backlog-size引數設定大小, 當主從節點offset的差距過大超過緩沖區長度時,將無法執行部分復制,只能執行全量復制,
- 服務器運行id(runid):每個從節點都存盤著主節點同步下來的runid,當網路磁區發生時,重連后slave節點會判斷同步的runid是否存在,如果存在優先考慮增量復制,如果不存在,則全量復制,
- psync命令復制原理:主節點收到psync命令后,進行判斷,如果命令為psync命令,則執行全量復制,如果收到的為psync {runid} {offset}命令,則執行增量復制,執行增量復制程序中如果offset差超過了buffer,則執行全量復制,
- 心跳檢測機制及其作用:1. 檢測主從鏈接 2. 輔助實作min-slaves選項 3. 檢測命令丟失,保證資料一致
- 通過判斷在線的從節點數量,實作min-slaves,
min-slaves概念,保證高可用:
未達到下面兩個條件時,寫操作就不會被執行
min-slaves-to-write 3 #最少包含的從服務器
min-slaves-max-lag 10 #延遲值 - 心跳會回傳offset資訊,通過offset判斷從節點的資料同步是否及時,不及時則通過offset補發資料
- 主從復制超時原理:斷開連接后會嘗試重連,
- 主節點判斷超時:每秒1次呼叫復制定時函式replicationCron(),如果時間大于到上次REPLCONF ACK的時長,則斷開連接,釋放資源(緩沖器、連接、帶寬),超時時間由引數repl-timeout值控制,
- 從節點判斷超時:主要也是由repl-timeout引數控制,1. 連接建立階段,若大于時間則斷開連接, 2. 全量復制階段:如果收到rdb的時間超時,則斷開連接, 3. 命令傳播階段:如果收到主節點ping的時間過長,超過timeout,則斷開連接,
- 如果rdb檔案過大,會導致一直同步失敗,會無線重連,應適當調大timeout值
- 主庫掛了發生什么?
不影響slave的讀,但redis不再提供寫服務,master重啟后redis將重新對外提供寫服務,
如果slave-serve-stale-data引數設定為yes,主節點掛掉后從節點可以繼續提供服務,如果設定為no,主節點掛掉后從節點不再提供讀資料服務,僅提供info、slaveof等少量命令,在強一致場景需要考慮設定為no,如分布式鎖,如果主節點掛掉,資料沒來得及同步從節點,會導致從節點讀不到,鎖失效,
重啟master節點需要保證rdb檔案或者aof檔案是最新, - redis如何保證主從服務器連接正常且資料最終一致?
命令傳播階段,從服務器會利用心跳檢測機制定時的向主服務發送訊息, - 如果提高資料實時一致性?
優化主從節點之間的網路環境(如在同機房部署);監控主從節點延遲(通過offset)判斷,如果從節點延遲過大,通知應用不再通過該從節點讀取資料; - java客戶端連接redis如何實作讀寫分離,讀負載均衡?
常見的客戶端有jredis,lettuce,redission,以lettuce為例,可以使用
StatefulRedisMasterSlaveConnection connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), redisURI);
connection.setReadFrom(ReadFrom.NEAREST);
// MASTER 主讀
// SLAVE 從讀
// MASTER_PREFERRED 優先master -> slave
// SLAVE_PREFERRED 優先slave -> master
// NEAREST 最近節點讀
// 實作很簡單,重寫ReadFrom的select方法,自帶的方法均無法實作負載均衡
static final class ReadFromSlavePreferred extends ReadFrom {
@Override
public List<RedisNodeDescription> select(Nodes nodes) {
List<RedisNodeDescription> result = new ArrayList<>(nodes.getNodes().size());
//優先添加slave節點
for (RedisNodeDescription node : nodes) {
if (node.getRole() == RedisInstance.Role.SLAVE) {
result.add(node);
}
}
//最后添加master節點
for (RedisNodeDescription node : nodes) {
if (node.getRole() == RedisInstance.Role.MASTER) {
result.add(node);
}
}
return result;
}
// 自定義負載均衡
@Bean(destroyMethod = "close")
StatefulRedisMasterSlaveConnection<String, String> statefulRedisMasterSlaveConnection(RedisClient redisClient, RedisURI redisURI) {
StatefulRedisMasterSlaveConnection connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), redisURI);
connection.setReadFrom(new ReadFrom() {
@Override
public List<RedisNodeDescription> select(Nodes nodes) {
List<RedisNodeDescription> list = nodes.getNodes();
Collections.shuffle(list);
return list;
}
});
return connection;
}
哨兵模式(主從高可用,自動化修復)
- 高可用架構一般將sentinel與redis實體部署在不同的服務器上,
- 作業機制:每個sentinel以每秒鐘一次的頻率向它所知的master,slave以及其他sentinel實體發送一個 PING 命令 ,如果回應超過 down-after-milliseconds,則標記為主觀下線,其它sentinel以每秒鐘一次的頻率ping主觀下線的實體,如果足夠多的(通過配置指定)sentinel確認該實體的主觀下線狀態,則置為客觀下線,Sentinel 和其他 Sentinel 協商 主節點 的狀態,如果 主節點 處于 SDOWN 狀態,則投票自動選出新的 主節點,將剩余的 從節點 指向 新的主節點 進行 資料復制
- 當使用sentinel模式的時候,客戶端就不要直接連接Redis,而是連接sentinel的ip和port,由luttuce自動獲取redis集群拓撲,從而獲取master節點與slave節點的資訊,并通過監聽redis事件更新拓撲資訊,
- 哨兵模式切換主節點導致資料不一致,“腦裂問題”的解決方案:
- 當網路磁區發生時,會導致master下線,實際master沒有掛,選舉出新的master后會出現兩個master,當網路磁區修好后,舊master會變為slave同步新master的資料,導致資料丟失,
配置:
min-slaves-to-write 1 # 必須至少要有1個slave
min-slaves-max-lag 10 # 資料復制和同步延遲不能超過10秒
不符合以上情況,master將拒絕接受請求,將資料丟失控制在10秒,
從業務上看,及其重要的資料,可以進行處理:
進redis前可以在其它地方臨時存盤,
可以加網關,降低流入速度,防止瞬間進入過多資料,
集群模式(資料分片,解決了寫操作無法負載均衡,單機容量存盤、并發問題)
- 一致性hash與hash槽
- 簡單hash演算法,通過計算hash值 % 機器數量,得到資料的實際對應節點,當增加或洗掉節點時,會導致大量的快取失效,甚至導致雪崩,
- 一致性哈希演算法中,整個哈希空間是一個虛擬圓環,共計2^32 - 1 個節點,將每個redis節點的ip地址進行hash計算后落位虛擬節點,每條資料通過計算hash值后順時針落位對應節點,當某個節點增、刪、不可用時,只會影響下一個節點,而不會影響所有節點,
一致性hash演算法的問題是會導致資料傾斜,如果節點數過少,在盤中的分布又不均勻,可能會導致所有資料都落在極少的節點上, - 哈希槽,redis包含了16384個哈希槽,每個 key 通過計算后都會落在具體一個槽位上,而這個槽位是屬于哪個存盤節點的,則由用戶自己定義分配,
對于槽位的轉移和分派,redis 集群是不會自動進行的,而是需要人工配置的,所以 redis 集群的高可用是依賴于節點的主從復制與主從間的自動故障轉移,
- redis-cluster是redis集群的官方實作版,具有投票、容錯性等特點,
- 集群中可以有多個master節點,需要保證所有master節點恰好覆寫所有hash槽,否則集群不能啟動,添加、洗掉節點,只需要更改對應的哈希槽范圍,
node1 : [1,15000]
node2 : [15001,20000]
… - 何時master節點算掛掉?
半數以上master節點與master節點通信超時(cluster-node-timeout),認為當前master節點掛掉,如果此時該master節點由slave節點,則集群依舊正常運行,對應掛掉的節點不再提供寫入服務, - 何時集群掛掉?
1.只要某個槽段失效,集群就會進入不可用狀態,主、從都掛,
2.超過半數的master節點掛掉,不管slave是否正常,集群不可用, - 一般情況,各個槽段的主、從redis節點可以交叉部署,部署在不同的服務器上,
redis應用場景
分布式鎖
- 單機版分布式鎖:
- 加鎖終極版:set nx px + 事務id
- 解鎖終極版:使用lua腳本實作,
if redis.call(“get”,KEYS[1]) == ARGV[1] then
return redis.call(“del”,KEYS[1])
else
return 0
end - 為何解鎖需要用lua,而加鎖不需要?
加鎖程序,set命令的 nx 引數 = set if not exist,get value->判斷不存在->set value,set 命令的 px引數,設定過期時間,相當于三條命令保持原子性,
解鎖程序,get獲取鍵值,判斷value是否相等,如果此時JVM
redis架構思維
待整理~
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/246531.html
標籤:其他
上一篇:資料結構——堆疊(C語言)
下一篇:React fiber 架構淺析
