1.Redis核心數(shù)據(jù)結(jié)構(gòu)
1.1 String
字符串常用操作
SET key value //存入字符串鍵值對
MSET key value [key value ...] //批量存儲字符串鍵值對
SETNX key value //存入一個不存在的字符串鍵值對
GET key //獲取一個字符串鍵值
MGET key [key ...] //批量獲取字符串鍵值
DEL key [key ...] //刪除一個鍵
EXPIRE key seconds //設(shè)置一個鍵的過期時間(秒)
原子加減
INCR key //將key中儲存的數(shù)字值加1
DECR key //將key中儲存的數(shù)字值減1
INCRBY key increment //將key所儲存的值加上increment
DECRBY key decrement //將key所儲存的值減去decrement
單值緩存
SET key value
GET key
對象緩存
1) SET user:1 value(json格式數(shù)據(jù))
2) MSET user:1:name zhuge user:1:balance 1888
MGET user:1:name user:1:balance
分布式鎖
SETNX product:10001 true //返回1代表獲取鎖成功
SETNX product:10001 true //返回0代表獲取鎖失敗
毅待。。。執(zhí)行業(yè)務(wù)操作
DEL product:10001 //執(zhí)行完業(yè)務(wù)釋放鎖
SET product:10001 true ex 10 nx //防止程序意外終止導(dǎo)致死鎖
計數(shù)器
INCR article:readcount:{文章id}
GET article:readcount:{文章id}
Web集群session共享
spring session + redis實現(xiàn)session共享
分布式系統(tǒng)全局序列號
INCRBY orderId 1000 //redis批量生成序列號提升性能
1.2 Hash
Hash常用操作
HSET key field value //存儲一個哈希表key的鍵值
HSETNX key field value //存儲一個不存在的哈希表key的鍵值
HMSET key field value [field value ...] //在一個哈希表key中存儲多個鍵值對
HGET key field //獲取哈希表key對應(yīng)的field鍵值
HMGET key field [field ...] //批量獲取哈希表key中多個field鍵值
HDEL key field [field ...] //刪除哈希表key中的field鍵值
HLEN key //返回哈希表key中field的數(shù)量
HGETALL key //返回哈希表key中所有的鍵值
HINCRBY key field increment //為哈希表key中field鍵的值加上增量increment
對象緩存
HMSET user {userId}:name zhuge {userId}:balance 1888
HMSET user 1:name zhuge 1:balance 1888
HMGET user 1:name 1:balance
電商購物車
1)以用戶id為key
2)商品id為field
3)商品數(shù)量為value
購物車操作
添加商品:hset cart:1001 10088 1
增加數(shù)量:hincrby cart:1001 10088 1
商品總數(shù):hlen cart:1001
刪除商品:hdel cart:1001 10088
獲取購物車所有商品:hgetall cart:1001
優(yōu)點(diǎn)
1)同類數(shù)據(jù)歸類整合儲存峡钓,方便數(shù)據(jù)管理
2)相比string操作消耗內(nèi)存與cpu更小
3)相比string儲存更節(jié)省空間
缺點(diǎn)
過期功能不能使用在field上习柠,只能用在key上
Redis集群架構(gòu)下不適合大規(guī)模使用
1.3 List
List常用操作
LPUSH key value [value ...] //將一個或多個值value插入到key列表的表頭(最左邊)
RPUSH key value [value ...] //將一個或多個值value插入到key列表的表尾(最右邊)
LPOP key //移除并返回key列表的頭元素
RPOP key //移除并返回key列表的尾元素
LRANGE key start stop //返回列表key中指定區(qū)間內(nèi)的元素喳坠,區(qū)間以偏移量start和stop指定
BLPOP key [key ...] timeout //從key列表表頭彈出一個元素构回,若列表中沒有元素谷炸,阻塞等待 timeout秒,如果timeout=0,一直阻塞等待
BRPOP key [key ...] timeout //從key列表表尾彈出一個元素陈惰,若列表中沒有元素畦徘,阻塞等待 timeout秒,如果timeout=0,一直阻塞等待
常用數(shù)據(jù)結(jié)構(gòu)
Stack(棧) = LPUSH + LPOP
Queue(隊列)= LPUSH + RPOP
Blocking MQ(阻塞隊列)= LPUSH + BRPOP
微博消息和微信公號消息
諸葛老師關(guān)注了MacTalk,備胎說車等大V
1)MacTalk發(fā)微博抬闯,消息ID為10018
LPUSH msg:{諸葛老師-ID} 10018
2)備胎說車發(fā)微博井辆,消息ID為10086
LPUSH msg:{諸葛老師-ID} 10086
3)查看最新微博消息
LRANGE msg:{諸葛老師-ID} 0 4
1.4 Set
Set常用操作
SADD key member [member ...] //往集合key中存入元素,元素存在則忽略溶握,
若key不存在則新建
SREM key member [member ...] //從集合key中刪除元素
SMEMBERS key //獲取集合key中所有元素
SCARD key //獲取集合key的元素個數(shù)
SISMEMBER key member //判斷member元素是否存在于集合key中
SRANDMEMBER key [count] //從集合key中選出count個元素杯缺,元素不從key中刪除
SPOP key [count] //從集合key中選出count個元素,元素從key中刪除
Set運(yùn)算操作
SINTER key [key ...] //交集運(yùn)算
SINTERSTORE destination key [key ..] //將交集結(jié)果存入新集合destination中
SUNION key [key ..] //并集運(yùn)算
SUNIONSTORE destination key [key ...] //將并集結(jié)果存入新集合destination中
SDIFF key [key ...] //差集運(yùn)算
SDIFFSTORE destination key [key ...] //將差集結(jié)果存入新集合destination中
微信抽獎小程序
1)點(diǎn)擊參與抽獎加入集合
SADD key {userlD}
2)查看參與抽獎所有用戶
SMEMBERS key
3)抽取count名中獎?wù)?br>
SRANDMEMBER key [count] / SPOP key [count]
微信微博點(diǎn)贊睡榆,收藏萍肆,標(biāo)簽
1)點(diǎn)贊
SADD like:{消息ID} {用戶ID}
2) 取消點(diǎn)贊
SREM like:{消息ID} {用戶ID}
3)檢查用戶是否點(diǎn)過贊
SISMEMBER like:{消息ID} {用戶ID}
4)獲取點(diǎn)贊的用戶列表
SMEMBERS like:{消息ID}
5)獲取點(diǎn)贊用戶數(shù)
SCARD like:{消息ID}
集合操作實現(xiàn)微博微信關(guān)注模型
- 諸葛老師關(guān)注的人:
zhugeSet-> {guojia, xushu} - 楊過老師關(guān)注的人:
yangguoSet--> {zhuge, baiqi, guojia, xushu} - 郭嘉老師關(guān)注的人:
guojiaSet-> {zhuge, yangguo, baiqi, xushu, xunyu) - 我和楊過老師共同關(guān)注:
SINTER zhugeSet yangguoSet--> {guojia, xushu} - 我關(guān)注的人也關(guān)注他(楊過老師):
SISMEMBER guojiaSet yangguo
SISMEMBER xushuSet yangguo - 我可能認(rèn)識的人:
SDIFF yangguoSet zhugeSet->(zhuge, baiqi}
1.5 ZSet
ZSet常用操作
ZADD key score member [[score member]…] //往有序集合key中加入帶分值元素
ZREM key member [member …] //從有序集合key中刪除元素
ZSCORE key member //返回有序集合key中元素member的分值
ZINCRBY key increment member //為有序集合key中元素member的分值加上increment
ZCARD key //返回有序集合key中元素個數(shù)
ZRANGE key start stop [WITHSCORES] //正序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素
ZREVRANGE key start stop [WITHSCORES] //倒序獲取有序集合key從start下標(biāo)到stop下標(biāo)的元素
Zset集合操作
ZUNIONSTORE destkey numkeys key [key ...] //并集計算
ZINTERSTORE destkey numkeys key [key …] //交集計算
Zset集合操作實現(xiàn)排行榜
1)點(diǎn)擊新聞
ZINCRBY hotNews:20190819 1 守護(hù)香港
2)展示當(dāng)日排行前十
ZREVRANGE hotNews:20190819 0 9 WITHSCORES
3)七日搜索榜單計算
ZUNIONSTORE hotNews:20190813-20190819 7
hotNews:20190813 hotNews:20190814... hotNews:20190819
4)展示七日排行前十
ZREVRANGE hotNews:20190813-20190819 0 9 WITHSCORES
2.Redis的單線程和高性能
Redis是單線程嗎?
Redis 的單線程主要是指 Redis 的網(wǎng)絡(luò) IO 和鍵值對讀寫是由一個線程來完成的胀屿,這也是 Redis 對外提供鍵值存儲服務(wù)的主要流程塘揣。但 Redis 的其他功能,比如持久化碉纳、異步刪除勿负、集群數(shù)據(jù)同步等,其實是由額外的線程執(zhí)行的。
Redis 單線程為什么還能這么快奴愉?
因為它所有的數(shù)據(jù)都在內(nèi)存中琅摩,所有的運(yùn)算都是內(nèi)存級別的運(yùn)算,而且單線程避免了多線程的切換性能損耗問題锭硼。正因為 Redis 是單線程房资,所以要小心使用 Redis 指令,對于那些耗時的指令(比如keys)檀头,一定要謹(jǐn)慎使用轰异,一不小心就可能會導(dǎo)致 Redis 卡頓。
Redis 單線程如何處理那么多的并發(fā)客戶端連接暑始?
Redis的IO多路復(fù)用:redis利用epoll來實現(xiàn)IO多路復(fù)用搭独,將連接信息和事件放到隊列中,依次放到文件事件分派器廊镜,事件分派器將事件分發(fā)給事件處理器牙肝。
3.持久化
3.1 RDB快照(snapshot)
在默認(rèn)情況下, Redis 將內(nèi)存數(shù)據(jù)庫快照保存在名字為 dump.rdb 的二進(jìn)制文件中嗤朴。
你可以對 Redis 進(jìn)行設(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快照吱雏,進(jìn)入redis客戶端執(zhí)行命令save或bgsave可以生成dump.rdb文件敦姻,每次命令執(zhí)行都會將所有redis內(nèi)存快照到一個新的rdb文件里,并覆蓋原有rdb快照文件歧杏。
3.2 AOF(append-only file)
快照功能并不是非常耐久(durable): 如果 Redis 因為某些原因而造成故障停機(jī)替劈, 那么服務(wù)器將丟失最近寫入、且仍未保存到快照中的那些數(shù)據(jù)得滤。從 1.1 版本開始, Redis 增加了一種完全耐久的持久化方式: AOF 持久化盒犹,將修改的每一條指令記錄進(jìn)文件appendonly.aof中(先寫入os cache懂更,每隔一段時間fsync到磁盤)
appendfsync always:每次有新命令追加到 AOF 文件時就執(zhí)行一次 fsync ,非常慢急膀,也非常安全沮协。
appendfsync everysec:每秒 fsync 一次,足夠快卓嫂,并且在故障時只會丟失 1 秒鐘的數(shù)據(jù)慷暂。
appendfsync no:從不 fsync ,將數(shù)據(jù)交給操作系統(tǒng)來處理。更快行瑞,也更不安全的選擇奸腺。
推薦(并且也是默認(rèn))的措施為每秒 fsync 一次, 這種 fsync 策略可以兼顧速度和安全性血久。
AOF重寫:
AOF文件里可能有太多沒用指令突照,所以AOF會定期根據(jù)內(nèi)存的最新數(shù)據(jù)生成aof文件。
3.3 Redis 4.0 混合持久化
重啟 Redis 時氧吐,我們很少使用 RDB來恢復(fù)內(nèi)存狀態(tài)讹蘑,因為會丟失大量數(shù)據(jù)。我們通常使用 AOF 日志重放筑舅,但是重放 AOF 日志性能相對 RDB來說要慢很多座慰,這樣在 Redis 實例很大的情況下,啟動需要花費(fèi)很長的時間翠拣。 Redis 4.0 為了解決這個問題版仔,帶來了一個新的持久化選項——混合持久化。
通過如下配置可以開啟混合持久化(必須先開啟aof):
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文件才會進(jìn)行改名又沾,覆蓋原有的AOF文件,完成新舊兩個AOF文件的替換熙卡。
于是在 Redis 重啟的時候杖刷,可以先加載 RDB 的內(nèi)容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放驳癌,因此重啟效率大幅得到提升滑燃。
4.Redis架構(gòu)
4.1 主從架構(gòu)
如果你為master配置了一個slave,不管這個slave是否是第一次連接上Master颓鲜,它都會發(fā)送一個PSYNC命令給master請求復(fù)制數(shù)據(jù)表窘。
master收到PSYNC命令后,會在后臺進(jìn)行數(shù)據(jù)持久化通過bgsave生成最新的rdb快照文件甜滨,持久化期間乐严,master會繼續(xù)接收客戶端的請求,它會把這些可能修改數(shù)據(jù)集的請求緩存在內(nèi)存中衣摩。當(dāng)持久化進(jìn)行完畢以后昂验,master會把這份rdb文件數(shù)據(jù)集發(fā)送給slave,slave會把接收到的數(shù)據(jù)進(jìn)行持久化生成rdb,然后再加載到內(nèi)存中既琴。然后占婉,master再將之前緩存在內(nèi)存中的命令發(fā)送給slave。
當(dāng)master與slave之間的連接由于某些原因而斷開時呛梆,slave能夠自動重連Master锐涯,如果master收到了多個slave并發(fā)連接請求,它只會進(jìn)行一次持久化填物,而不是一個連接一次纹腌,然后再把這一份持久化的數(shù)據(jù)發(fā)送給多個并發(fā)連接的slave。
主從復(fù)制(全量復(fù)制)流程圖:
數(shù)據(jù)部分復(fù)制
當(dāng)master和slave斷開重連后滞磺,一般都會對整份數(shù)據(jù)進(jìn)行復(fù)制升薯。但從redis2.8版本開始,redis改用可以支持部分?jǐn)?shù)據(jù)復(fù)制的命令PSYNC去master同步數(shù)據(jù)击困,slave與master能夠在網(wǎng)絡(luò)連接斷開重連后只進(jìn)行部分?jǐn)?shù)據(jù)復(fù)制(斷點(diǎn)續(xù)傳)涎劈。
master會在其內(nèi)存中創(chuàng)建一個復(fù)制數(shù)據(jù)用的緩存隊列,緩存最近一段時間的數(shù)據(jù)阅茶,master和它所有的slave都維護(hù)了復(fù)制的數(shù)據(jù)下標(biāo)offset和master的進(jìn)程id蛛枚,因此,當(dāng)網(wǎng)絡(luò)連接斷開后脸哀,slave會請求master繼續(xù)進(jìn)行未完成的復(fù)制蹦浦,從所記錄的數(shù)據(jù)下標(biāo)開始。如果master進(jìn)程id變化了撞蜂,或者從節(jié)點(diǎn)數(shù)據(jù)下標(biāo)offset太舊盲镶,已經(jīng)不在master的緩存隊列里了,那么將會進(jìn)行一次全量數(shù)據(jù)的復(fù)制蝌诡。
主從復(fù)制(部分復(fù)制溉贿,斷點(diǎn)續(xù)傳)流程圖:
如果有很多從節(jié)點(diǎn),為了緩解主從復(fù)制風(fēng)暴(多個從節(jié)點(diǎn)同時復(fù)制主節(jié)點(diǎn)導(dǎo)致主節(jié)點(diǎn)壓力過大)浦旱,可以做如下架構(gòu)宇色,讓部分從節(jié)點(diǎn)與從節(jié)點(diǎn)(與主節(jié)點(diǎn)同步)同步數(shù)據(jù)
4.2 哨兵架構(gòu)
sentinel哨兵是特殊的redis服務(wù),不提供讀寫服務(wù)颁湖,主要用來監(jiān)控redis實例節(jié)點(diǎn)代兵。
哨兵架構(gòu)下client端第一次從哨兵找出redis的主節(jié)點(diǎn),后續(xù)就直接訪問redis的主節(jié)點(diǎn)爷狈,不會每次都通過sentinel代理訪問redis的主節(jié)點(diǎn),當(dāng)redis的主節(jié)點(diǎn)發(fā)生變化裳擎,哨兵會第一時間感知到涎永,并且將新的redis主節(jié)點(diǎn)通知給client端(這里面redis的client端一般都實現(xiàn)了訂閱功能,訂閱sentinel發(fā)布的節(jié)點(diǎn)變動消息)。
在redis3.0以前的版本要實現(xiàn)集群一般是借助哨兵sentinel工具來監(jiān)控master節(jié)點(diǎn)的狀態(tài)羡微,如果master節(jié)點(diǎn)異常谷饿,則會做主從切換,將某一臺slave作為master妈倔,哨兵的配置略微復(fù)雜博投,并且性能和高可用性等各方面表現(xiàn)一般,特別是在主從切換的瞬間存在訪問瞬斷的情況盯蝴,而且哨兵模式只有一個主節(jié)點(diǎn)對外提供服務(wù)毅哗,沒法支持很高的并發(fā),且單個主節(jié)點(diǎn)內(nèi)存也不宜設(shè)置得過大捧挺,否則會導(dǎo)致持久化文件過大虑绵,影響數(shù)據(jù)恢復(fù)或主從同步的效率。
單節(jié)點(diǎn)內(nèi)存不超過10G闽烙,過大會導(dǎo)致主從復(fù)制翅睛、aof重寫變得很慢。
4.3 集群架構(gòu)
redis集群是一個由多個主從節(jié)點(diǎn)群組成的分布式服務(wù)器群黑竞,它具有復(fù)制捕发、高可用和分片特性。Redis集群不需要sentinel哨兵·也能完成節(jié)點(diǎn)移除和故障轉(zhuǎn)移的功能很魂。需要將每個節(jié)點(diǎn)設(shè)置成集群模式扎酷,這種集群模式?jīng)]有中心節(jié)點(diǎn),可水平擴(kuò)展莫换,據(jù)官方文檔稱可以線性擴(kuò)展到上萬個節(jié)點(diǎn)(官方推薦不超過1000個節(jié)點(diǎn))霞玄。redis集群的性能和高可用性均優(yōu)于之前版本的哨兵模式,且集群配置非常簡單拉岁。
只有主節(jié)點(diǎn)可以讀寫坷剧,從節(jié)點(diǎn)只是備份。
Redis Cluster 將所有數(shù)據(jù)劃分為 16384 個 slots(槽位)喊暖,每個節(jié)點(diǎn)負(fù)責(zé)其中一部分槽位惫企。槽位的信息存儲于每個節(jié)點(diǎn)中。
當(dāng) Redis Cluster 的客戶端來連接集群時陵叽,它也會得到一份集群的槽位配置信息并將其緩存在客戶端本地狞尔。這樣當(dāng)客戶端要查找某個 key 時,可以直接定位到目標(biāo)節(jié)點(diǎn)巩掺。同時因為槽位的信息可能會存在客戶端與服務(wù)器不一致的情況偏序,還需要糾正機(jī)制來實現(xiàn)槽位信息的校驗調(diào)整。
4.3.1 槽位定位算法
Cluster 默認(rèn)會對 key 值使用 crc16 算法進(jìn)行 hash 得到一個整數(shù)值胖替,然后用這個整數(shù)值對 16384 進(jìn)行取模來得到具體槽位研儒。
HASH_SLOT = CRC16(key) mod 16384
4.3.2 跳轉(zhuǎn)重定位
當(dāng)客戶端向一個錯誤的節(jié)點(diǎn)發(fā)出了指令豫缨,該節(jié)點(diǎn)會發(fā)現(xiàn)指令的 key 所在的槽位并不歸自己管理,這時它會向客戶端發(fā)送一個特殊的跳轉(zhuǎn)指令攜帶目標(biāo)操作的節(jié)點(diǎn)地址端朵,告訴客戶端去連這個節(jié)點(diǎn)去獲取數(shù)據(jù)好芭。客戶端收到指令后除了跳轉(zhuǎn)到正確的節(jié)點(diǎn)上去操作冲呢,還會同步更新糾正本地的槽位映射表緩存舍败,后續(xù)所有 key 將使用新的槽位映射表。
4.3.3 Redis集群節(jié)點(diǎn)間的通信機(jī)制
redis cluster節(jié)點(diǎn)間采取gossip協(xié)議進(jìn)行通信
維護(hù)集群的元數(shù)據(jù)(集群節(jié)點(diǎn)信息敬拓,主從角色邻薯,節(jié)點(diǎn)數(shù)量,各節(jié)點(diǎn)共享的數(shù)據(jù)等)有兩種方式:集中式和gossip
集中式:
優(yōu)點(diǎn)在于元數(shù)據(jù)的更新和讀取恩尾,時效性非常好弛说,一旦元數(shù)據(jù)出現(xiàn)變更立即就會更新到集中式的存儲中,其他節(jié)點(diǎn)讀取的時候立即就可以立即感知到翰意;不足在于所有的元數(shù)據(jù)的更新壓力全部集中在一個地方木人,可能導(dǎo)致元數(shù)據(jù)的存儲壓力。 很多中間件都會借助zookeeper集中式存儲元數(shù)據(jù)冀偶。
gossip:
gossip協(xié)議包含多種消息醒第,包括ping,pong进鸠,meet稠曼,fail等等。
meet:某個節(jié)點(diǎn)發(fā)送meet給新加入的節(jié)點(diǎn)客年,讓新節(jié)點(diǎn)加入集群中霞幅,然后新節(jié)點(diǎn)就會開始與其他節(jié)點(diǎn)進(jìn)行通信;
ping:每個節(jié)點(diǎn)都會頻繁給其他節(jié)點(diǎn)發(fā)送ping量瓜,其中包含自己的狀態(tài)還有自己維護(hù)的集群元數(shù)據(jù)司恳,互相通過ping交換元數(shù)據(jù)(類似自己感知到的集群節(jié)點(diǎn)增加和移除,hash slot信息等)绍傲;
pong: 對ping和meet消息的返回扔傅,包含自己的狀態(tài)和其他信息,也可以用于信息廣播和更新烫饼;
fail: 某個節(jié)點(diǎn)判斷另一個節(jié)點(diǎn)fail之后猎塞,就發(fā)送fail給其他節(jié)點(diǎn),通知其他節(jié)點(diǎn)杠纵,指定的節(jié)點(diǎn)宕機(jī)了荠耽。
gossip協(xié)議的優(yōu)點(diǎn)在于元數(shù)據(jù)的更新比較分散,不是集中在一個地方比藻,更新請求會陸陸續(xù)續(xù)铝量,打到所有節(jié)點(diǎn)上去更新伊履,有一定的延時,降低了壓力款违;缺點(diǎn)在于元數(shù)據(jù)更新有延時可能導(dǎo)致集群的一些操作會有一些滯后。
gossip通信的10000端口
每個節(jié)點(diǎn)都有一個專門用于節(jié)點(diǎn)間gossip通信的端口群凶,就是自己提供服務(wù)的端口號+10000插爹,比如7001,那么用于節(jié)點(diǎn)間通信的就是17001端口请梢。 每個節(jié)點(diǎn)每隔一段時間都會往另外幾個節(jié)點(diǎn)發(fā)送ping消息赠尾,同時其他幾點(diǎn)接收到ping消息之后返回pong消息。
4.3.4 網(wǎng)絡(luò)抖動
真實世界的機(jī)房網(wǎng)絡(luò)往往并不是風(fēng)平浪靜的毅弧,它們經(jīng)常會發(fā)生各種各樣的小問題气嫁。比如網(wǎng)絡(luò)抖動就是非常常見的一種現(xiàn)象,突然之間部分連接變得不可訪問够坐,然后很快又恢復(fù)正常寸宵。
為解決這種問題,Redis Cluster 提供了一種選項cluster-node-timeout元咙,表示當(dāng)某個節(jié)點(diǎn)持續(xù) timeout 的時間失聯(lián)時本股,才可以認(rèn)定該節(jié)點(diǎn)出現(xiàn)故障眨攘,需要進(jìn)行主從切換。如果沒有這個選項,網(wǎng)絡(luò)抖動會導(dǎo)致主從頻繁切換 (數(shù)據(jù)的重新復(fù)制)遭殉。
4.3.5 Redis集群選舉原理分析
當(dāng)slave發(fā)現(xiàn)自己的master變?yōu)镕AIL狀態(tài)時,便嘗試進(jìn)行Failover舍咖,以期成為新的master达箍。由于掛掉的master可能會有多個slave,從而存在多個slave競爭成為master節(jié)點(diǎn)的過程奢赂, 其過程如下:
1.slave發(fā)現(xiàn)自己的master變?yōu)镕AIL
2.將自己記錄的集群currentEpoch加1陪白,并廣播FAILOVER_AUTH_REQUEST 信息
3.其他節(jié)點(diǎn)收到該信息,只有master響應(yīng)呈驶,判斷請求者的合法性拷泽,并發(fā)送FAILOVER_AUTH_ACK,對每一個epoch只發(fā)送一次ack
4.嘗試failover的slave收集master返回的FAILOVER_AUTH_ACK
5.slave收到超過半數(shù)master的ack后變成新Master(這里解釋了集群為什么至少需要三個主節(jié)點(diǎn)袖瞻,如果只有兩個司致,當(dāng)其中一個掛了,只剩一個主節(jié)點(diǎn)是不能選舉成功的)
6.slave廣播Pong消息通知其他集群節(jié)點(diǎn)聋迎。
從節(jié)點(diǎn)并不是在主節(jié)點(diǎn)一進(jìn)入 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復(fù)制數(shù)據(jù)的總量的rank。Rank越小代表已復(fù)制的數(shù)據(jù)越新拄轻。這種方式下颅围,持有最新數(shù)據(jù)的slave將會首先發(fā)起選舉(理論上)。
4.3.6 集群腦裂數(shù)據(jù)丟失問題
redis集群沒有過半機(jī)制會有腦裂問題恨搓,網(wǎng)絡(luò)分區(qū)導(dǎo)致腦裂后多個主節(jié)點(diǎn)對外提供寫服務(wù)院促,一旦網(wǎng)絡(luò)分區(qū)恢復(fù),會將其中一個主節(jié)點(diǎn)變?yōu)閺墓?jié)點(diǎn)斧抱,這時會有大量數(shù)據(jù)丟失常拓。
規(guī)避方法可以在redis配置里加上參數(shù)(這種方法不可能百分百避免數(shù)據(jù)丟失,參考集群leader選舉機(jī)制):
min-replicas-to-write 1 //寫數(shù)據(jù)成功最少同步的slave數(shù)量辉浦,這個數(shù)量可以模仿大于半數(shù)機(jī)制配置弄抬,比如集群總共三個節(jié)點(diǎn)可以配置1,加上leader就是2宪郊,超過了半數(shù)
注意:這個配置在一定程度上會影響集群的可用性掂恕,比如slave要是少于1個,這個集群就算leader正常也不能提供服務(wù)了废膘,需要具體場景權(quán)衡選擇竹海。