做電影網(wǎng)站用什么軟件叫什么名字嗎seo服務(wù)公司招聘
Redis知識點(diǎn)總結(jié)(六)——主從同步、哨兵模式、集群
- 主從同步
- 哨兵
- 集群
主從同步
redis的主從同步,一般是一個主節(jié)點(diǎn),加上多個從節(jié)點(diǎn)。只有主節(jié)點(diǎn)可以接收寫命令,主節(jié)點(diǎn)接收到的寫命令,會同步給從節(jié)點(diǎn),從節(jié)點(diǎn)接收到主節(jié)點(diǎn)同步過來的寫命令,會執(zhí)行該命令,把數(shù)據(jù)寫操作應(yīng)用到內(nèi)存。主節(jié)點(diǎn)和從節(jié)點(diǎn)都可以接收讀命令,從節(jié)點(diǎn)可以分擔(dān)主節(jié)點(diǎn)的讀請求壓力,這樣就實(shí)現(xiàn)了讀寫分離。
以下是redis主從同步的原理:
要開啟主從同步,我們要在主節(jié)點(diǎn)以外,多部署一個從節(jié)點(diǎn)。然后我們要登上從節(jié)點(diǎn),執(zhí)行replicaof {主節(jié)點(diǎn)ip} {主節(jié)點(diǎn)port} 的命令。
主從同步開啟后,從節(jié)點(diǎn)會向主節(jié)點(diǎn)發(fā)送 psync {runid} {offset} 命令。runid是每個節(jié)點(diǎn)啟動時都會被分配的一個唯一的id值,這里psync命令發(fā)送的runid是主節(jié)點(diǎn)的id,如果是重連發(fā)送的psync,runid是之前同步的主節(jié)點(diǎn)的id值,如果是第一次連接主節(jié)點(diǎn),那么runid就是個問號“?”。offset是從節(jié)點(diǎn)當(dāng)前復(fù)制到的偏移量,因?yàn)榇藭r是第一次連接,從未進(jìn)行過主從復(fù)制,那么發(fā)送的offset就是“-1”,于是第一次發(fā)送的psync命令就是“psync ? -1”。
當(dāng)主節(jié)點(diǎn)接收到從節(jié)點(diǎn)發(fā)來的psync命令,分析發(fā)現(xiàn)從節(jié)點(diǎn)是第一次同步,會給從節(jié)點(diǎn)返回一個FULLREPLICA命令,表示接下來要進(jìn)行全量同步。FULLREPLICA命令會帶上主節(jié)點(diǎn)自己的runid,以及主節(jié)點(diǎn)目前的寫入的偏移量offset。
然后主節(jié)點(diǎn)會執(zhí)行bgsave命令,fork出一個子進(jìn)程,子進(jìn)程會dump出一個rdb快照,發(fā)給從節(jié)點(diǎn)。
從節(jié)點(diǎn)接收到rdb快照,會首先清空自己的數(shù)據(jù),然后加載rdb快照。
在主節(jié)點(diǎn)執(zhí)行完bgsave命令之后,主節(jié)點(diǎn)會繼續(xù)接收客戶端請求,期間的寫命令會緩存在replication buffer緩沖區(qū)中,待從節(jié)點(diǎn)加載完rdb快照后,主節(jié)點(diǎn)會把replication buffer中的命令發(fā)送給從節(jié)點(diǎn),從節(jié)點(diǎn)會執(zhí)行這些命令,把它們應(yīng)用到緩存中。
最后主節(jié)點(diǎn)與從節(jié)點(diǎn)之間會維持一個長連接,基于這個長連接進(jìn)行命令傳播。
如果從節(jié)點(diǎn)與主節(jié)點(diǎn)的連接斷開了,從節(jié)點(diǎn)會嘗試重連,流程如下:
在斷開連接的這一段期間,主節(jié)點(diǎn)會繼續(xù)接受請求,寫命令會被寫入一個名叫repl_backlog_buffer的環(huán)形緩沖區(qū)中,這個環(huán)形緩沖區(qū)在寫滿后,會回到開頭進(jìn)行覆蓋寫。
當(dāng)從節(jié)點(diǎn)重新連上主節(jié)點(diǎn)后,從節(jié)點(diǎn)會發(fā)送 psync {runid} {offset} 命令。
主節(jié)點(diǎn)接收到從節(jié)點(diǎn)發(fā)來的psync命令后,會根據(jù)里面的offset判斷repl_backlog_buffer中對應(yīng)的寫命令是否已被覆蓋。如果沒有被覆蓋,那么會把repl_backlog_buffer中從節(jié)點(diǎn)的offset到主節(jié)點(diǎn)的offset之間的命令發(fā)送給從節(jié)點(diǎn),這個就是增量同步;如果已經(jīng)被覆蓋了,那么就要進(jìn)行全量同步了。
哨兵
當(dāng)主節(jié)點(diǎn)掛掉之后,從節(jié)點(diǎn)是不會自動切換為主節(jié)點(diǎn)的,需要我們手動進(jìn)行切換,但是這樣就太麻煩了,于是就引入了哨兵集群。
哨兵集群由多個哨兵實(shí)例組成,每個哨兵實(shí)例其實(shí)就是一個redis節(jié)點(diǎn),只是這個節(jié)點(diǎn)它不負(fù)責(zé)數(shù)據(jù)的讀寫,這個節(jié)點(diǎn)以哨兵的角色啟動,負(fù)責(zé)對進(jìn)行數(shù)據(jù)讀寫的redis節(jié)點(diǎn)進(jìn)行監(jiān)控,選主和通知這三個工作:
- 監(jiān)控:監(jiān)控主節(jié)點(diǎn)和從節(jié)點(diǎn)是否正常運(yùn)行。
- 選主:當(dāng)主節(jié)點(diǎn)下線之后,在從節(jié)點(diǎn)之中挑選一個提升為主節(jié)點(diǎn)。
- 通知:發(fā)生主從切換之后,通知其他從節(jié)點(diǎn)與新主節(jié)點(diǎn)進(jìn)行同步,通知客戶端。
我們只需要給哨兵節(jié)點(diǎn)配置主節(jié)點(diǎn)的ip地址和端口號即可,哨兵節(jié)點(diǎn)啟動之后,就會自動組成哨兵集群。哨兵節(jié)點(diǎn)自動組成集群,是通過主節(jié)點(diǎn)完成的,它們在主節(jié)點(diǎn)的“__sentinel__:hello”頻道執(zhí)行pub和sub命令,也就是進(jìn)行發(fā)布訂閱,pub命令把自己的ip地址和端口號發(fā)布到主節(jié)點(diǎn)的“__sentinel__:hello”頻道上,sub命令訂閱主節(jié)點(diǎn)的“__sentinel__:hello”頻道,當(dāng)其他哨兵節(jié)點(diǎn)把自己的ip地址和端口號發(fā)布到主節(jié)點(diǎn)的該頻道,當(dāng)前哨兵節(jié)點(diǎn)就可以訂閱到其他節(jié)點(diǎn)的ip地址和端口號。
哨兵也要對從節(jié)點(diǎn)進(jìn)行監(jiān)控,但是我們啟動哨兵的時候,并沒有配置從節(jié)點(diǎn)的ip地址和端口號,哨兵也是通過主節(jié)點(diǎn)得知從節(jié)點(diǎn)的ip地址和端口號的。哨兵節(jié)點(diǎn)會向主節(jié)點(diǎn)發(fā)送一個INFO命令,主節(jié)點(diǎn)接收到哨兵節(jié)點(diǎn)發(fā)來的INFO命令,會把從節(jié)點(diǎn)信息列表返回給哨兵。哨兵獲取到主節(jié)點(diǎn)返回的從節(jié)點(diǎn)信息列表,就可以向從節(jié)點(diǎn)發(fā)起連接。
哨兵持續(xù)監(jiān)控著每一個節(jié)點(diǎn),當(dāng)有節(jié)點(diǎn)下線時,哨兵會把它標(biāo)記為主觀下線。如果下線的是從節(jié)點(diǎn),那么就僅僅是標(biāo)記為主觀下線。如果是發(fā)現(xiàn)下線的是主節(jié)點(diǎn),則流程稍微復(fù)雜一點(diǎn):
- 首先標(biāo)記主節(jié)點(diǎn)為主觀下線
- 詢問哨兵集群中的其他節(jié)點(diǎn),看是否也把主節(jié)點(diǎn)標(biāo)記為主觀下線
- 如果達(dá)到quorum配置項(xiàng)指定個數(shù)的哨兵節(jié)點(diǎn)標(biāo)記主節(jié)點(diǎn)為主觀下線,則標(biāo)記主節(jié)點(diǎn)為客觀下線,并且要進(jìn)行重新選主
重新選主的過程,也是比較復(fù)雜的。當(dāng)一個哨兵節(jié)點(diǎn)把主節(jié)點(diǎn)標(biāo)記為客觀下線后,并不是馬上進(jìn)行重新選主,而是要在哨兵集群內(nèi)進(jìn)行投票,選出一個leader,它要向哨兵集群內(nèi)每一個哨兵節(jié)點(diǎn)請求投自己一票,如果超過半數(shù)的哨兵節(jié)點(diǎn)都投了贊成票,該節(jié)點(diǎn)才成為選主的leader,由該哨兵節(jié)點(diǎn)來進(jìn)行重新選主。如果該哨兵節(jié)點(diǎn)沒有成功當(dāng)上leader,那么就看一下哨兵集群中是否已經(jīng)選出了leader,如果哨兵集群中已經(jīng)選出了leader,那么它就啥也不用干了,否則就要等待一段時間,重新發(fā)起選舉投票。
重新選主就是從眾多從節(jié)點(diǎn)當(dāng)中,挑選一個,把它提升為主節(jié)點(diǎn)。挑選的規(guī)則就是先過濾掉有問題的節(jié)點(diǎn),然后對剩下的節(jié)點(diǎn)進(jìn)行篩選打分,具體流程如下
- 先過濾掉已下線的從節(jié)點(diǎn),以及經(jīng)常斷連的從節(jié)點(diǎn)
- 從剩下的從節(jié)點(diǎn)中,選出一個優(yōu)先級最高的
- 如果有兩個以上的從節(jié)點(diǎn),有相同的優(yōu)先級,則選出一個復(fù)制進(jìn)度offset離主節(jié)點(diǎn)寫入進(jìn)度最近的
- 如果有兩個以上的從節(jié)點(diǎn)offset相等,并且離主節(jié)點(diǎn)的寫入進(jìn)度最近,那么從它們之間選出ID最小的一個
選出主節(jié)點(diǎn)之后,該哨兵節(jié)點(diǎn)還要通知其他從節(jié)點(diǎn)與新的主節(jié)點(diǎn)進(jìn)行同步,還要通知客戶端與新的主節(jié)點(diǎn)連接。
集群
redis集群(Redis Cluster)是redis3.0版本提供的redis集群方案。redis集群采用了hash slot(哈希槽),集群中一共有16384個hash slot。當(dāng)要往集群中set或get一個key時,會用CRC16算法根據(jù)key算出一個hash值,然后利用該hash值對16384取模,得知該key位于哪個hash槽,再請求該hash槽被分配到的redis節(jié)點(diǎn)進(jìn)行讀寫。
我們可以使用 cluster create命令創(chuàng)建redis集群,并自動分配hash槽;也可以使用cluster meet命令手動建立實(shí)例間的連接,然后使用cluster addslots命令手動分配hash槽。
分配好hash槽后,redis集群中的節(jié)點(diǎn)之間,會互相交換信息,然后集群中的每個節(jié)點(diǎn),都擁有了整個集群的hash槽分配情況。
然后當(dāng)redis客戶端連接上集群中的其中一個節(jié)點(diǎn)時,就能獲取到集群中所有hash槽的分配情況,客戶端會緩存集群信息在本地。
那么,當(dāng)redis客戶端要向集群set一個key或者get一個key時,就會按照規(guī)則進(jìn)行計(jì)算:CRC16(key) % 16384,得到目標(biāo)hash槽,然后查詢本地緩存,看看該hash槽屬于哪一個redis節(jié)點(diǎn),然后再向該節(jié)點(diǎn)發(fā)起請求。
但是有時候由于發(fā)生了集群內(nèi)hash槽的遷移,可能該hash槽已不歸該redis節(jié)點(diǎn)管理,那么該redis節(jié)點(diǎn)上就沒有對應(yīng)的key了。此時redis節(jié)點(diǎn)會根據(jù)情況返回MOVED命令或ASK命令中的其中一個:
- 如果hash槽里的所有key都完成了遷移,那么返回MOVED命令,并帶上該hash槽最新的所屬redis節(jié)點(diǎn)的ip地址和端口號。
- 如果hash槽里還有部分key沒有完成遷移,那么返回ASK命令,并帶上該hash槽最新的所屬redis節(jié)點(diǎn)的ip地址和端口號。
如果返回的時MOVED命令,redis客戶端會直接向MOVED命令指定的redis節(jié)點(diǎn)發(fā)送請求,然后會更新本地緩存。
如果返回的是ASK命令,redis客戶端會先向ASK命令指定的redis節(jié)點(diǎn)發(fā)送一個ASKING命令,表示允許該redis節(jié)點(diǎn)執(zhí)行接下來該客戶端發(fā)送的命令,然后客戶端再發(fā)送真正的請求,但是不會更新本地緩存。