Redis持久化
RDB
在默認情況下, Redis 將內(nèi)存數(shù)據(jù)庫快照保存在名字dump.rdb 的二進制文件中。你可以對 Redis 進行設(shè)置, 讓它在“ N 秒內(nèi)數(shù)據(jù)集至少有 M 個改動”這一條件被滿足時, 自動保存一次 數(shù)據(jù)集。比如說, 以下設(shè)置會讓 Redis 在滿足“ 60 秒內(nèi)有至少有 1000 個鍵被改動”這一條件時, 自動保存一次 數(shù)據(jù)集:
# save 60 1000
關(guān)閉RDB只需要將所有的save保存策略注釋掉即可。還可以手動執(zhí)行命令生成RDB快照,進入redis客戶端執(zhí)行命令save、bgsave可以生成dump.rdb文件, 每次命令執(zhí)行都會將所有redis內(nèi)存快照到一個新的rdb文件里,并覆蓋原有rdb快照文件。 save是同步命令,bgsave是異步命令,bgsave會從redis主進程fork(fork()是linux函數(shù))出一個子進程 專門用來生成rdb快照文件。
| 命令 | save | bgsave |
|---|---|---|
| io類型 | 同步 | 異步 |
| 是否阻塞redis其它命令 | 是 | 否(在生成子進程執(zhí)行調(diào)用fork函 數(shù)時會有短暫阻塞) |
| 復雜度 | O(n) | O(n) |
| 優(yōu)點 | 不會消耗額外內(nèi)存 | 不阻塞客戶端命令 |
| 缺點 | 阻塞客戶端命令 | fork子進程消耗內(nèi)存 |
- 配置自動生成rdb文件后臺使用的是bgsave方式。
AOF
快照功能并不是非常耐久(durable): 如果 Redis 因為某些原因而造成故障停機, 那么服務(wù)器將丟失 最近寫入、且仍未保存到快照中的那些數(shù)據(jù)。從 1.1 版本開始, Redis 增加了一種完全耐久的持久化方 式: AOF 持久化,將修改的每一條指令記錄進文件appendonly.aof中。你可以通過修改配置文件來打開 AOF 功能:
# appendonly yes
從現(xiàn)在開始, 每當 Redis 執(zhí)行一個改變數(shù)據(jù)集的命令時(比如 SET), 這個命令就會被追加到 AOF 文 件的末尾。這樣的話, 當 Redis 重新啟動時, 程序就可以通過重新執(zhí)行 AOF 文件中的命令來達到重建數(shù)據(jù)集的目 的。你可以配置 Redis 多久才將數(shù)據(jù) fsync 到磁盤一次。
appendfsync always//每次有新命令追加到 AOF 文件時就執(zhí)行一次 fsync ,非常慢,也非 常安全。
appendfsync everysec//每秒 fsync 一次,足夠快(和使用 RDB 持久化差不多),并且在 故障時只會丟失 1 秒鐘的數(shù)據(jù)。
appendfsync no//從不 fsync 將數(shù)據(jù)交給操作系統(tǒng)來處理。更快,也更不安全的選擇。
推薦(并且也是默認)的措施為每秒 fsync 一次, 這種 fsync 策略可以兼顧速度和安全性。
AOF重寫
AOF文件里可能有太多沒用指令,所以AOF會定期根據(jù)內(nèi)存的最新數(shù)據(jù)生成aof文件。如下兩個配置可以控制AOF自動重寫頻率:
# auto-aof-rewrite-min-size 64mb //aof文件至少要達到64M才會自動重寫,文件太小恢復速度本 來就很快,重寫的意義不大
# auto-aof-rewrite-percentage 100 //aof文件自上一次重寫后文件大小增長了100%則再次觸發(fā)重 寫
當然AOF還可以手動重寫,進入redis客戶端執(zhí)行命令bgrewriteaof重寫AOF。
- 注意,AOF重寫redis會fork出一個子進程去做,不會對redis正常命令處理有太多影響
Redis 4.0 混合持久化
重啟 Redis 時,我們很少使用 RDB來恢復內(nèi)存狀態(tài),因為會丟失大量數(shù)據(jù)。我們通常使用 AOF 日志重 放,但是重放 AOF 日志性能相對 RDB來說要慢很多,這樣在 Redis 實例很大的情況下,啟動需要花費很 長的時間。 Redis 4.0 為了解決這個問題,帶來了一個新的持久化選項——混合持久化。 通過如下配置可以開啟混合持久化:
# aof-use-rdb-preamble yes
如果開啟了混合持久化,AOF在重寫時,不再是單純將內(nèi)存數(shù)據(jù)轉(zhuǎn)換為RESP命令寫入AOF文件,而是將 重寫這一刻之前的內(nèi)存做RDB快照處理,并且將RDB快照內(nèi)容和增量的AOF修改內(nèi)存數(shù)據(jù)的命令存在一 起,都寫入新的AOF文件,新的文件一開始不叫appendonly.aof,等到重寫完新的AOF文件才會進行改 名,原子的覆蓋原有的AOF文件,完成新舊兩個AOF文件的替換。于是在 Redis 重啟的時候,可以先加載 RDB 的內(nèi)容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,因此重啟效率大幅得到提升。
Redis主從架構(gòu)
- Redis主從工作原理
如果你為master配置了一個slave,不管這個slave是否是第一次連接上Master,它都會發(fā)送一個SYNC命 令(redis2.8版本之前的命令)給master請求復制數(shù)據(jù)。 master收到SYNC命令后,會在后臺進行數(shù)據(jù)持久化通過bgsave生成最新的rdb快照文件,持久化期間, master會繼續(xù)接收客戶端的請求,它會把這些可能修改數(shù)據(jù)集的請求緩存在內(nèi)存中。當持久化進行完畢以 后master會把這份rdb文件數(shù)據(jù)集發(fā)送給slave,slave會把接收到的數(shù)據(jù)進行持久化生成rdb,然后再加 載到內(nèi)存中。然后,master再將之前緩存在內(nèi)存中的命令發(fā)送給slave。 當master與slave之間的連接由于某些原因而斷開時,slave能夠自動重連Master,如果master收到了多 個slave并發(fā)連接請求,它只會進行一次持久化,而不是一個連接一次,然后再把這一份持久化的數(shù)據(jù)發(fā)送 給多個并發(fā)連接的slave。 當master和slave斷開重連后,一般都會對整份數(shù)據(jù)進行復制。但從redis2.8版本開始,master和slave斷 開重連后支持部分復制。 - 數(shù)據(jù)部分復制
從2.8版本開始,slave與master能夠在網(wǎng)絡(luò)連接斷開重連后只進行部分數(shù)據(jù)復制。 master會在其內(nèi)存中創(chuàng)建一個復制數(shù)據(jù)用的緩存隊列,緩存最近一段時間的數(shù)據(jù),master和它所有的 slave都維護了復制的數(shù)據(jù)下標offset和master的進程id,因此,當網(wǎng)絡(luò)連接斷開后,slave會請求master 繼續(xù)進行未完成的復制,從所記錄的數(shù)據(jù)下標開始。如果master進程id變化了,或者從節(jié)點數(shù)據(jù)下標 offset太舊,已經(jīng)不在master的緩存隊列里了,那么將會進行一次全量數(shù)據(jù)的復制。從2.8版本開始,redis改用可以支持部分數(shù)據(jù)復制的命令PSYNC去master同步數(shù)據(jù)。
3.redis主從架構(gòu)搭建,配置從節(jié)點步驟:
1、復制一份redis.conf文件 2
2、將相關(guān)配置修改為如下值:
port6380
pidfile/var/run/redis_6380.pid
logfile"6380.log"
dir/usr/local/redis‐5.0.3/data/6380 8
3、配置主從復制
replicaof192.168.0.606379#從本機6379的redis實例復制數(shù)據(jù)
replica‐read‐onlyyes
4、啟動從節(jié)點
redis‐serverredis.conf
5、連接從節(jié)點
redis‐cli‐p6380
6、測試在6379實例上寫數(shù)據(jù),6380實例是否能及時同步新修改數(shù)據(jù)
7、可以自己再配置一個6381的從節(jié)點
Redis哨兵高可用架構(gòu)
sentinel哨兵是特殊的redis服務(wù),不提供讀寫服務(wù),主要用來監(jiān)控redis實例節(jié)點。 哨兵架構(gòu)下client端第一次從哨兵找出redis的主節(jié)點,后續(xù)就直接訪問redis的主節(jié)點,不會每次都通sentinel代理訪問redis的主節(jié)點,當redis的主節(jié)點發(fā)生變化,哨兵會第一時間感知到,并且將新的redis 主節(jié)點通知給client端(這里面redis的client端一般都實現(xiàn)了訂閱功能,訂閱sentinel發(fā)布的節(jié)點變動消息)
redis哨兵架構(gòu)搭建步驟:
1、復制一份sentinel.conf文件
cpsentinel.confsentinel‐26379.conf
2、將相關(guān)配置修改為如下值:
port 26379
daemonize yes
pidfile"/var/run/redis‐sentinel‐26379.pid"
logfile"26379.log"
dir"/usr/local/redis‐5.0.3/data"
#sentinelmonitor<master‐name><ip><redis‐port><quorum>
#quorum是一個數(shù)字,指明當有多少個sentinel認為一個master失效時(值一般為:sentinel總數(shù)/2+ 1),master才算真正失效
sentinel monitor mymaster 192.168.0.60 6379
3、啟動sentinel哨兵實例
src/redis‐sentinelsentinel‐26379.conf
4、查看sentinel的info信息
src/redis‐cli‐p26379
127.0.0.1:26379>info
可以看到Sentinel的info里已經(jīng)識別出了redis的主從
5、可以自己再配置兩個sentinel,端口26380和26381,注意上述配置文件里的對應(yīng)數(shù)字都要修改。
哨兵leader選舉流程
當一個master服務(wù)器被某sentinel視為客觀下線狀態(tài)后,該sentinel會與其他sentinel協(xié)商選出sentinel的leader進行故 障轉(zhuǎn)移工作。每個發(fā)現(xiàn)master服務(wù)器進入客觀下線的sentinel都可以要求其他sentinel選自己為sentinel的leader,選舉 是先到先得。同時每個sentinel每次選舉都會自增配置紀元(選舉周期),每個紀元中只會選擇一個sentinel的leader。如 果所有超過一半的sentinel選舉某sentinel作為leader。之后該sentinel進行故障轉(zhuǎn)移操作,從存活的slave中選舉出新 的master,這個選舉過程跟集群的master選舉很類似。 哨兵集群只有一個哨兵節(jié)點,redis的主從也能正常運行以及選舉master,如果master掛了,那唯一的那個哨兵節(jié)點就 是哨兵leader了,可以正常選舉新master。 不過為了高可用一般都推薦至少部署三個哨兵節(jié)點。為什么推薦奇數(shù)個哨兵節(jié)點原理跟集群奇數(shù)個master節(jié)點類似。
集群模式
- 哨兵模式的不足
在redis3.0以前的版本要實現(xiàn)集群一般是借助哨兵sentinel工具來監(jiān)控master節(jié)點的狀態(tài),如果master節(jié)點異常,則會 做主從切換,將某一臺slave作為master,哨兵的配置略微復雜,并且性能和高可用性等各方面表現(xiàn)一般,特別是在主 從切換的瞬間存在訪問瞬斷的情況,而且哨兵模式只有一個主節(jié)點對外提供服務(wù),沒法支持很高的并發(fā),且單個主節(jié)點 內(nèi)存也不宜設(shè)置得過大,否則會導致持久化文件過大,影響數(shù)據(jù)恢復或主從同步的效率。 - redis集群:redis集群是一個由多個主從節(jié)點群組成的分布式服務(wù)器群,它具有復制、高可用和分片特性。Redis集群不需要 sentinel哨兵也能完成節(jié)點移除和故障轉(zhuǎn)移的功能。需要將每個節(jié)點設(shè)置成集群模式,這種集群模式?jīng)]有中心節(jié)點,可 水平擴展,據(jù)官方文檔稱可以線性擴展到上萬個節(jié)點(官方推薦不超過1000個節(jié)點)。redis集群的性能和高可用性均優(yōu)于 之前版本的哨兵模式,且集群配置非常簡單。
Redis集群原理分析
- Redis Cluster 將所有數(shù)據(jù)劃分為 16384 個 slots(槽位),每個節(jié)點負責其中一部分槽位。槽位的信息存儲于每個節(jié)點 中。當 Redis Cluster 的客戶端來連接集群時,它也會得到一份集群的槽位配置信息并將其緩存在客戶端本地。這樣當客戶 端要查找某個 key 時,可以直接定位到目標節(jié)點。同時因為槽位的信息可能會存在客戶端與服務(wù)器不一致的情況,還需 要糾正機制來實現(xiàn)槽位信息的校驗調(diào)整。
- 槽位定位算法:Cluster 默認會對 key 值使用 crc16 算法進行 hash 得到一個整數(shù)值,然后用這個整數(shù)值對 16384 進行取模來得到具體 槽位。HASH_SLOT = CRC16(key) mod 16384。
- 跳轉(zhuǎn)重定位:當客戶端向一個錯誤的節(jié)點發(fā)出了指令,該節(jié)點會發(fā)現(xiàn)指令的 key 所在的槽位并不歸自己管理,這時它會向客戶端發(fā)送 一個特殊的跳轉(zhuǎn)指令攜帶目標操作的節(jié)點地址,告訴客戶端去連這個節(jié)點去獲取數(shù)據(jù)??蛻舳耸盏街噶詈蟪颂D(zhuǎn)到正 確的節(jié)點上去操作,還會同步更新糾正本地的槽位映射表緩存,后續(xù)所有 key 將使用新的槽位映射表。
- Redis集群節(jié)點間的通信機制:redis cluster節(jié)點間采取gossip協(xié)議進行通信。維護集群的元數(shù)據(jù)有兩種方式:集中式和gossip。
- 集中式
優(yōu)點在于元數(shù)據(jù)的更新和讀取,時效性非常好,一旦元數(shù)據(jù)出現(xiàn)變更立即就會更新到集中式的存儲中,其他節(jié)點讀取的 時候立即就可以立即感知到;不足在于所有的元數(shù)據(jù)的更新壓力全部集中在一個地方,可能導致元數(shù)據(jù)的存儲壓力。 - gossip:gossip協(xié)議包含多種消息,包括ping,pong,meet,fail等等。
ping:每個節(jié)點都會頻繁給其他節(jié)點發(fā)送ping,其中包含自己的狀態(tài)還有自己維護的集群元數(shù)據(jù),互相通過ping交換元 數(shù)據(jù)。
pong: 返回ping和meet,包含自己的狀態(tài)和其他信息,也可以用于信息廣播和更新。
fail: 某個節(jié)點判斷另一個節(jié)點fail之后,就發(fā)送fail給其他節(jié)點,通知其他節(jié)點,指定的節(jié)點宕機了。
meet:某個節(jié)點發(fā)送meet給新加入的節(jié)點,讓新節(jié)點加入集群中,然后新節(jié)點就會開始與其他節(jié)點進行通信,不需要 發(fā)送形成網(wǎng)絡(luò)的所需的所有CLUSTER MEET命令。發(fā)送CLUSTER MEET消息以便每個節(jié)點能夠達到其他每個節(jié)點只需通 過一條已知的節(jié)點鏈就夠了。由于在心跳包中會交換gossip信息,將會創(chuàng)建節(jié)點間缺失的鏈接。
gossip協(xié)議的優(yōu)點在于元數(shù)據(jù)的更新比較分散,不是集中在一個地方,更新請求會陸陸續(xù)續(xù),打到所有節(jié)點上去更新, 有一定的延時,降低了壓力;缺點在于元數(shù)據(jù)更新有延時可能導致集群的一些操作會有一些滯后。
每個節(jié)點都有一個專門用于節(jié)點間通信的端口,就是自己提供服務(wù)的端口號+10000,比如7001,那么用于節(jié)點間通信 的就是17001端口。 每個節(jié)點每隔一段時間都會往另外幾個節(jié)點發(fā)送ping消息,同時其他幾點接收到ping消息之后返 回pong消息。
- 網(wǎng)絡(luò)抖動:真實世界的機房網(wǎng)絡(luò)往往并不是風平浪靜的,它們經(jīng)常會發(fā)生各種各樣的小問題。比如網(wǎng)絡(luò)抖動就是非常常見的一種現(xiàn) 象,突然之間部分連接變得不可訪問,然后很快又恢復正常。為解決這種問題,Redis Cluster 提供了一種選項cluster-node-timeout,表示當某個節(jié)點持續(xù) timeout 的時間失 聯(lián)時,才可以認定該節(jié)點出現(xiàn)故障,需要進行主從切換。如果沒有這個選項,網(wǎng)絡(luò)抖動會導致主從頻繁切換 (數(shù)據(jù)的重 新復制)。
Redis集群選舉原理分析
當slave發(fā)現(xiàn)自己的master變?yōu)镕AIL狀態(tài)時,便嘗試進行Failover,以期成為新的master。由于掛掉的master可能會有 多個slave,從而存在多個slave競爭成為master節(jié)點的過程, 其過程如下:
- slave發(fā)現(xiàn)自己的master變?yōu)镕AIL
- 將自己記錄的集群currentEpoch加1,并廣播FAILOVER_AUTH_REQUEST 信息
- 其他節(jié)點收到該信息,只有master響應(yīng),判斷請求者的合法性,并發(fā)送FAILOVER_AUTH_ACK,對每一個epoch只發(fā) 送一次ack
- 嘗試failover的slave收集master返回的FAILOVER_AUTH_ACK
- slave收到超過半數(shù)master的ack后變成新Master(這里解釋了集群為什么至少需要三個主節(jié)點,如果只有兩個,當其 中一個掛了,只剩一個主節(jié)點是不能選舉成功的)
- 廣播Pong消息通知其他集群節(jié)點。
從節(jié)點并不是在主節(jié)點一進入 FAIL 狀態(tài)就馬上嘗試發(fā)起選舉,而是有一定延遲,一定的延遲確保我們等待FAIL狀態(tài)在 集群中傳播,slave如果立即嘗試選舉,其它masters或許尚未意識到FAIL狀態(tài),可能會拒絕投票
- 延遲計算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
SLAVE_RANK表示此slave已經(jīng)從master復制數(shù)據(jù)的總量的rank。Rank越小代表已復制的數(shù)據(jù)越新。這種方式下,持 有最新數(shù)據(jù)的slave將會首先發(fā)起選舉(理論上)。
集群是否完整才能對外提供服務(wù)
當redis.conf的配置cluster-require-full-coverage為no時,表示當負責一個插槽的主庫下線且沒有相應(yīng)的從庫進行故 障恢復時,集群仍然可用,如果為yes則集群不可用。
Redis集群為什么至少需要三個master節(jié)點,并且推薦節(jié)點數(shù)為奇數(shù)?
因為新master的選舉需要大于半數(shù)的集群master節(jié)點同意才能選舉成功,如果只有兩個master節(jié)點,當其中一個掛 了,是達不到選舉新master的條件的。奇數(shù)個master節(jié)點可以在滿足選舉該條件的基礎(chǔ)上節(jié)省一個節(jié)點,比如三個master節(jié)點和四個master節(jié)點的集群相 比,大家如果都掛了一個master節(jié)點都能選舉新master節(jié)點,如果都掛了兩個master節(jié)點都沒法選舉新master節(jié)點 了,所以奇數(shù)的master節(jié)點更多的是從節(jié)省機器資源角度出發(fā)說的。