您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Redis緩存在系統(tǒng)中用來做什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
一、緩存在系統(tǒng)中用來做什么?
1. 少量數(shù)據(jù)存儲(chǔ),高速讀寫訪問。通過數(shù)據(jù)全部in-momery 的方式來保證高速訪問,同時(shí)提供數(shù)據(jù)落地的功能,實(shí)際這正是Redis最主要的適用場(chǎng)景。
2. 海量數(shù)據(jù)存儲(chǔ),分布式系統(tǒng)支持,數(shù)據(jù)一致性保證,方便的集群節(jié)點(diǎn)添加/刪除。Redis3.0以后開始支持集群,實(shí)現(xiàn)了半自動(dòng)化的數(shù)據(jù)分片,不過需要smart-client的支持。
二、從不同的角度來詳細(xì)介紹redis
網(wǎng)絡(luò)模型:Redis使用單線程的IO復(fù)用模型,自己封裝了一個(gè)簡(jiǎn)單的AeEvent事件處理框架,主要實(shí)現(xiàn)了epoll、kqueue和select,對(duì)于單純只有IO操作來說,單線程可以將速度優(yōu)勢(shì)發(fā)揮到最大,但是Redis也提供了一些簡(jiǎn)單的計(jì)算功能,比如排序、聚合等,對(duì)于這些操作,單線程模型實(shí)際會(huì)嚴(yán)重影響整體吞吐量,CPU計(jì)算過程中,整個(gè)IO調(diào)度都是被阻塞住的。
內(nèi)存管理:Redis使用現(xiàn)場(chǎng)申請(qǐng)內(nèi)存的方式來存儲(chǔ)數(shù)據(jù),并且很少使用free-list等方式來優(yōu)化內(nèi)存分配,會(huì)在一定程度上存在內(nèi)存碎片,Redis跟據(jù)存儲(chǔ)命令參數(shù),會(huì)把帶過期時(shí)間的數(shù)據(jù)單獨(dú)存放在一起,并把它們稱為臨時(shí)數(shù)據(jù),非臨時(shí)數(shù)據(jù)是永遠(yuǎn)不會(huì)被剔除的,即便物理內(nèi)存不夠,導(dǎo)致swap也不會(huì)剔除任何非臨時(shí)數(shù)據(jù)(但會(huì)嘗試剔除部分臨時(shí)數(shù)據(jù)),這點(diǎn)上Redis更適合作為存儲(chǔ)而不是cache。
數(shù)據(jù)一致性問題:在一致性問題上,個(gè)人感覺redis沒有memcached實(shí)現(xiàn)的好,Memcached提供了cas命令,可以保證多個(gè)并發(fā)訪問操作同一份數(shù)據(jù)的一致性問題。 Redis沒有提供cas 命令,并不能保證這點(diǎn),不過Redis提供了事務(wù)的功能,可以保證一串命令的原子性,中間不會(huì)被任何操作打斷。
支持的KEY類型:Redis除key/value之外,還支持list,set,sorted set,hash等眾多數(shù)據(jù)結(jié)構(gòu),提供了KEYS進(jìn)行枚舉操作,但不能在線上使用,如果需要枚舉線上數(shù)據(jù),Redis提供了工具可以直接掃描其dump文件,枚舉出所有數(shù)據(jù),Redis還同時(shí)提供了持久化和復(fù)制等功能。
客戶端支持:redis官方提供了豐富的客戶端支持,包括了絕大多數(shù)編程語言的客戶端,比如我此次測(cè)試就選擇了官方推薦了Java客戶端Jedis.里面提供了豐富的接口、方法使得開發(fā)人員無需關(guān)系內(nèi)部的數(shù)據(jù)分片、讀取數(shù)據(jù)的路由等,只需簡(jiǎn)單的調(diào)用即可,非常方便。
數(shù)據(jù)復(fù)制:從2.8開始,Slave會(huì)周期性(每秒一次)發(fā)起一個(gè)Ack確認(rèn)復(fù)制流(replication stream)被處理進(jìn)度, Redis復(fù)制工作原理詳細(xì)過程如下:
1. 如果設(shè)置了一個(gè)Slave,無論是第一次連接還是重連到Master,它都會(huì)發(fā)出一個(gè)SYNC命令;
2. 當(dāng)Master收到SYNC命令之后,會(huì)做兩件事:
a) Master執(zhí)行BGSAVE:后臺(tái)寫數(shù)據(jù)到磁盤(rdb快照);
b) Master同時(shí)將新收到的寫入和修改數(shù)據(jù)集的命令存入緩沖區(qū)(非查詢類);
3. 當(dāng)Master在后臺(tái)把數(shù)據(jù)保存到快照文件完成之后,Master會(huì)把這個(gè)快照文件傳送給Slave,而Slave則把內(nèi)存清空后,加載該文件到內(nèi)存中;
4. 而Master也會(huì)把此前收集到緩沖區(qū)中的命令,通過Reids命令協(xié)議形式轉(zhuǎn)發(fā)給Slave,Slave執(zhí)行這些命令,實(shí)現(xiàn)和Master的同步;
5. Master/Slave此后會(huì)不斷通過異步方式進(jìn)行命令的同步,達(dá)到最終數(shù)據(jù)的同步一致;
6. 需要注意的是Master和Slave之間一旦發(fā)生重連都會(huì)引發(fā)全量同步操作。但在2.8之后,也可能是部分同步操作。
2.8開始,當(dāng)Master和Slave之間的連接斷開之后,他們之間可以采用持續(xù)復(fù)制處理方式代替采用全量同步。
Master端為復(fù)制流維護(hù)一個(gè)內(nèi)存緩沖區(qū)(in-memory backlog),記錄最近發(fā)送的復(fù)制流命令;同時(shí),Master和Slave之間都維護(hù)一個(gè)復(fù)制偏移量(replication offset)和當(dāng)前Master服務(wù)器ID(Masterrun id)。
當(dāng)網(wǎng)絡(luò)斷開,Slave嘗試重連時(shí):
a. 如果MasterID相同(即仍是斷網(wǎng)前的Master服務(wù)器),并且從斷開時(shí)到當(dāng)前時(shí)刻的歷史命令依然在Master的內(nèi)存緩沖區(qū)中存在,則Master會(huì)將缺失的這段時(shí)間的所有命令發(fā)送給Slave執(zhí)行,然后復(fù)制工作就可以繼續(xù)執(zhí)行了;
b. 否則,依然需要全量復(fù)制操作。
讀寫分離:redis支持讀寫分離,而且使用簡(jiǎn)單,只需在配置文件中把redis讀服務(wù)器和寫服務(wù)器進(jìn)行配置,多個(gè)服務(wù)器使用逗號(hào)分開如下:
水平動(dòng)態(tài)擴(kuò)展:歷時(shí)三年之久,終于等來了期待已由的Redis 3.0。新版本主要是實(shí)現(xiàn)了Cluster的功能,增刪集群節(jié)點(diǎn)后會(huì)自動(dòng)的進(jìn)行數(shù)據(jù)遷移。實(shí)現(xiàn) Redis 集群在線重配置的核心就是將槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)的能力。因?yàn)橐粋€(gè)哈希槽實(shí)際上就是一些鍵的集合, 所以 Redis 集群在重哈希(rehash)時(shí)真正要做的,就是將一些鍵從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)。
數(shù)據(jù)淘汰策略:redis 內(nèi)存數(shù)據(jù)集大小上升到一定大小的時(shí)候,就會(huì)施行數(shù)據(jù)淘汰策略。redis 提供 6種數(shù)據(jù)淘汰策略:
volatile-lru:從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選最近最少使用的數(shù)據(jù)淘汰
volatile-ttl:從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中挑選將要過期的數(shù)據(jù)淘汰
volatile-random:從已設(shè)置過期時(shí)間的數(shù)據(jù)集(server.db[i].expires)中任意選擇數(shù)據(jù)淘汰
allkeys-lru:從數(shù)據(jù)集(server.db[i].dict)中挑選最近最少使用的數(shù)據(jù)淘汰
allkeys-random:從數(shù)據(jù)集(server.db[i].dict)中任意選擇數(shù)據(jù)淘汰
no-enviction(驅(qū)逐):禁止驅(qū)逐數(shù)據(jù)
三、集群(即分布式)
下面詳細(xì)介紹一下redis的集群功能,從3.0以后的版本開始支持集群功能,也就是正真意義上實(shí)現(xiàn)了分布式。
Redis 集群是一個(gè)分布式(distributed)、容錯(cuò)(fault-tolerant)的 Redis 實(shí)現(xiàn), 集群可以使用的功能是普通單機(jī) Redis 所能使用的功能的一個(gè)子集(subset)。
Redis 集群中不存在中心(central)節(jié)點(diǎn)或者代理(proxy)節(jié)點(diǎn), 集群的其中一個(gè)主要設(shè)計(jì)目標(biāo)是達(dá)到線性可擴(kuò)展性(linear scalability)。
Redis 集群為了保證一致性(consistency)而犧牲了一部分容錯(cuò)性: 系統(tǒng)會(huì)在保證對(duì)網(wǎng)絡(luò)斷線(netsplit)和節(jié)點(diǎn)失效(node failure)具有有限(limited)抵抗力的前提下,盡可能地保持?jǐn)?shù)據(jù)的一致性。
集群特性:
(1)所有的redis節(jié)點(diǎn)彼此互聯(lián)(PING-PONG機(jī)制),內(nèi)部使用二進(jìn)制協(xié)議優(yōu)化傳輸速度和帶寬。
(2)節(jié)點(diǎn)的fail是通過集群中超過半數(shù)的節(jié)點(diǎn)檢測(cè)失效時(shí)才生效。
(3)客戶端與redis節(jié)點(diǎn)直連,不需要中間proxy層.客戶端不需要連接集群所有節(jié)點(diǎn),連接集群中任何一個(gè)可用節(jié)點(diǎn)即可。
(4)redis-cluster把所有的物理節(jié)點(diǎn)映射到[0-16383]slot上,cluster 負(fù)責(zé)維護(hù)node<->slot<->value
Redis 集群實(shí)現(xiàn)的功能子集:
Redis集群實(shí)現(xiàn)了單機(jī) Redis 中, 所有處理單個(gè)數(shù)據(jù)庫鍵的命令。針對(duì)多個(gè)數(shù)據(jù)庫鍵的復(fù)雜計(jì)算操作, 比如集合的并集操作、合集操作沒有被實(shí)現(xiàn),那些理論上需要使用多個(gè)節(jié)點(diǎn)的多個(gè)數(shù)據(jù)庫鍵才能完成的命令也沒有被實(shí)現(xiàn)。在將來, 用戶也許可以通過 MIGRATE COPY 命令,在集群的計(jì)算節(jié)點(diǎn)(computation node)中執(zhí)行針對(duì)多個(gè)數(shù)據(jù)庫鍵的只讀操作, 但集群本身不會(huì)去實(shí)現(xiàn)那些需要將多個(gè)數(shù)據(jù)庫鍵在多個(gè)節(jié)點(diǎn)中移來移去的復(fù)雜多鍵命令。
Redis 集群不像單機(jī)Redis 那樣支持多數(shù)據(jù)庫功能, 集群只使用默認(rèn)的 0 號(hào)數(shù)據(jù)庫, 并且不能使用 SELECT 命令。
Redis 集群協(xié)議中的客戶端和服務(wù)器:
Redis 集群中的節(jié)點(diǎn)有以下責(zé)任:
1. 持有鍵值對(duì)數(shù)據(jù)。
2. 記錄集群的狀態(tài),包括鍵到正確節(jié)點(diǎn)的映射(mappingkeys to right nodes)。
3. 自動(dòng)發(fā)現(xiàn)其他節(jié)點(diǎn),識(shí)別工作不正常的節(jié)點(diǎn),并在有需要時(shí),在從節(jié)點(diǎn)中選舉出新的主節(jié)點(diǎn)。
為了執(zhí)行以上列出的任務(wù), 集群中的每個(gè)節(jié)點(diǎn)都與其他節(jié)點(diǎn)建立起了“集群連接(cluster bus)”, 該連接是一個(gè) TCP 連接, 使用二進(jìn)制協(xié)議進(jìn)行通訊。
節(jié)點(diǎn)之間使用Gossip 協(xié)議 來進(jìn)行以下工作:
1. 傳播(propagate)關(guān)于集群的信息,以此來發(fā)現(xiàn)新的節(jié)點(diǎn)。
2. 向其他節(jié)點(diǎn)發(fā)送 PING 數(shù)據(jù)包,以此來檢查目標(biāo)節(jié)點(diǎn)是否正常運(yùn)作。
3. 在特定事件發(fā)生時(shí),發(fā)送集群信息。
4. 除此之外, 集群連接還用于在集群中發(fā)布或訂閱信息。
因?yàn)榧汗?jié)點(diǎn)不能代理(proxy)命令請(qǐng)求, 所以客戶端應(yīng)該在節(jié)點(diǎn)返回 -MOVED 或者 -ASK 轉(zhuǎn)向(redirection)錯(cuò)誤時(shí),自行將命令請(qǐng)求轉(zhuǎn)發(fā)至其他節(jié)點(diǎn)。因?yàn)榭蛻舳丝梢宰杂傻叵蚣褐械娜魏我粋€(gè)節(jié)點(diǎn)發(fā)送命令請(qǐng)求, 并可以在有需要時(shí), 根據(jù)轉(zhuǎn)向錯(cuò)誤所提供的信息, 將命令轉(zhuǎn)發(fā)至正確的節(jié)點(diǎn),所以在理論上來說, 客戶端是無須保存集群狀態(tài)信息的。不過, 如果客戶端可以將鍵和節(jié)點(diǎn)之間的映射信息保存起來, 可以有效地減少可能出現(xiàn)的轉(zhuǎn)向次數(shù), 籍此提升命令執(zhí)行的效率。
鍵分布模型
Redis 集群的鍵空間被分割為 16384 個(gè)槽(slot), 集群的最大節(jié)點(diǎn)數(shù)量也是 16384 個(gè)。
推薦的最大節(jié)點(diǎn)數(shù)量為 1000 個(gè)左右。每個(gè)主節(jié)點(diǎn)都負(fù)責(zé)處理 16384 個(gè)哈希槽的其中一部分。
當(dāng)我們說一個(gè)集群處于“穩(wěn)定”(stable)狀態(tài)時(shí), 指的是集群沒有在執(zhí)行重配(reconfiguration)操作,每個(gè)哈希槽都只由一個(gè)節(jié)點(diǎn)進(jìn)行處理。重配置指的是將某個(gè)/某些槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)。一個(gè)主節(jié)點(diǎn)可以有任意多個(gè)從節(jié)點(diǎn),這些從節(jié)點(diǎn)用于在主節(jié)點(diǎn)發(fā)生網(wǎng)絡(luò)斷線或者節(jié)點(diǎn)失效時(shí), 對(duì)主節(jié)點(diǎn)進(jìn)行替換。
集群節(jié)點(diǎn)屬性:
每個(gè)節(jié)點(diǎn)在集群中都有一個(gè)獨(dú)一無二的 ID , 該 ID 是一個(gè)十六進(jìn)制表示的 160 位隨機(jī)數(shù), 在節(jié)點(diǎn)第一次啟動(dòng)時(shí)由 /dev/urandom 生成。
節(jié)點(diǎn)會(huì)將它的 ID 保存到配置文件, 只要這個(gè)配置文件不被刪除,節(jié)點(diǎn)就會(huì)一直沿用這個(gè) ID 。節(jié)點(diǎn) ID 用于標(biāo)識(shí)集群中的每個(gè)節(jié)點(diǎn)。一個(gè)節(jié)點(diǎn)可以改變它的 IP 和端口號(hào), 而不改變節(jié)點(diǎn) ID 。集群可以自動(dòng)識(shí)別出 IP/端口號(hào)的變化, 并將這一信息通過 Gossip 協(xié)議廣播給其他節(jié)點(diǎn)知道。
以下是每個(gè)節(jié)點(diǎn)都有的關(guān)聯(lián)信息, 并且節(jié)點(diǎn)會(huì)將這些信息發(fā)送給其他節(jié)點(diǎn):
1. 節(jié)點(diǎn)所使用的 IP 地址和 TCP 端口號(hào)。
2. 節(jié)點(diǎn)的標(biāo)志(flags)。
3. 節(jié)點(diǎn)負(fù)責(zé)處理的哈希槽。
4. 節(jié)點(diǎn)最近一次使用集群連接發(fā)送 PING 數(shù)據(jù)包(packet)的時(shí)間。
5. 節(jié)點(diǎn)最近一次在回復(fù)中接收到 PONG 數(shù)據(jù)包的時(shí)間。
6. 集群將該節(jié)點(diǎn)標(biāo)記為下線的時(shí)間。
7. 該節(jié)點(diǎn)的從節(jié)點(diǎn)數(shù)量。
8. 如果該節(jié)點(diǎn)是從節(jié)點(diǎn)的話,那么它會(huì)記錄主節(jié)點(diǎn)的節(jié)點(diǎn) ID 。如果這是一個(gè)主節(jié)點(diǎn)的話,那么主節(jié)點(diǎn) ID 這一欄的值為 0000000 。
以上信息的其中一部分可以通過向集群中的任意節(jié)點(diǎn)(主節(jié)點(diǎn)或者從節(jié)點(diǎn)都可以)發(fā)送 CLUSTER NODES 命令來獲得。
節(jié)點(diǎn)握手:
節(jié)點(diǎn)總是應(yīng)答(accept)來自集群連接端口的連接請(qǐng)求,并對(duì)接收到的 PING 數(shù)據(jù)包進(jìn)行回復(fù), 即使這個(gè) PING 數(shù)據(jù)包來自不可信的節(jié)點(diǎn)。然而,除了 PING 之外, 節(jié)點(diǎn)會(huì)拒絕其他所有并非來自集群節(jié)點(diǎn)的數(shù)據(jù)包。要讓一個(gè)節(jié)點(diǎn)承認(rèn)另一個(gè)節(jié)點(diǎn)同屬于一個(gè)集群,只有以下兩種方法:
1. 一個(gè)節(jié)點(diǎn)可以通過向另一個(gè)節(jié)點(diǎn)發(fā)送 MEET 信息,來強(qiáng)制讓接收信息的節(jié)點(diǎn)承認(rèn)發(fā)送信息的節(jié)點(diǎn)為集群中的一份子。 一個(gè)節(jié)點(diǎn)僅在管理員顯式地向它發(fā)送CLUSTER MEET ipport 命令時(shí), 才會(huì)向另一個(gè)節(jié)點(diǎn)發(fā)送 MEET 信息。
2. 如果一個(gè)可信節(jié)點(diǎn)向另一個(gè)節(jié)點(diǎn)傳播第三者節(jié)點(diǎn)的信息, 那么接收信息的那個(gè)節(jié)點(diǎn)也會(huì)將第三者節(jié)點(diǎn)識(shí)別為集群中的一份子。也即是說, 如果 A 認(rèn)識(shí) B , B 認(rèn)識(shí) C , 并且 B 向 A 傳播關(guān)于 C 的信息, 那么 A 也會(huì)將 C 識(shí)別為集群中的一份子, 并嘗試連接 C 。
這意味著如果我們將一個(gè)/一些新節(jié)點(diǎn)添加到一個(gè)集群中, 那么這個(gè)/這些新節(jié)點(diǎn)最終會(huì)和集群中已有的其他所有節(jié)點(diǎn)連接起來。
這說明只要管理員使用 CLUSTER MEET 命令顯式地指定了可信關(guān)系,集群就可以自動(dòng)發(fā)現(xiàn)其他節(jié)點(diǎn)。這種節(jié)點(diǎn)識(shí)別機(jī)制通過防止不同的 Redis 集群因?yàn)?IP 地址變更或者其他網(wǎng)絡(luò)事件的發(fā)生而產(chǎn)生意料之外的聯(lián)合(mix), 從而使得集群更具健壯性。當(dāng)節(jié)點(diǎn)的網(wǎng)絡(luò)連接斷開時(shí),它會(huì)主動(dòng)連接其他已知的節(jié)點(diǎn)。
MOVED 轉(zhuǎn)向:
一個(gè) Redis 客戶端可以向集群中的任意節(jié)點(diǎn)(包括從節(jié)點(diǎn))發(fā)送命令請(qǐng)求。節(jié)點(diǎn)會(huì)對(duì)命令請(qǐng)求進(jìn)行分析, 如果該命令是集群可以執(zhí)行的命令, 那么節(jié)點(diǎn)會(huì)查找這個(gè)命令所要處理的鍵所在的槽。如果要查找的哈希槽正好就由接收到命令的節(jié)點(diǎn)負(fù)責(zé)處理,那么節(jié)點(diǎn)就直接執(zhí)行這個(gè)命令。另一方面, 如果所查找的槽不是由該節(jié)點(diǎn)處理的話, 節(jié)點(diǎn)將查看自身內(nèi)部所保存的哈希槽到節(jié)點(diǎn) ID 的映射記錄,并向客戶端回復(fù)一個(gè) MOVED 錯(cuò)誤。
即使客戶端在重新發(fā)送 GET 命令之前, 等待了非常久的時(shí)間,以至于集群又再次更改了配置, 使得節(jié)點(diǎn) 127.0.0.1:6381 已經(jīng)不再處理槽 3999 , 那么當(dāng)客戶端向節(jié)點(diǎn) 127.0.0.1:6381 發(fā)送 GET 命令的時(shí)候, 節(jié)點(diǎn)將再次向客戶端返回 MOVED 錯(cuò)誤, 指示現(xiàn)在負(fù)責(zé)處理槽 3999 的節(jié)點(diǎn)。
雖然我們用 ID 來標(biāo)識(shí)集群中的節(jié)點(diǎn), 但是為了讓客戶端的轉(zhuǎn)向操作盡可能地簡(jiǎn)單,,節(jié)點(diǎn)在 MOVED 錯(cuò)誤中直接返回目標(biāo)節(jié)點(diǎn)的 IP 和端口號(hào),而不是目標(biāo)節(jié)點(diǎn)的 ID 。但一個(gè)客戶端應(yīng)該記錄(memorize)下“槽 3999 由節(jié)點(diǎn) 127.0.0.1:6381 負(fù)責(zé)處理“這一信息, 這樣當(dāng)再次有命令需要對(duì)槽 3999 執(zhí)行時(shí), 客戶端就可以加快尋找正確節(jié)點(diǎn)的速度。
注意, 當(dāng)集群處于穩(wěn)定狀態(tài)時(shí), 所有客戶端最終都會(huì)保存有一個(gè)哈希槽至節(jié)點(diǎn)的映射記錄(map of hash slots to nodes), 使得集群非常高效: 客戶端可以直接向正確的節(jié)點(diǎn)發(fā)送命令請(qǐng)求, 無須轉(zhuǎn)向、代理或者其他任何可能發(fā)生單點(diǎn)故障(single point failure)的實(shí)體(entiy)。
除了 MOVED轉(zhuǎn)向錯(cuò)誤之外, 一個(gè)客戶端還應(yīng)該可以處理稍后介紹的 ASK 轉(zhuǎn)向錯(cuò)誤。
集群在線重配置:
Redis 集群支持在集群運(yùn)行的過程中添加或者移除節(jié)點(diǎn)。實(shí)際上, 節(jié)點(diǎn)的添加操作和節(jié)點(diǎn)的刪除操作可以抽象成同一個(gè)操作,那就是, 將哈希槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn):添加一個(gè)新節(jié)點(diǎn)到集群, 等于將其他已存在節(jié)點(diǎn)的槽移動(dòng)到一個(gè)空白的新節(jié)點(diǎn)里面。從集群中移除一個(gè)節(jié)點(diǎn), 等于將被移除節(jié)點(diǎn)的所有槽移動(dòng)到集群的其他節(jié)點(diǎn)上面去。
因此, 實(shí)現(xiàn)Redis 集群在線重配置的核心就是將槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)的能力。 因?yàn)橐粋€(gè)哈希槽實(shí)際上就是一些鍵的集合, 所以 Redis 集群在重哈希(rehash)時(shí)真正要做的, 就是將一些鍵從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)。
要理解Redis 集群如何將槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn), 我們需要對(duì) CLUSTER 命令的各個(gè)子命令進(jìn)行介紹,這些命理負(fù)責(zé)管理集群節(jié)點(diǎn)的槽轉(zhuǎn)換表(slots translation table)。
以下是CLUSTER 命令可用的子命令:
最開頭的兩條命令A(yù)DDSLOTS 和 DELSLOTS 分別用于向節(jié)點(diǎn)指派(assign)或者移除節(jié)點(diǎn),當(dāng)槽被指派或者移除之后, 節(jié)點(diǎn)會(huì)將這一信息通過 Gossip 協(xié)議傳播到整個(gè)集群。 ADDSLOTS 命令通常在新創(chuàng)建集群時(shí), 作為一種快速地將各個(gè)槽指派給各個(gè)節(jié)點(diǎn)的手段來使用。
CLUSTERSETSLOT slot NODE node 子命令可以將指定的槽 slot 指派給節(jié)點(diǎn)node 。
至于CLUSTER SETSLOT slot MIGRATING node 命令和 CLUSTER SETSLOTslot IMPORTING node 命令, 前者用于將給定節(jié)點(diǎn) node 中的槽 slot 遷移出節(jié)點(diǎn), 而后者用于將給定槽 slot導(dǎo)入到節(jié)點(diǎn) node :
當(dāng)一個(gè)槽被設(shè)置為MIGRATING 狀態(tài)時(shí), 原來持有這個(gè)槽的節(jié)點(diǎn)仍然會(huì)繼續(xù)接受關(guān)于這個(gè)槽的命令請(qǐng)求, 但只有命令所處理的鍵仍然存在于節(jié)點(diǎn)時(shí), 節(jié)點(diǎn)才會(huì)處理這個(gè)命令請(qǐng)求。
如果命令所使用的鍵不存在與該節(jié)點(diǎn), 那么節(jié)點(diǎn)將向客戶端返回一個(gè) -ASK 轉(zhuǎn)向(redirection)錯(cuò)誤, 告知客戶端, 要將命令請(qǐng)求發(fā)送到槽的遷移目標(biāo)節(jié)點(diǎn)。
當(dāng)一個(gè)槽被設(shè)置為IMPORTING 狀態(tài)時(shí), 節(jié)點(diǎn)僅在接收到 ASKING 命令之后, 才會(huì)接受關(guān)于這個(gè)槽的命令請(qǐng)求。
如果客戶端沒有向節(jié)點(diǎn)發(fā)送 ASKING 命令, 那么節(jié)點(diǎn)會(huì)使用 -MOVED 轉(zhuǎn)向錯(cuò)誤將命令請(qǐng)求轉(zhuǎn)向至真正負(fù)責(zé)處理這個(gè)槽的節(jié)點(diǎn)。
上面關(guān)于MIGRATING 和 IMPORTING 的說明有些難懂, 讓我們用一個(gè)實(shí)際的實(shí)例來說明一下。
假設(shè)現(xiàn)在, 我們有 A 和 B 兩個(gè)節(jié)點(diǎn), 并且我們想將槽8 從節(jié)點(diǎn) A 移動(dòng)到節(jié)點(diǎn) B , 于是我們:
向節(jié)點(diǎn) B 發(fā)送命令 CLUSTER SETSLOT 8 IMPORTING A
向節(jié)點(diǎn) A 發(fā)送命令 CLUSTER SETSLOT 8 MIGRATING B
每當(dāng)客戶端向其他節(jié)點(diǎn)發(fā)送關(guān)于哈希槽 8 的命令請(qǐng)求時(shí), 這些節(jié)點(diǎn)都會(huì)向客戶端返回指向節(jié)點(diǎn) A 的轉(zhuǎn)向信息:
如果命令要處理的鍵已經(jīng)存在于槽 8 里面, 那么這個(gè)命令將由節(jié)點(diǎn) A 處理。
如果命令要處理的鍵未存在于槽 8 里面(比如說,要向槽添加一個(gè)新的鍵), 那么這個(gè)命令由節(jié)點(diǎn) B 處理。
這種機(jī)制將使得節(jié)點(diǎn) A 不再創(chuàng)建關(guān)于槽 8 的任何新鍵。
與此同時(shí), 一個(gè)特殊的客戶端 redis-trib 以及 Redis 集群配置程序(configuration utility)會(huì)將節(jié)點(diǎn) A 中槽 8 里面的鍵移動(dòng)到節(jié)點(diǎn) B 。
鍵的移動(dòng)操作由以下兩個(gè)命令執(zhí)行:
CLUSTERGETKEYSINSLOT slot count
上面的命令會(huì)讓節(jié)點(diǎn)返回 count 個(gè) slot 槽中的鍵, 對(duì)于命令所返回的每個(gè)鍵, redis-trib 都會(huì)向節(jié)點(diǎn) A 發(fā)送一條 MIGRATE 命令, 該命令會(huì)將所指定的鍵原子地(atomic)從節(jié)點(diǎn) A 移動(dòng)到節(jié)點(diǎn) B (在移動(dòng)鍵期間,兩個(gè)節(jié)點(diǎn)都會(huì)處于阻塞狀態(tài),以免出現(xiàn)競(jìng)爭(zhēng)條件)。
以下為MIGRATE 命令的運(yùn)作原理:
MIGRATEtarget_host target_port key target_database id timeout
執(zhí)行MIGRATE 命令的節(jié)點(diǎn)會(huì)連接到 target 節(jié)點(diǎn), 并將序列化后的 key 數(shù)據(jù)發(fā)送給 target , 一旦 target 返回 OK , 節(jié)點(diǎn)就將自己的 key 從數(shù)據(jù)庫中刪除。
從一個(gè)外部客戶端的視角來看, 在某個(gè)時(shí)間點(diǎn)上, 鍵 key 要么存在于節(jié)點(diǎn) A , 要么存在于節(jié)點(diǎn) B , 但不會(huì)同時(shí)存在于節(jié)點(diǎn) A 和節(jié)點(diǎn) B 。
因?yàn)?Redis集群只使用 0 號(hào)數(shù)據(jù)庫, 所以當(dāng) MIGRATE 命令被用于執(zhí)行集群操作時(shí), target_database 的值總是 0 。
target_database參數(shù)的存在是為了讓 MIGRATE 命令成為一個(gè)通用命令, 從而可以作用于集群以外的其他功能。
我們對(duì)MIGRATE 命令做了優(yōu)化, 使得它即使在傳輸包含多個(gè)元素的列表鍵這樣的復(fù)雜數(shù)據(jù)時(shí), 也可以保持高效。
不過, 盡管MIGRATE 非常高效, 對(duì)一個(gè)鍵非常多、并且鍵的數(shù)據(jù)量非常大的集群來說, 集群重配置還是會(huì)占用大量的時(shí)間, 可能會(huì)導(dǎo)致集群沒辦法適應(yīng)那些對(duì)于響應(yīng)時(shí)間有嚴(yán)格要求的應(yīng)用程序。
ASK 轉(zhuǎn)向:
在之前介紹 MOVED 轉(zhuǎn)向的時(shí)候, 我們說除了 MOVED 轉(zhuǎn)向之外, 還有另一種 ASK 轉(zhuǎn)向。當(dāng)節(jié)點(diǎn)需要讓一個(gè)客戶端長(zhǎng)期地(permanently)將針對(duì)某個(gè)槽的命令請(qǐng)求發(fā)送至另一個(gè)節(jié)點(diǎn)時(shí),節(jié)點(diǎn)向客戶端返回 MOVED 轉(zhuǎn)向。另一方面, 當(dāng)節(jié)點(diǎn)需要讓客戶端僅僅在下一個(gè)命令請(qǐng)求中轉(zhuǎn)向至另一個(gè)節(jié)點(diǎn)時(shí), 節(jié)點(diǎn)向客戶端返回 ASK 轉(zhuǎn)向。
比如說, 在我們上一節(jié)列舉的槽 8 的例子中, 因?yàn)椴?8 所包含的各個(gè)鍵分散在節(jié)點(diǎn) A 和節(jié)點(diǎn) B 中, 所以當(dāng)客戶端在節(jié)點(diǎn) A 中沒找到某個(gè)鍵時(shí), 它應(yīng)該轉(zhuǎn)向到節(jié)點(diǎn) B 中去尋找, 但是這種轉(zhuǎn)向應(yīng)該僅僅影響一次命令查詢,而不是讓客戶端每次都直接去查找節(jié)點(diǎn) B : 在節(jié)點(diǎn) A 所持有的屬于槽 8 的鍵沒有全部被遷移到節(jié)點(diǎn) B 之前, 客戶端應(yīng)該先訪問節(jié)點(diǎn) A , 然后再訪問節(jié)點(diǎn) B 。因?yàn)檫@種轉(zhuǎn)向只針對(duì) 16384 個(gè)槽中的其中一個(gè)槽, 所以轉(zhuǎn)向?qū)涸斐傻男阅軗p耗屬于可接受的范圍。
因?yàn)樯鲜鲈颍?如果我們要在查找節(jié)點(diǎn) A 之后, 繼續(xù)查找節(jié)點(diǎn) B , 那么客戶端在向節(jié)點(diǎn) B 發(fā)送命令請(qǐng)求之前, 應(yīng)該先發(fā)送一個(gè) ASKING 命令, 否則這個(gè)針對(duì)帶有IMPORTING 狀態(tài)的槽的命令請(qǐng)求將被節(jié)點(diǎn) B 拒絕執(zhí)行。接收到客戶端 ASKING 命令的節(jié)點(diǎn)將為客戶端設(shè)置一個(gè)一次性的標(biāo)志(flag), 使得客戶端可以執(zhí)行一次針對(duì) IMPORTING 狀態(tài)的槽的命令請(qǐng)求。從客戶端的角度來看, ASK 轉(zhuǎn)向的完整語義(semantics)如下:
1. 如果客戶端接收到 ASK 轉(zhuǎn)向, 那么將命令請(qǐng)求的發(fā)送對(duì)象調(diào)整為轉(zhuǎn)向所指定的節(jié)點(diǎn)。
2. 先發(fā)送一個(gè) ASKING 命令,然后再發(fā)送真正的命令請(qǐng)求。
3. 不必更新客戶端所記錄的槽 8 至節(jié)點(diǎn)的映射: 槽 8 應(yīng)該仍然映射到節(jié)點(diǎn) A , 而不是節(jié)點(diǎn) B 。
一旦節(jié)點(diǎn) A 針對(duì)槽 8 的遷移工作完成, 節(jié)點(diǎn) A 在再次收到針對(duì)槽 8 的命令請(qǐng)求時(shí), 就會(huì)向客戶端返回 MOVED 轉(zhuǎn)向, 將關(guān)于槽 8 的命令請(qǐng)求長(zhǎng)期地轉(zhuǎn)向到節(jié)點(diǎn) B 。
注意, 即使客戶端出現(xiàn) Bug , 過早地將槽 8 映射到了節(jié)點(diǎn) B 上面, 但只要這個(gè)客戶端不發(fā)送 ASKING 命令, 客戶端發(fā)送命令請(qǐng)求的時(shí)候就會(huì)遇上 MOVED 錯(cuò)誤, 并將它轉(zhuǎn)向回節(jié)點(diǎn) A 。
容錯(cuò):
節(jié)點(diǎn)失效檢測(cè),以下是節(jié)點(diǎn)失效檢查的實(shí)現(xiàn)方法:
1. 當(dāng)一個(gè)節(jié)點(diǎn)向另一個(gè)節(jié)點(diǎn)發(fā)送 PING 命令, 但是目標(biāo)節(jié)點(diǎn)未能在給定的時(shí)限內(nèi)返回 PING 命令的回復(fù)時(shí), 那么發(fā)送命令的節(jié)點(diǎn)會(huì)將目標(biāo)節(jié)點(diǎn)標(biāo)記為 PFAIL(possible failure,可能已失效)。等待 PING 命令回復(fù)的時(shí)限稱為“節(jié)點(diǎn)超時(shí)時(shí)限(node timeout)”, 是一個(gè)節(jié)點(diǎn)選項(xiàng)(node-wise setting)。
2. 每次當(dāng)節(jié)點(diǎn)對(duì)其他節(jié)點(diǎn)發(fā)送 PING 命令的時(shí)候,它都會(huì)隨機(jī)地廣播三個(gè)它所知道的節(jié)點(diǎn)的信息, 這些信息里面的其中一項(xiàng)就是說明節(jié)點(diǎn)是否已經(jīng)被標(biāo)記為 PFAIL或者 FAIL 。
當(dāng)節(jié)點(diǎn)接收到其他節(jié)點(diǎn)發(fā)來的信息時(shí), 它會(huì)記下那些被其他節(jié)點(diǎn)標(biāo)記為失效的節(jié)點(diǎn)。這稱為失效報(bào)告(failure report)。
3. 如果節(jié)點(diǎn)已經(jīng)將某個(gè)節(jié)點(diǎn)標(biāo)記為 PFAIL , 并且根據(jù)節(jié)點(diǎn)所收到的失效報(bào)告顯式,集群中的大部分其他主節(jié)點(diǎn)也認(rèn)為那個(gè)節(jié)點(diǎn)進(jìn)入了失效狀態(tài), 那么節(jié)點(diǎn)會(huì)將那個(gè)失效節(jié)點(diǎn)的狀態(tài)標(biāo)記為 FAIL 。
4. 一旦某個(gè)節(jié)點(diǎn)被標(biāo)記為 FAIL , 關(guān)于這個(gè)節(jié)點(diǎn)已失效的信息就會(huì)被廣播到整個(gè)集群,所有接收到這條信息的節(jié)點(diǎn)都會(huì)將失效節(jié)點(diǎn)標(biāo)記為 FAIL 。
簡(jiǎn)單來說, 一個(gè)節(jié)點(diǎn)要將另一個(gè)節(jié)點(diǎn)標(biāo)記為失效, 必須先詢問其他節(jié)點(diǎn)的意見, 并且得到大部分主節(jié)點(diǎn)的同意才行。因?yàn)檫^期的失效報(bào)告會(huì)被移除,所以主節(jié)點(diǎn)要將某個(gè)節(jié)點(diǎn)標(biāo)記為 FAIL 的話, 必須以最近接收到的失效報(bào)告作為根據(jù)。
從節(jié)點(diǎn)選舉:一旦某個(gè)主節(jié)點(diǎn)進(jìn)入 FAIL 狀態(tài), 如果這個(gè)主節(jié)點(diǎn)有一個(gè)或多個(gè)從節(jié)點(diǎn)存在,那么其中一個(gè)從節(jié)點(diǎn)會(huì)被升級(jí)為新的主節(jié)點(diǎn), 而其他從節(jié)點(diǎn)則會(huì)開始對(duì)這個(gè)新的主節(jié)點(diǎn)進(jìn)行復(fù)制。
新的主節(jié)點(diǎn)由已下線主節(jié)點(diǎn)屬下的所有從節(jié)點(diǎn)中自行選舉產(chǎn)生,以下是選舉的條件:
1. 這個(gè)節(jié)點(diǎn)是已下線主節(jié)點(diǎn)的從節(jié)點(diǎn)。
2. 已下線主節(jié)點(diǎn)負(fù)責(zé)處理的槽數(shù)量非空。
3. 從節(jié)點(diǎn)的數(shù)據(jù)被認(rèn)為是可靠的, 也即是, 主從節(jié)點(diǎn)之間的復(fù)制連接(replication link)的斷線時(shí)長(zhǎng)不能超過節(jié)點(diǎn)超時(shí)時(shí)限(nodetimeout)乘以REDIS_CLUSTER_SLAVE_VALIDITY_MULT 常量得出的積。
如果一個(gè)從節(jié)點(diǎn)滿足了以上的所有條件, 那么這個(gè)從節(jié)點(diǎn)將向集群中的其他主節(jié)點(diǎn)發(fā)送授權(quán)請(qǐng)求, 詢問它們,是否允許自己(從節(jié)點(diǎn))升級(jí)為新的主節(jié)點(diǎn)。
如果發(fā)送授權(quán)請(qǐng)求的從節(jié)點(diǎn)滿足以下屬性, 那么主節(jié)點(diǎn)將向從節(jié)點(diǎn)返FAILOVER_AUTH_GRANTED 授權(quán), 同意從節(jié)點(diǎn)的升級(jí)要求:
1. 發(fā)送授權(quán)請(qǐng)求的是一個(gè)從節(jié)點(diǎn), 并且它所屬的主節(jié)點(diǎn)處于 FAIL狀態(tài)。
2. 在已下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)中, 這個(gè)從節(jié)點(diǎn)的節(jié)點(diǎn) ID 在排序中是最小的。
3. 這個(gè)從節(jié)點(diǎn)處于正常的運(yùn)行狀態(tài): 它沒有被標(biāo)記為 FAIL 狀態(tài),也沒有被標(biāo)記為 PFAIL 狀態(tài)。
一旦某個(gè)從節(jié)點(diǎn)在給定的時(shí)限內(nèi)得到大部分主節(jié)點(diǎn)的授權(quán),它就會(huì)開始執(zhí)行以下故障轉(zhuǎn)移操作:
1. 通過 PONG 數(shù)據(jù)包(packet)告知其他節(jié)點(diǎn), 這個(gè)節(jié)點(diǎn)現(xiàn)在是主節(jié)點(diǎn)了。
2. 通過 PONG 數(shù)據(jù)包告知其他節(jié)點(diǎn), 這個(gè)節(jié)點(diǎn)是一個(gè)已升級(jí)的從節(jié)點(diǎn)(promoted slave)。
3. 接管(claiming)所有由已下線主節(jié)點(diǎn)負(fù)責(zé)處理的哈希槽。
4. 顯式地向所有節(jié)點(diǎn)廣播一個(gè) PONG 數(shù)據(jù)包, 加速其他節(jié)點(diǎn)識(shí)別這個(gè)節(jié)點(diǎn)的進(jìn)度,而不是等待定時(shí)的 PING / PONG 數(shù)據(jù)包。
所有其他節(jié)點(diǎn)都會(huì)根據(jù)新的主節(jié)點(diǎn)對(duì)配置進(jìn)行相應(yīng)的更新:
所有被新的主節(jié)點(diǎn)接管的槽會(huì)被更新。
已下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)會(huì)察覺到 PROMOTED 標(biāo)志,并開始對(duì)新的主節(jié)點(diǎn)進(jìn)行復(fù)制。
如果已下線的主節(jié)點(diǎn)重新回到上線狀態(tài), 那么它會(huì)察覺到PROMOTED 標(biāo)志, 并將自身調(diào)整為現(xiàn)任主節(jié)點(diǎn)的從節(jié)點(diǎn)。
可獲取一份Java架構(gòu)進(jìn)階技術(shù)精品視頻。(高并發(fā)+Spring源碼+JVM原理解析+分布式架構(gòu)+微服務(wù)架構(gòu)+多線程并發(fā)原理+BATJ面試寶典)
在集群的生命周期中, 如果一個(gè)帶有 PROMOTED 標(biāo)識(shí)的主節(jié)點(diǎn)因?yàn)槟承┰蜣D(zhuǎn)變成了從節(jié)點(diǎn),那么該節(jié)點(diǎn)將丟失它所帶有的 PROMOTED 標(biāo)識(shí)。
“Redis緩存在系統(tǒng)中用來做什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。