Redis的架构模式
單機(jī)版
特點(diǎn):簡(jiǎn)單
問(wèn)題:
1、內(nèi)存容量有限 2、處理能力有限 3、無(wú)法高可用。
主從復(fù)制
Redis 的復(fù)制(replication)功能允許用戶(hù)根據(jù)一個(gè) Redis 服務(wù)器來(lái)創(chuàng)建任意多個(gè)該服務(wù)器的復(fù)制品,其中被復(fù)制的服務(wù)器為主服務(wù)器(master),而通過(guò)復(fù)制創(chuàng)建出來(lái)的服務(wù)器復(fù)制品則為從服務(wù)器(slave)。 只要主從服務(wù)器之間的網(wǎng)絡(luò)連接正常,主從服務(wù)器兩者會(huì)具有相同的數(shù)據(jù),主服務(wù)器就會(huì)一直將發(fā)生在自己身上的數(shù)據(jù)更新同步 給從服務(wù)器,從而一直保證主從服務(wù)器的數(shù)據(jù)相同。
特點(diǎn):
1、master/slave 角色
2、master/slave 數(shù)據(jù)相同
3、降低 master 讀壓力在轉(zhuǎn)交從庫(kù)
問(wèn)題:
無(wú)法保證高可用
沒(méi)有解決 master 寫(xiě)的壓力
哨兵
Redis sentinel 是一個(gè)分布式系統(tǒng)中監(jiān)控 redis 主從服務(wù)器,并在主服務(wù)器下線時(shí)自動(dòng)進(jìn)行故障轉(zhuǎn)移。其中三個(gè)特性:
監(jiān)控(Monitoring): Sentinel 會(huì)不斷地檢查你的主服務(wù)器和從服務(wù)器是否運(yùn)作正常。
提醒(Notification): 當(dāng)被監(jiān)控的某個(gè) Redis 服務(wù)器出現(xiàn)問(wèn)題時(shí), Sentinel 可以通過(guò) API 向管理員或者其他應(yīng)用程序發(fā)送通知。
自動(dòng)故障遷移(Automatic failover): 當(dāng)一個(gè)主服務(wù)器不能正常工作時(shí), Sentinel 會(huì)開(kāi)始一次自動(dòng)故障遷移操作。
特點(diǎn):
1、保證高可用
2、監(jiān)控各個(gè)節(jié)點(diǎn)
3、自動(dòng)故障遷移
缺點(diǎn):主從模式,切換需要時(shí)間丟數(shù)據(jù)
沒(méi)有解決 master 寫(xiě)的壓力
集群模式:
業(yè)界主流的Redis集群化方案主要包括以下幾個(gè):
- 客戶(hù)端分片
- 代理分片
- 服務(wù)端分片
代理分片包括:
-
Codis
-
Twemproxy
服務(wù)端分片包括:
- Redis Cluster
它們還可以用是否中心化來(lái)劃分,其中客戶(hù)端分片、Redis Cluster屬于無(wú)中心化的集群方案,Codis、Tweproxy屬于中心化的集群方案。
是否中心化是指客戶(hù)端訪問(wèn)多個(gè)Redis節(jié)點(diǎn)時(shí),是直接訪問(wèn)還是通過(guò)一個(gè)中間層Proxy來(lái)進(jìn)行操作,直接訪問(wèn)的就屬于無(wú)中心化的方案,通過(guò)中間層Proxy訪問(wèn)的就屬于中心化的方案,它們有各自的優(yōu)劣,下面分別來(lái)介紹。
1 客戶(hù)端分片
客戶(hù)端分片主要是說(shuō),我們只需要部署多個(gè)Redis節(jié)點(diǎn),具體如何使用這些節(jié)點(diǎn),主要工作在客戶(hù)端。
客戶(hù)端通過(guò)固定的Hash算法,針對(duì)不同的key計(jì)算對(duì)應(yīng)的Hash值,然后對(duì)不同的Redis節(jié)點(diǎn)進(jìn)行讀寫(xiě)。
客戶(hù)端分片集群模式 需要業(yè)務(wù)開(kāi)發(fā)人員事先評(píng)估業(yè)務(wù)的請(qǐng)求量和數(shù)據(jù)量,然后讓DBA部署足夠的節(jié)點(diǎn)交給開(kāi)發(fā)人員使用即可。
這個(gè)方案的優(yōu)點(diǎn)是部署非常方便,業(yè)務(wù)需要多少個(gè)節(jié)點(diǎn)DBA直接部署交付即可,剩下的事情就需要業(yè)務(wù)開(kāi)發(fā)人員根據(jù)節(jié)點(diǎn)數(shù)量來(lái)編寫(xiě)key的請(qǐng)求路由邏輯,制定一個(gè)規(guī)則,一般采用固定的Hash算法,把不同的key寫(xiě)入到不同的節(jié)點(diǎn)上,然后再根據(jù)這個(gè)規(guī)則進(jìn)行數(shù)據(jù)讀取。
可見(jiàn),它的缺點(diǎn)是業(yè)務(wù)開(kāi)發(fā)人員使用Redis的成本較高,需要編寫(xiě)路由規(guī)則的代碼來(lái)使用多個(gè)節(jié)點(diǎn),而且如果事先對(duì)業(yè)務(wù)的數(shù)據(jù)量評(píng)估不準(zhǔn)確,后期的擴(kuò)容和遷移成本非常高,因?yàn)楣?jié)點(diǎn)數(shù)量發(fā)生變更后,Hash算法對(duì)應(yīng)的節(jié)點(diǎn)也就不再是之前的節(jié)點(diǎn)了。
所以后來(lái)又衍生出了一致性哈希算法,就是為了解決當(dāng)節(jié)點(diǎn)數(shù)量變更時(shí),盡量減少數(shù)據(jù)的遷移和性能問(wèn)題。
這種客戶(hù)端分片的方案一般用于業(yè)務(wù)數(shù)據(jù)量比較穩(wěn)定,后期不會(huì)有大幅度增長(zhǎng)的業(yè)務(wù)場(chǎng)景下使用,只需要前期評(píng)估好業(yè)務(wù)數(shù)據(jù)量即可。
客戶(hù)端分片的特點(diǎn)
這實(shí)際上是一種靜態(tài)分片技術(shù)。Redis 實(shí)例的增減,都得手工調(diào)整分片程序。基于此分片機(jī)制的開(kāi)源產(chǎn)品,現(xiàn)在仍不多見(jiàn)。
這種分片機(jī)制的性能比代理式更好(少了一個(gè)中間分發(fā)環(huán)節(jié))。但缺點(diǎn)是升級(jí)麻煩,對(duì)研發(fā)人員的個(gè)人依賴(lài)性強(qiáng)——需要有較強(qiáng)的程序開(kāi)發(fā)能力做后盾。如果主力程序員離職,可能新的負(fù)責(zé)人,會(huì)選擇重寫(xiě)一遍。
所以,這種方式下,可運(yùn)維性較差。出現(xiàn)故障,定位和解決都得研發(fā)和運(yùn)維配合著解決,故障時(shí)間變長(zhǎng)。
這種方案,難以進(jìn)行標(biāo)準(zhǔn)化運(yùn)維,不太適合中小公司(除非有足夠的 DevOPS)。
2 代理分片 (代理型集群)
這種方案,將分片工作交給專(zhuān)門(mén)的代理程序來(lái)做。代理程序接收到來(lái)自業(yè)務(wù)程序的數(shù)據(jù)請(qǐng)求,根據(jù)路由規(guī)則,將這些請(qǐng)求分發(fā)給正確的 Redis 實(shí)例并返回給業(yè)務(wù)程序。
這種機(jī)制下,一般會(huì)選用第三方代理程序(而不是自己研發(fā)),因?yàn)楹蠖擞卸鄠€(gè) Redis 實(shí)例,所以這類(lèi)程序又稱(chēng)為分布式中間件。
這樣的好處是,業(yè)務(wù)程序不用關(guān)心后端 Redis 實(shí)例,運(yùn)維起來(lái)也方便。雖然會(huì)因此帶來(lái)些性能損耗,但對(duì)于 Redis 這種內(nèi)存讀寫(xiě)型應(yīng)用,相對(duì)而言是能容忍的。
Codis
隨著業(yè)務(wù)和技術(shù)的發(fā)展,人們?cè)桨l(fā)覺(jué)得,當(dāng)我需要使用Redis時(shí),我們不想關(guān)心集群后面有多少個(gè)節(jié)點(diǎn),我們希望我們使用的Redis是一個(gè)大集群,當(dāng)我們的業(yè)務(wù)量增加時(shí),這個(gè)大集群可以增加新的節(jié)點(diǎn)來(lái)解決容量不夠用和性能問(wèn)題。
這種方式就是服務(wù)端分片方案,客戶(hù)端不需要關(guān)心集群后面有多少個(gè)Redis節(jié)點(diǎn),只需要像使用一個(gè)Redis的方式去操作這個(gè)集群,這種方案將大大降低開(kāi)發(fā)人員的使用成本,開(kāi)發(fā)人員可以只需要關(guān)注業(yè)務(wù)邏輯即可,不需要關(guān)心Redis的資源問(wèn)題。
多個(gè)節(jié)點(diǎn)組成的集群,如何讓開(kāi)發(fā)人員像操作一個(gè)Redis時(shí)那樣來(lái)使用呢?這就涉及到多個(gè)節(jié)點(diǎn)是如何組織起來(lái)提供服務(wù)的,一般我們會(huì)在客戶(hù)端和服務(wù)端中間增加一個(gè)代理層,客戶(hù)端只需要操作這個(gè)代理層,代理層實(shí)現(xiàn)了具體的請(qǐng)求轉(zhuǎn)發(fā)規(guī)則,然后轉(zhuǎn)發(fā)請(qǐng)求到后面的多個(gè)節(jié)點(diǎn)上,因此這種方式也叫做中心化方式的集群方案,Codis就是以這種方式實(shí)現(xiàn)的集群化方案。
Proxy集群模式
Codis是由國(guó)人前豌豆莢大神開(kāi)發(fā)的,采用中心化方式的集群方案。因?yàn)樾枰韺覲roxy來(lái)進(jìn)行所有請(qǐng)求的轉(zhuǎn)發(fā),所以對(duì)Proxy的性能要求很高,Codis采用Go語(yǔ)言開(kāi)發(fā),兼容了開(kāi)發(fā)效率和性能。
Codis包含了多個(gè)組件:
- codis-proxy:主要負(fù)責(zé)對(duì)請(qǐng)求的讀寫(xiě)進(jìn)行轉(zhuǎn)發(fā)
- codis-dashbaord:統(tǒng)一的控制中心,整合了數(shù)據(jù)轉(zhuǎn)發(fā)規(guī)則、故障自動(dòng)恢復(fù)、數(shù)據(jù)在線遷移、節(jié)點(diǎn)擴(kuò)容縮容、自動(dòng)化運(yùn)維API等功能
- codis-group:基于Redis 3.2.8版本二次開(kāi)發(fā)的Redis Server,增加了異步數(shù)據(jù)遷移功能
- codis-fe:管理多個(gè)集群的UI界面
可見(jiàn)Codis的組件還是挺多的,它的功能非常全,除了請(qǐng)求轉(zhuǎn)發(fā)功能之外,還實(shí)現(xiàn)了在線數(shù)據(jù)遷移、節(jié)點(diǎn)擴(kuò)容縮容、故障自動(dòng)恢復(fù)等功能。
Codis的Proxy就是負(fù)責(zé)請(qǐng)求轉(zhuǎn)發(fā)的組件,它內(nèi)部維護(hù)了請(qǐng)求轉(zhuǎn)發(fā)的具體規(guī)則,Codis把整個(gè)集群劃分為1024個(gè)槽位,在處理讀寫(xiě)請(qǐng)求時(shí),采用crc32Hash算法計(jì)算key的Hash值,然后再根據(jù)Hash值對(duì)1024個(gè)槽位取模,最終找到具體的Redis節(jié)點(diǎn)。
Codis最大的特點(diǎn)就是可以在線擴(kuò)容,在擴(kuò)容期間不影響客戶(hù)端的訪問(wèn),也就是不需要停機(jī)。這對(duì)業(yè)務(wù)使用方是極大的便利,當(dāng)集群性能不夠時(shí),就可以動(dòng)態(tài)增加節(jié)點(diǎn)來(lái)提升集群的性能。
為了實(shí)現(xiàn)在線擴(kuò)容,保證數(shù)據(jù)在遷移過(guò)程中還有可靠的性能,Codis針對(duì)Redis進(jìn)行了修改,增加了針對(duì)異步遷移數(shù)據(jù)相關(guān)命令,它基于Redis 3.2.8進(jìn)行開(kāi)發(fā),上層配合Dashboard和Proxy組件,完成對(duì)業(yè)務(wù)無(wú)損的數(shù)據(jù)遷移和擴(kuò)容功能。
因此,要想使用Codis,必須使用它內(nèi)置的Redis,這也就意味著Codis中的Redis是否能跟上官方最新版的功能特性,可能無(wú)法得到保障,這取決于Codis的維護(hù)方,目前Codis已經(jīng)不再維護(hù),所以使用Codis時(shí)只能使用3.2.8版的Redis,這是一個(gè)痛點(diǎn)。
另外,由于集群化都需要部署多個(gè)節(jié)點(diǎn),因此操作集群并不能完全像操作單個(gè)Redis一樣實(shí)現(xiàn)所有功能,主要是對(duì)于操作多個(gè)節(jié)點(diǎn)可能產(chǎn)生問(wèn)題的命令進(jìn)行了禁用或限制,具體可參考Codis不支持的命令列表。
但這不影響它是一個(gè)優(yōu)秀的集群化方案,由于我司使用Redis集群方案較早,那時(shí)Redis Cluster還不夠成熟,所以我司使用的Redis集群方案就是Codis。目前我的工作主要是圍繞Codis展開(kāi)的,我們公司對(duì)Codis進(jìn)行了定制開(kāi)發(fā),還對(duì)Redis進(jìn)行了一些改造,讓Codis支持了跨多個(gè)數(shù)據(jù)中心的數(shù)據(jù)同步,因此我對(duì)Codis的代碼比較熟悉,后面會(huì)專(zhuān)門(mén)寫(xiě)一些文章來(lái)剖析Codis的實(shí)現(xiàn)原理,學(xué)習(xí)它的原理,這對(duì)我們理解分布式存儲(chǔ)有很大的幫助!
Twemproxy
Twemproxy是由Twitter開(kāi)源的集群化方案,它既可以做Redis Proxy,還可以做Memcached Proxy。
它的功能比較單一,只實(shí)現(xiàn)了請(qǐng)求路由轉(zhuǎn)發(fā),沒(méi)有像Codis那么全面有在線擴(kuò)容的功能,它解決的重點(diǎn)就是把客戶(hù)端分片的邏輯統(tǒng)一放到了Proxy層而已,其他功能沒(méi)有做任何處理。
Tweproxy推出的時(shí)間最久,在早期沒(méi)有好的服務(wù)端分片集群方案時(shí),應(yīng)用范圍很廣,而且性能也極其穩(wěn)定。
但它的痛點(diǎn)就是無(wú)法在線擴(kuò)容、縮容,這就導(dǎo)致運(yùn)維非常不方便,而且也沒(méi)有友好的運(yùn)維UI可以使用。
Twemproxy 是一個(gè) Twitter 開(kāi)源的一個(gè) redis 和 memcache 快速/輕量級(jí)代理服務(wù)器; Twemproxy 是一個(gè)快速的單線程代理程序,支持 Memcached ASCII 協(xié)議和 redis 協(xié)議。
特點(diǎn):
1、多種 hash 算法:MD5、CRC16、CRC32、CRC32a、hsieh、murmur、Jenkins
2、支持失敗節(jié)點(diǎn)自動(dòng)刪除
3、后端 Sharding 分片邏輯對(duì)業(yè)務(wù)透明,業(yè)務(wù)方的讀寫(xiě)方式和操作單個(gè) Redis 一致
缺點(diǎn):
增加了新的 proxy,需要維護(hù)其高可用。failover 邏輯需要自己實(shí)現(xiàn),其本身不能支持故障的自動(dòng)轉(zhuǎn)移可擴(kuò)展性差,進(jìn)行擴(kuò)縮容都需要手動(dòng)干預(yù)
Codis就是因?yàn)樵谶@種背景下才衍生出來(lái)的。
3 服務(wù)端分片(直連型集群)
采用中間加一層Proxy的中心化模式時(shí),這就對(duì)Proxy的要求很高,因?yàn)樗坏┏霈F(xiàn)故障,那么操作這個(gè)Proxy的所有客戶(hù)端都無(wú)法處理,要想實(shí)現(xiàn)Proxy的高可用,還需要另外的機(jī)制來(lái)實(shí)現(xiàn),例如Keepalive。
而且增加一層Proxy進(jìn)行轉(zhuǎn)發(fā),必然會(huì)有一定的性能損耗,那么除了客戶(hù)端分片和上面提到的中心化的方案之外,還有比較好的解決方案么?
直連型集群。從redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用無(wú)中心結(jié)構(gòu),每個(gè)節(jié)點(diǎn)保存數(shù)據(jù)和整個(gè)集群狀態(tài),每個(gè)節(jié)點(diǎn)都和其他所有節(jié)點(diǎn)連接。
Redis Cluster
Redis官方推出的Redis Cluster另辟蹊徑,它沒(méi)有采用中心化模式的Proxy方案,而是把請(qǐng)求轉(zhuǎn)發(fā)邏輯一部分放在客戶(hù)端,一部分放在了服務(wù)端,它們之間互相配合完成請(qǐng)求的處理。
Redis Cluster是在Redis 3.0推出的,早起的Redis Cluster由于沒(méi)有經(jīng)過(guò)嚴(yán)格的測(cè)試和生產(chǎn)驗(yàn)證,所以并沒(méi)有廣泛推廣開(kāi)來(lái)。也正是在這樣的背景下,業(yè)界衍生了出了上面所說(shuō)的中心化集群方案:Codis和Tweproxy。
但隨著Redis的版本迭代,Redis官方的Cluster也越來(lái)越穩(wěn)定,更多人開(kāi)始采用官方的集群化方案。也正是因?yàn)樗枪俜酵瞥龅?#xff0c;所以它的持續(xù)維護(hù)性可以得到保障,這就比那些第三方的開(kāi)源方案更有優(yōu)勢(shì)。
Redis Cluster沒(méi)有了中間的Proxy代理層,那么是如何進(jìn)行請(qǐng)求的轉(zhuǎn)發(fā)呢?
Redis把請(qǐng)求轉(zhuǎn)發(fā)的邏輯放在了Smart Client中,要想使用Redis Cluster,必須升級(jí)Client SDK,這個(gè)SDK中內(nèi)置了請(qǐng)求轉(zhuǎn)發(fā)的邏輯,所以業(yè)務(wù)開(kāi)發(fā)人員同樣不需要自己編寫(xiě)轉(zhuǎn)發(fā)規(guī)則,Redis Cluster采用16384個(gè)槽位進(jìn)行路由規(guī)則的轉(zhuǎn)發(fā)。
沒(méi)有了Proxy層進(jìn)行轉(zhuǎn)發(fā),客戶(hù)端可以直接操作對(duì)應(yīng)的Redis節(jié)點(diǎn),這樣就少了Proxy層轉(zhuǎn)發(fā)的性能損耗。
Redis Cluster也提供了在線數(shù)據(jù)遷移、節(jié)點(diǎn)擴(kuò)容縮容等功能,內(nèi)部還內(nèi)置了哨兵完成故障自動(dòng)恢復(fù)功能,可見(jiàn)它是一個(gè)集成所有功能于一體的Cluster。因此它在部署時(shí)非常簡(jiǎn)單,不需要部署過(guò)多的組件,對(duì)于運(yùn)維極其友好。
Redis Cluster在節(jié)點(diǎn)數(shù)據(jù)遷移、擴(kuò)容縮容時(shí),對(duì)于客戶(hù)端的請(qǐng)求處理也做了相應(yīng)的處理。當(dāng)客戶(hù)端訪問(wèn)的數(shù)據(jù)正好在遷移過(guò)程中時(shí),服務(wù)端與客戶(hù)端制定了一些協(xié)議,來(lái)告知客戶(hù)端去正確的節(jié)點(diǎn)上訪問(wèn),幫助客戶(hù)端訂正自己的路由規(guī)則。
雖然Redis Cluster提供了在線數(shù)據(jù)遷移的功能,但它的遷移性能并不高,遷移過(guò)程中遇到大key時(shí)還有可能長(zhǎng)時(shí)間阻塞遷移的兩個(gè)節(jié)點(diǎn),這個(gè)功能相較于Codis來(lái)說(shuō),Codis數(shù)據(jù)遷移性能更好。關(guān)注公眾號(hào)Java技術(shù)棧可以閱讀一些集群搭建實(shí)戰(zhàn)的文章。
現(xiàn)在越來(lái)越多的公司開(kāi)始采用Redis Cluster,有能力的公司還在它的基礎(chǔ)上進(jìn)行了二次開(kāi)發(fā)和定制,來(lái)解決Redis Cluster存在的一些問(wèn)題,我們期待Redis Cluster未來(lái)有更好的發(fā)展。
特點(diǎn):
1、無(wú)中心架構(gòu)(不存在哪個(gè)節(jié)點(diǎn)影響性能瓶頸),少了 proxy 層。
2、數(shù)據(jù)按照 slot 存儲(chǔ)分布在多個(gè)節(jié)點(diǎn),節(jié)點(diǎn)間數(shù)據(jù)共享,可動(dòng)態(tài)調(diào)整數(shù)據(jù)分布。
3、可擴(kuò)展性,可線性擴(kuò)展到 1000 個(gè)節(jié)點(diǎn),節(jié)點(diǎn)可動(dòng)態(tài)添加或刪除。
4、高可用性,部分節(jié)點(diǎn)不可用時(shí),集群仍可用。通過(guò)增加 Slave 做備份數(shù)據(jù)副本
5、實(shí)現(xiàn)故障自動(dòng) failover,節(jié)點(diǎn)之間通過(guò) gossip 協(xié)議交換狀態(tài)信息,用投票機(jī)制完成 Slave到 Master 的角色提升。
缺點(diǎn):
1、資源隔離性較差,容易出現(xiàn)相互影響的情況。
2、數(shù)據(jù)通過(guò)異步復(fù)制,不保證數(shù)據(jù)的強(qiáng)一致性
集群的必要性
所謂的集群,就是通過(guò)添加服務(wù)器的數(shù)量,提供相同的服務(wù),從而讓服務(wù)器達(dá)到一個(gè)穩(wěn)定、高效的狀態(tài)。
1.1.1 使用 redis 集群的必要性
問(wèn)題:我們已經(jīng)部署好了redis,并且能啟動(dòng)一個(gè)redis,實(shí)現(xiàn)數(shù)據(jù)的讀寫(xiě),為什么還要學(xué)習(xí)redis集群?
答:(1)單個(gè)redis存在不穩(wěn)定性。當(dāng)redis服務(wù)宕機(jī)了,就沒(méi)有可用的服務(wù)了。
(2)單個(gè)redis的讀寫(xiě)能力是有限的。
總結(jié):redis集群是為了強(qiáng)化redis的讀寫(xiě)能力。
1.1.2 如何學(xué)習(xí)redis集群
說(shuō)明:(1)redis集群中,每一個(gè)redis稱(chēng)之為一個(gè)節(jié)點(diǎn)。
? (2)redis集群中,有兩種類(lèi)型的節(jié)點(diǎn):主節(jié)點(diǎn)(master)、從節(jié)點(diǎn)(slave)。
? (3)redis集群,是基于redis主從復(fù)制實(shí)現(xiàn)。
?
所以,學(xué)習(xí)redis集群,就是從學(xué)習(xí)redis主從復(fù)制模型開(kāi)始的。
redis主從復(fù)制
1.1 概念
? 主從復(fù)制模型中,有多個(gè)redis節(jié)點(diǎn)。
? 其中,有且僅有一個(gè)為主節(jié)點(diǎn)Master。從節(jié)點(diǎn)Slave可以有多個(gè)。
只要網(wǎng)絡(luò)連接正常,Master會(huì)一直將自己的數(shù)據(jù)更新同步給Slaves,保持主從同步。
1.1 特點(diǎn)
(1)主節(jié)點(diǎn)Master可讀、可寫(xiě).
(2)從節(jié)點(diǎn)Slave只讀。(read-only)
因此,主從模型可以提高讀的能力,在一定程度上緩解了寫(xiě)的能力。因?yàn)槟軐?xiě)仍然只有Master節(jié)點(diǎn)一個(gè),可以將讀的操作全部移交到從節(jié)點(diǎn)上,變相提高了寫(xiě)能力。
1.1 基于配置實(shí)現(xiàn)
1.1.1 需求
| 從節(jié)點(diǎn)(兩個(gè)) | 6381、6382 |
1.1.2 配置步驟
(1)在/usr/local目錄下,創(chuàng)建一個(gè)/redis/master-slave目錄
[root@node0719 local]# mkdir -p redis/master-slave
(2)在master-slave目錄下,創(chuàng)建三個(gè)子目錄6380、6381、6382
[root@node0719 master-slave]# mkdir 6380 6381 6382
(3)依次拷貝redis解壓目錄下的redis.conf配置文件,到這三個(gè)子目錄中。
[root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6380/ [root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6381/ [root@node0719 master-slave]# cp /root/redis-3.2.9/redis.conf ./6382/
(4)進(jìn)入6380目錄,修改redis.conf,將port端口修改成6380即可。
[root@node0719 master-slave]# cd ./6380 [root@node0719 6380]# vim redis.conf
(5)進(jìn)入6381目錄,修改redis.conf,將port端口改成6381,同時(shí)指定開(kāi)啟主從復(fù)制。
[root@node0719 6380]# cd ../6381 [root@node0719 6381]# vim redis.conf
(6)進(jìn)入6382目錄,修改redis.conf,將port端口改成6382,同時(shí)指定開(kāi)啟主從復(fù)制。
[root@node0719 6380]# cd ../6382 [root@node0719 6381]# vim redis.conf
1.1.1 測(cè)試
(1)打開(kāi)三個(gè)xshell窗口,在每一個(gè)窗口中,啟動(dòng)一個(gè)redis節(jié)點(diǎn)。查看日志輸出。(不要改成后臺(tái)模式啟動(dòng),看不到日志,不直觀)
[root@node0719 master-slave]# cd 6380 && redis-server ./redis.conf [root@node0719 master-slave]# cd 6381 && redis-server ./redis.conf [root@node0719 master-slave]# cd 6382 && redis-server ./redis.conf
(2)另外再打開(kāi)三個(gè)xshell窗口,在每一個(gè)窗口中,登陸一個(gè)redis節(jié)點(diǎn)
[root@node0719 ~]# redis-cli -p 6380 [root@node0719 ~]# redis-cli -p 6381 [root@node0719 ~]# redis-cli -p 6382
(3)在主節(jié)點(diǎn)6380上,進(jìn)行讀寫(xiě)操作,操作成功
[root@node0719 ~]# redis-cli -p 6380 127.0.0.1:6380> set user:name zs OK 127.0.0.1:6380> get user:name "zs" 127.0.0.1:6380>
(4)在從節(jié)點(diǎn)6381上
讀操作執(zhí)行成功,并且成功從6380上同步了數(shù)據(jù)
[root@node0719 ~]# redis-cli -p 6381 127.0.0.1:6381> get user:name "zs"
寫(xiě)操作執(zhí)行失敗。(從節(jié)點(diǎn),只能讀,不能寫(xiě))
127.0.0.1:6381> set user:age 18 (error) READONLY You can't write against a read only slave.
Sentinel哨兵模式
1.1 主從模式的缺陷
當(dāng)主節(jié)點(diǎn)宕機(jī)了,整個(gè)集群就沒(méi)有可寫(xiě)的節(jié)點(diǎn)了。
由于從節(jié)點(diǎn)上備份了主節(jié)點(diǎn)的所有數(shù)據(jù),那在主節(jié)點(diǎn)宕機(jī)的情況下,如果能夠?qū)墓?jié)點(diǎn)變成一個(gè)主節(jié)點(diǎn),是不是就可以解決這個(gè)問(wèn)題了呢?
答:是的,這個(gè)就是Sentinel哨兵的作用。
1.2 哨兵的任務(wù)
Redis 的 Sentinel 系統(tǒng)用于管理多個(gè) Redis 服務(wù)器(instance), 該系統(tǒng)執(zhí)行以下三個(gè)任務(wù):
監(jiān)控(Monitoring****): Sentinel 會(huì)不斷地檢查你的主服務(wù)器和從服務(wù)器是否運(yùn)作正常。
提醒(Notification****): 當(dāng)被監(jiān)控的某個(gè) Redis 服務(wù)器出現(xiàn)問(wèn)題時(shí), Sentinel 可以通過(guò) API 向管理員或者其他應(yīng)用程序發(fā)送通知。
自動(dòng)故障遷移(Automatic failover****): 當(dāng)一個(gè)主服務(wù)器不能正常工作時(shí), Sentinel 會(huì)開(kāi)始一次自動(dòng)故障遷移操作, 它會(huì)進(jìn)行選舉,將其中一個(gè)從服務(wù)器升級(jí)為新的主服務(wù)器, 并讓失效主服務(wù)器的其他從服務(wù)器改為復(fù)制新的主服務(wù)器; 當(dāng)客戶(hù)端試圖連接失效的主服務(wù)器時(shí), 集群也會(huì)向客戶(hù)端返回新主服務(wù)器的地址, 使得集群可以使用新主服務(wù)器代替失效服務(wù)器。
1.2.1 監(jiān)控(Monitoring)
(1)Sentinel可以監(jiān)控任意多個(gè)Master和該Master下的Slaves。(即多個(gè)主從模式)
(2)同一個(gè)哨兵下的、不同主從模型,彼此之間相互獨(dú)立。
(3)Sentinel會(huì)不斷檢查Master和Slaves是否正常。
1.2.2 自動(dòng)故障切換(Automatic failover)
1.2.2.1 Sentinel網(wǎng)絡(luò)
監(jiān)控同一個(gè)Master的Sentinel會(huì)自動(dòng)連接,組成一個(gè)分布式的Sentinel網(wǎng)絡(luò),互相通信并交換彼此關(guān)于被監(jiān)視服務(wù)器的信息。下圖中,三個(gè)監(jiān)控s1的Sentinel,自動(dòng)組成Sentinel網(wǎng)絡(luò)結(jié)構(gòu)。
疑問(wèn):為什么要使用sentinel網(wǎng)絡(luò)呢?
答:當(dāng)只有一個(gè)sentinel的時(shí)候,如果這個(gè)sentinel掛掉了,那么就無(wú)法實(shí)現(xiàn)自動(dòng)故障切換了。
在sentinel網(wǎng)絡(luò)中,只要還有一個(gè)sentinel活著,就可以實(shí)現(xiàn)故障切換。
1.1.1.1 故障切換的過(guò)程
(1)投票(半數(shù)原則)
當(dāng)任何一個(gè)Sentinel發(fā)現(xiàn)被監(jiān)控的Master下線時(shí),會(huì)通知其它的Sentinel開(kāi)會(huì),投票確定該Master是否下線(半數(shù)以上,所以sentinel通常配奇數(shù)個(gè))。
(2)選舉
當(dāng)Sentinel確定Master下線后,會(huì)在所有的Slaves中,選舉一個(gè)新的節(jié)點(diǎn),升級(jí)成Master節(jié)點(diǎn)。
其它Slaves節(jié)點(diǎn),轉(zhuǎn)為該節(jié)點(diǎn)的從節(jié)點(diǎn)。
(3)原Master重新上線
當(dāng)原Master節(jié)點(diǎn)重新上線后,自動(dòng)轉(zhuǎn)為當(dāng)前Master節(jié)點(diǎn)的從節(jié)點(diǎn)。
1.1 哨兵模式部署
1.1.1 需求
前提:已經(jīng)存在一個(gè)正在運(yùn)行的主從模式。
另外,配置三個(gè)Sentinel實(shí)例,監(jiān)控同一個(gè)Master節(jié)點(diǎn)。
1.1.2 配置Sentinel
(1)在/usr/local目錄下,創(chuàng)建/redis/sentinels/目錄
[root@node0719 local]# mkdir -p redis/sentinels
(2)在/sentinels目錄下,以次創(chuàng)建s1、s2、s3三個(gè)子目錄中
[root@node0719 sentinels]# mkdir s1 s2 s3
(3)依次拷貝redis解壓目錄下的sentinel.conf文件,到這三個(gè)子目錄中
[root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s1/ [root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s2/ [root@node0719 sentinels]# cp /root/redis-3.2.9/sentinel.conf ./s3/
(4)依次修改s1、s2、s3子目錄中的sentinel.conf文件,修改端口,并指定要監(jiān)控的主節(jié)點(diǎn)。(從節(jié)點(diǎn)不需要指定,sentinel會(huì)自動(dòng)識(shí)別)
S1哨兵配置如下:
S2哨兵配置如下:
S3哨兵配置如下:
(5)再打開(kāi)三個(gè)xshell窗口,在每一個(gè)窗口中,啟動(dòng)一個(gè)哨兵實(shí)例,并觀察日志輸出
[root@node0719 sentinels]# redis-sentinel ./s1/sentinel.conf [root@node0719 sentinels]# redis-sentinel ./s2/sentinel.conf [root@node0719 sentinels]# redis-sentinel ./s3/sentinel.conf
對(duì)于用redis-server啟動(dòng)哨兵的方式如下:
[root@node0719 sentinels]# redis-server ./s1/sentinel.conf --sentinel
1.1.3 測(cè)試
(1)先關(guān)閉6380節(jié)點(diǎn)(kill掉)。發(fā)現(xiàn),確實(shí)重新指定了一個(gè)主節(jié)點(diǎn)
(2)再次上線6380節(jié)點(diǎn)。發(fā)現(xiàn),6380節(jié)點(diǎn)成為了新的主節(jié)點(diǎn)的從節(jié)點(diǎn)。
1.2 結(jié)論
Sentinel哨兵模式,確實(shí)能實(shí)現(xiàn)自動(dòng)故障切換。提供穩(wěn)定的服務(wù)
redis集群搭建
Redis Cluster屬于無(wú)中心化的集群方案,
一、Redis Cluster(Redis集群)簡(jiǎn)介
RedisCluster 是 Redis 的親兒子,它是 Redis 作者自己提供的 Redis 集群化方案。 相對(duì)于 Codis 的不同,它是去中心化的,如圖所示,該集群有三個(gè) Redis 節(jié)點(diǎn)組成, 每個(gè)節(jié)點(diǎn)負(fù)責(zé)整個(gè)集群的一部分?jǐn)?shù)據(jù),每個(gè)節(jié)點(diǎn)負(fù)責(zé)的數(shù)據(jù)多少可能不一樣。這三個(gè)節(jié)點(diǎn)相 互連接組成一個(gè)對(duì)等的集群,它們之間通過(guò)一種特殊的二進(jìn)制協(xié)議相互交互集群信息。
Redis Cluster 將所有數(shù)據(jù)劃分為 16384 的 slots,它比 Codis 的 1024 個(gè)槽劃分得更為精細(xì),每個(gè)節(jié)點(diǎn)負(fù)責(zé)其中一部分槽位。槽位的信息存儲(chǔ)于每個(gè)節(jié)點(diǎn)中,它不像 Codis,它不 需要另外的分布式存儲(chǔ)來(lái)存儲(chǔ)節(jié)點(diǎn)槽位信息。
當(dāng) Redis Cluster 的客戶(hù)端來(lái)連接集群時(shí),它也會(huì)得到一份集群的槽位配置信息。這樣當(dāng)客戶(hù)端要查找某個(gè) key 時(shí),可以直接定位到目標(biāo)節(jié)點(diǎn)。
這點(diǎn)不同于 Codis,Codis 需要通過(guò) Proxy 來(lái)定位目標(biāo)節(jié)點(diǎn),RedisCluster 是直接定 位。客戶(hù)端為了可以直接定位某個(gè)具體的 key 所在的節(jié)點(diǎn),它就需要緩存槽位相關(guān)信息,這樣才可以準(zhǔn)確快速地定位到相應(yīng)的節(jié)點(diǎn)。同時(shí)因?yàn)椴畚坏男畔⒖赡軙?huì)存在客戶(hù)端與服務(wù)器不一致的情況,還需要糾正機(jī)制來(lái)實(shí)現(xiàn)槽位信息的校驗(yàn)調(diào)整。 另外,RedisCluster 的每個(gè)節(jié)點(diǎn)會(huì)將集群的配置信息持久化到配置文件中,所以必須確保配置文件是可寫(xiě)的,而且盡量不要依靠人工修改配置文件。
- redis是一個(gè)開(kāi)源的key value存儲(chǔ)系統(tǒng),受到了廣大互聯(lián)網(wǎng)公司的青睞。redis3.0版本之前只支持單例模式,在3.0版本及以后才支持集群,我這里用的是redis3.0.0版本;
- redis集群采用P2P模式,是完全去中心化的,不存在中心節(jié)點(diǎn)或者代理節(jié)點(diǎn);
- redis集群是沒(méi)有統(tǒng)一的入口的,客戶(hù)端(client)連接集群的時(shí)候連接集群中的任意節(jié)點(diǎn)(node)即可,集群內(nèi)部的節(jié)點(diǎn)是相互通信的(PING-PONG機(jī)制),每個(gè)節(jié)點(diǎn)都是一個(gè)redis實(shí)例;
- 為了實(shí)現(xiàn)集群的高可用,即判斷節(jié)點(diǎn)是否健康(能否正常使用),redis-cluster有這么一個(gè)投票容錯(cuò)機(jī)制:如果集群中超過(guò)半數(shù)的節(jié)點(diǎn)投票認(rèn)為某個(gè)節(jié)點(diǎn)掛了,那么這個(gè)節(jié)點(diǎn)就掛了(fail)。這是判斷節(jié)點(diǎn)是否掛了的方法;
- 那么如何判斷集群是否掛了呢? -> 如果集群中任意一個(gè)節(jié)點(diǎn)掛了,而且該節(jié)點(diǎn)沒(méi)有從節(jié)點(diǎn)(備份節(jié)點(diǎn)),那么這個(gè)集群就掛了。這是判斷集群是否掛了的方法;
- 那么為什么任意一個(gè)節(jié)點(diǎn)掛了(沒(méi)有從節(jié)點(diǎn))這個(gè)集群就掛了呢? -> 因?yàn)榧簝?nèi)置了16384個(gè)slot(哈希槽),并且把所有的物理節(jié)點(diǎn)映射到了這16384[0-16383]個(gè)slot上,或者說(shuō)把這些slot均等的分配給了各個(gè)節(jié)點(diǎn)。當(dāng)需要在Redis集群存放一個(gè)數(shù)據(jù)(key-value)時(shí),redis會(huì)先對(duì)這個(gè)key進(jìn)行crc16算法,然后得到一個(gè)結(jié)果。再把這個(gè)結(jié)果對(duì)16384進(jìn)行求余,這個(gè)余數(shù)會(huì)對(duì)應(yīng)[0-16383]其中一個(gè)槽,進(jìn)而決定key-value存儲(chǔ)到哪個(gè)節(jié)點(diǎn)中。所以一旦某個(gè)節(jié)點(diǎn)掛了,該節(jié)點(diǎn)對(duì)應(yīng)的slot就無(wú)法使用,那么就會(huì)導(dǎo)致集群無(wú)法正常工作。
- 綜上所述,每個(gè)Redis集群理論上最多可以有16384個(gè)節(jié)點(diǎn)。
二、集群搭建需要的環(huán)境
防火墻設(shè)置
redis集群中的每個(gè)節(jié)點(diǎn)都需要建立2個(gè)tcp連接,監(jiān)聽(tīng)這2個(gè)端口:一個(gè)端口稱(chēng)之為“客戶(hù)端端口”,用于接受客戶(hù)端指令,與客戶(hù)端交互,比如6379;另一個(gè)端口稱(chēng)之為“集群總線端口”,是在客戶(hù)端端口號(hào)上加10000,比如16379,用于節(jié)點(diǎn)之間通過(guò)二進(jìn)制協(xié)議通訊。各節(jié)點(diǎn)通過(guò)集群總線檢測(cè)宕機(jī)節(jié)點(diǎn)、更新配置、故障轉(zhuǎn)移驗(yàn)證等。客戶(hù)端只能使用客戶(hù)端端口,不能使用集群總線端口。請(qǐng)確保你的防火墻允許打開(kāi)這兩個(gè)端口,否則redis集群沒(méi)法工作。客戶(hù)端端口和集群總線端口之間的差值是固定的,集群總線端口比客戶(hù)端端口高10000。
注意,關(guān)于集群的2個(gè)端口:
- 客戶(hù)端端口(一般是6379)需要對(duì)所有客戶(hù)端和集群節(jié)點(diǎn)開(kāi)放,因?yàn)榧汗?jié)點(diǎn)需要通過(guò)該端口轉(zhuǎn)移數(shù)據(jù)。
- 集群總線端口(一般是16379)只需對(duì)集群中的所有節(jié)點(diǎn)開(kāi)放
這2個(gè)端口必須打開(kāi),否則集群沒(méi)法正常工作。
集群節(jié)點(diǎn)之間通過(guò)集群總線端口交互數(shù)據(jù),使用的協(xié)議不同于客戶(hù)端的協(xié)議,是二進(jìn)制協(xié)議,這可以減少帶寬和處理時(shí)間。
Redis集群數(shù)據(jù)的分片
Redis集群不是使用一致性哈希,而是使用哈希槽。整個(gè)redis集群有16384個(gè)哈希槽,決定一個(gè)key應(yīng)該分配到那個(gè)槽的算法是:計(jì)算該key的CRC16結(jié)果再模16834。
集群中的每個(gè)節(jié)點(diǎn)負(fù)責(zé)一部分哈希槽,比如集群中有3個(gè)節(jié)點(diǎn),則:
- 節(jié)點(diǎn)A存儲(chǔ)的哈希槽范圍是:0 – 5500
- 節(jié)點(diǎn)B存儲(chǔ)的哈希槽范圍是:5501 – 11000
- 節(jié)點(diǎn)C存儲(chǔ)的哈希槽范圍是:11001 – 16384
這樣的分布方式方便節(jié)點(diǎn)的添加和刪除。比如,需要新增一個(gè)節(jié)點(diǎn)D,只需要把A、B、C中的部分哈希槽數(shù)據(jù)移到D節(jié)點(diǎn)。同樣,如果希望在集群中刪除A節(jié)點(diǎn),只需要把A節(jié)點(diǎn)的哈希槽的數(shù)據(jù)移到B和C節(jié)點(diǎn),當(dāng)A節(jié)點(diǎn)的數(shù)據(jù)全部被移走后,A節(jié)點(diǎn)就可以完全從集群中刪除。
因?yàn)榘压2蹚囊粋€(gè)節(jié)點(diǎn)移到另一個(gè)節(jié)點(diǎn)是不需要停機(jī)的,所以,增加或刪除節(jié)點(diǎn),或更改節(jié)點(diǎn)上的哈希槽,也是不需要停機(jī)的。
如果多個(gè)key都屬于一個(gè)哈希槽,集群支持通過(guò)一個(gè)命令(或事務(wù), 或lua腳本)同時(shí)操作這些key。通過(guò)“哈希標(biāo)簽”的概念,用戶(hù)可以讓多個(gè)key分配到同一個(gè)哈希槽。哈希標(biāo)簽在集群詳細(xì)文檔中有描述,這里做個(gè)簡(jiǎn)單介紹:如果key含有大括號(hào)”{}”,則只有大括號(hào)中的字符串會(huì)參與哈希,比如”this{foo}”和”another{foo}”這2個(gè)key會(huì)分配到同一個(gè)哈希槽,所以可以在一個(gè)命令中同時(shí)操作他們。
Redis集群的一致性保證
Redis集群不能保證強(qiáng)一致性。一些已經(jīng)向客戶(hù)端確認(rèn)寫(xiě)成功的操作,會(huì)在某些不確定的情況下丟失。
產(chǎn)生寫(xiě)操作丟失的第一個(gè)原因,是因?yàn)橹鲝墓?jié)點(diǎn)之間使用了異步的方式來(lái)同步數(shù)據(jù)。
一個(gè)寫(xiě)操作是這樣一個(gè)流程:
-
1)客戶(hù)端向主節(jié)點(diǎn)B發(fā)起寫(xiě)的操作
-
2)主節(jié)點(diǎn)B回應(yīng)客戶(hù)端寫(xiě)操作成功
-
3)主節(jié)點(diǎn)B向它的從節(jié)點(diǎn)B1,B2,B3同步該寫(xiě)操作
從上面的流程可以看出來(lái),主節(jié)點(diǎn)B并沒(méi)有等從節(jié)點(diǎn)B1,B2,B3寫(xiě)完之后再回復(fù)客戶(hù)端這次操作的結(jié)果。所以,如果主節(jié)點(diǎn)B在通知客戶(hù)端寫(xiě)操作成功之后,但同步給從節(jié)點(diǎn)之前,主節(jié)點(diǎn)B故障了,其中一個(gè)沒(méi)有收到該寫(xiě)操作的從節(jié)點(diǎn)會(huì)晉升成主節(jié)點(diǎn),該寫(xiě)操作就這樣永遠(yuǎn)丟失了。
就像傳統(tǒng)的數(shù)據(jù)庫(kù),在不涉及到分布式的情況下,它每秒寫(xiě)回磁盤(pán)。為了提高一致性,可以在寫(xiě)盤(pán)完成之后再回復(fù)客戶(hù)端,但這樣就要損失性能。這種方式就等于Redis集群使用同步復(fù)制的方式。
基本上,在性能和一致性之間,需要一個(gè)權(quán)衡。
如果真的需要,Redis集群支持同步復(fù)制的方式,通過(guò)WAIT指令來(lái)實(shí)現(xiàn),這可以讓丟失寫(xiě)操作的可能性降到很低。但就算使用了同步復(fù)制的方式,Redis集群依然不是強(qiáng)一致性的,在某些復(fù)雜的情況下,比如從節(jié)點(diǎn)在與主節(jié)點(diǎn)失去連接之后被選為主節(jié)點(diǎn),不一致性還是會(huì)發(fā)生。
這種不一致性發(fā)生的情況是這樣的,當(dāng)客戶(hù)端與少數(shù)的節(jié)點(diǎn)(至少含有一個(gè)主節(jié)點(diǎn))網(wǎng)絡(luò)聯(lián)通,但他們與其他大多數(shù)節(jié)點(diǎn)網(wǎng)絡(luò)不通。比如6個(gè)節(jié)點(diǎn),A,B,C是主節(jié)點(diǎn),A1,B1,C1分別是他們的從節(jié)點(diǎn),一個(gè)客戶(hù)端稱(chēng)之為Z1。
當(dāng)網(wǎng)絡(luò)出問(wèn)題時(shí),他們被分成2組網(wǎng)絡(luò),組內(nèi)網(wǎng)絡(luò)聯(lián)通,但2組之間的網(wǎng)絡(luò)不通,假設(shè)A,C,A1,B1,C1彼此之間是聯(lián)通的,另一邊,B和Z1的網(wǎng)絡(luò)是聯(lián)通的。Z1可以繼續(xù)往B發(fā)起寫(xiě)操作,B也接受Z1的寫(xiě)操作。當(dāng)網(wǎng)絡(luò)恢復(fù)時(shí),如果這個(gè)時(shí)間間隔足夠短,集群仍然能繼續(xù)正常工作。如果時(shí)間比較長(zhǎng),以致B1在大多數(shù)的這邊被選為主節(jié)點(diǎn),那剛才Z1發(fā)給B的寫(xiě)操作都將丟失。
注意,Z1給B發(fā)送寫(xiě)操作是有一個(gè)限制的,如果時(shí)間長(zhǎng)度達(dá)到了大多數(shù)節(jié)點(diǎn)那邊可以選出一個(gè)新的主節(jié)點(diǎn)時(shí),少數(shù)這邊的所有主節(jié)點(diǎn)都不接受寫(xiě)操作。
這個(gè)時(shí)間的配置,稱(chēng)之為節(jié)點(diǎn)超時(shí)(node timeout),對(duì)集群來(lái)說(shuō)非常重要,當(dāng)達(dá)到了這個(gè)節(jié)點(diǎn)超時(shí)的時(shí)間之后,主節(jié)點(diǎn)被認(rèn)為已經(jīng)宕機(jī),可以用它
Redis集群參數(shù)配置
我們后面會(huì)部署一個(gè)Redis集群作為例子,在那之前,先介紹一下集群在redis.conf中的參數(shù)。
- cluster-enabled?<yes/no>: 如果配置”yes”則開(kāi)啟集群功能,此redis實(shí)例作為集群的一個(gè)節(jié)點(diǎn),否則,它是一個(gè)普通的單一的redis實(shí)例。
- cluster-config-file?<filename>: 注意:雖然此配置的名字叫“集群配置文件”,但是此配置文件不能人工編輯,它是集群節(jié)點(diǎn)自動(dòng)維護(hù)的文件,主要用于記錄集群中有哪些節(jié)點(diǎn)、他們的狀態(tài)以及一些持久化參數(shù)等,方便在重啟時(shí)恢復(fù)這些狀態(tài)。通常是在收到請(qǐng)求之后這個(gè)文件就會(huì)被更新。
- cluster-node-timeout?<milliseconds>: 這是集群中的節(jié)點(diǎn)能夠失聯(lián)的最大時(shí)間,超過(guò)這個(gè)時(shí)間,該節(jié)點(diǎn)就會(huì)被認(rèn)為故障。如果主節(jié)點(diǎn)超過(guò)這個(gè)時(shí)間還是不可達(dá),則用它的從節(jié)點(diǎn)將啟動(dòng)故障遷移,升級(jí)成主節(jié)點(diǎn)。注意,任何一個(gè)節(jié)點(diǎn)在這個(gè)時(shí)間之內(nèi)如果還是沒(méi)有連上大部分的主節(jié)點(diǎn),則此節(jié)點(diǎn)將停止接收任何請(qǐng)求。
- cluster-slave-validity-factor?<factor>: 如果設(shè)置成0,則無(wú)論從節(jié)點(diǎn)與主節(jié)點(diǎn)失聯(lián)多久,從節(jié)點(diǎn)都會(huì)嘗試升級(jí)成主節(jié)點(diǎn)。如果設(shè)置成正數(shù),則cluster-node-timeout乘以cluster-slave-validity-factor得到的時(shí)間,是從節(jié)點(diǎn)與主節(jié)點(diǎn)失聯(lián)后,此從節(jié)點(diǎn)數(shù)據(jù)有效的最長(zhǎng)時(shí)間,超過(guò)這個(gè)時(shí)間,從節(jié)點(diǎn)不會(huì)啟動(dòng)故障遷移。假設(shè)cluster-node-timeout=5,cluster-slave-validity-factor=10,則如果從節(jié)點(diǎn)跟主節(jié)點(diǎn)失聯(lián)超過(guò)50秒,此從節(jié)點(diǎn)不能成為主節(jié)點(diǎn)。注意,如果此參數(shù)配置為非0,將可能出現(xiàn)由于某主節(jié)點(diǎn)失聯(lián)卻沒(méi)有從節(jié)點(diǎn)能頂上的情況,從而導(dǎo)致集群不能正常工作,在這種情況下,只有等到原來(lái)的主節(jié)點(diǎn)重新回歸到集群,集群才恢復(fù)運(yùn)作。
- cluster-migration-barrier?<count>:主節(jié)點(diǎn)需要的最小從節(jié)點(diǎn)數(shù),只有達(dá)到這個(gè)數(shù),主節(jié)點(diǎn)失敗時(shí),它從節(jié)點(diǎn)才會(huì)進(jìn)行遷移。更詳細(xì)介紹可以看本教程后面關(guān)于副本遷移到部分。
- cluster-require-full-coverage?<yes/no>:在部分key所在的節(jié)點(diǎn)不可用時(shí),如果此參數(shù)設(shè)置為”yes”(默認(rèn)值), 則整個(gè)集群停止接受操作;如果此參數(shù)設(shè)置為”no”,則集群依然為可達(dá)節(jié)點(diǎn)上的key提供讀操作。
port 7000 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes
開(kāi)啟集群模式只需打開(kāi)cluster-enabled配置項(xiàng)即可。每一個(gè)redis實(shí)例都包含一個(gè)配置文件,默認(rèn)是nodes.conf,用于存儲(chǔ)此節(jié)點(diǎn)的一些配置信息。這個(gè)配置文件由redis集群的節(jié)點(diǎn)自行創(chuàng)建和更新,不能由人手動(dòng)地去修改。
一個(gè)最小的集群需要最少3個(gè)主節(jié)點(diǎn)。第一次測(cè)試,強(qiáng)烈建議你配置6個(gè)節(jié)點(diǎn):3個(gè)主節(jié)點(diǎn)和3個(gè)從節(jié)點(diǎn)
搭建準(zhǔn)備
2.1 Redis集群至少需要3個(gè)節(jié)點(diǎn),因?yàn)橥镀比蒎e(cuò)機(jī)制要求超過(guò)半數(shù)節(jié)點(diǎn)認(rèn)為某個(gè)節(jié)點(diǎn)掛了該節(jié)點(diǎn)才是掛了,所以2個(gè)節(jié)點(diǎn)無(wú)法構(gòu)成集群。
2.2 要保證集群的高可用,需要每個(gè)節(jié)點(diǎn)都有從節(jié)點(diǎn),也就是備份節(jié)點(diǎn),所以Redis集群至少需要6臺(tái)服務(wù)器。因?yàn)槲覜](méi)有那么多服務(wù)器,也啟動(dòng)不了那么多虛擬機(jī),所在這里搭建的是偽分布式集群,即一臺(tái)服務(wù)器虛擬運(yùn)行6個(gè)redis實(shí)例,修改端口號(hào)為(7001-7006),當(dāng)然實(shí)際生產(chǎn)環(huán)境的Redis集群搭建和這里是一樣的。
將redis-cluster/redis01文件復(fù)制5份到redis-cluster目錄下(redis02-redis06),創(chuàng)建6個(gè)redis實(shí)例,模擬Redis集群的6個(gè)節(jié)點(diǎn)。然后將其余5個(gè)文件下的redis.conf里面的端口號(hào)分別修改為7002-7006。分別如下圖所示:
創(chuàng)建redis02-06目錄
接著啟動(dòng)所有redis節(jié)點(diǎn),由于一個(gè)一個(gè)啟動(dòng)太麻煩了,所以在這里創(chuàng)建一個(gè)批量啟動(dòng)redis節(jié)點(diǎn)的腳本文件,命令為start-all.sh,文件內(nèi)容如下:
cd redis01 ./redis-server redis.conf cd .. cd redis02 ./redis-server redis.conf cd .. cd redis03 ./redis-server redis.conf cd .. cd redis04 ./redis-server redis.conf cd .. cd redis05 ./redis-server redis.conf cd .. cd redis06 ./redis-server redis.conf cd ..
創(chuàng)建好啟動(dòng)腳本文件之后,需要修改該腳本的權(quán)限,使之能夠執(zhí)行,指令如下:
chmod +x start-all.sh
執(zhí)行start-all.sh腳本,啟動(dòng)6個(gè)redis節(jié)點(diǎn)
至此6個(gè)redis節(jié)點(diǎn)啟動(dòng)成功,接下來(lái)正式開(kāi)啟搭建集群,
三:實(shí)戰(zhàn):搭建集群
Redis 官方提供了 redis-trib.rb 這個(gè)工具,作為搭建集群的專(zhuān)門(mén)工具。
Redis的實(shí)例全部運(yùn)行之后,還需要redis-trib.rb工具來(lái)完成集群的創(chuàng)建,redis-trib.rb二進(jìn)制文件在Redis包主目錄下的src目錄中,運(yùn)行該工具依賴(lài)Ruby環(huán)境和gem,因此需要提前安裝Ruby。
因?yàn)檫@個(gè)工具是一個(gè)ruby腳本文件,所以這個(gè)工具的運(yùn)行需要ruby的運(yùn)行環(huán)境,該環(huán)境相當(dāng)于JVM虛擬機(jī)環(huán)境,是的ruby腳本運(yùn)行時(shí),就相當(dāng)于java語(yǔ)言的運(yùn)行需要在jvm上。
安裝ruby,指令如下:
yum install ruby
/usr/local/redis/redis-4.0.9/src/redis-trib.rb create --replicas 1 \ 10.10.1.129:7001 10.10.1.129:7002 10.10.1.129:7003 \ 10.10.1.130:7004 10.10.1.130:7005 10.10.1.130:7006 \ 10.10.1.131:7007 10.10.1.131:7008 10.10.1.131:7009
--replicas 1 表示主從復(fù)制比例為 1:1,即一個(gè)主節(jié)點(diǎn)對(duì)應(yīng)一個(gè)從節(jié)點(diǎn);然后,默認(rèn)給我們分配好了每個(gè)主節(jié)點(diǎn)和對(duì)應(yīng)從節(jié)點(diǎn)服務(wù),以及 solt 的大小,因?yàn)樵?Redis 集群中有且僅有 16383 個(gè) solt ,默認(rèn)情況會(huì)給我們平均分配,當(dāng)然你可以指定,后續(xù)的增減節(jié)點(diǎn)也可以重新分配。
/usr/local/redis/redis-4.0.9/src/redis-trib.rb create --replicas 1 \ > 10.10.1.129:7001 10.10.1.129:7002 10.10.1.129:7003 \ > 10.10.1.130:7004 10.10.1.130:7005 10.10.1.130:7006 \ > 10.10.1.131:7007 10.10.1.131:7008 10.10.1.131:7009 >>> Creating cluster >>> Performing hash slots allocation on 9 nodes... Using 4 masters: 10.10.1.129:7001 10.10.1.130:7004 10.10.1.131:7007 10.10.1.129:7002 Adding replica 10.10.1.131:7008 to 10.10.1.129:7001 Adding replica 10.10.1.129:7003 to 10.10.1.130:7004 Adding replica 10.10.1.130:7006 to 10.10.1.131:7007 Adding replica 10.10.1.131:7009 to 10.10.1.129:7002 Adding replica 10.10.1.130:7005 to 10.10.1.129:7001 M: 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 10.10.1.129:7001 slots:0-4095 (4096 slots) master M: 924d61969343b5cc2200bd3a2277e815dc76048c 10.10.1.129:7002 slots:12288-16383 (4096 slots) master S: b9ba251f575b5396da4bea307e25a98d85b3c504 10.10.1.129:7003 replicates a4a5de0be9bb5704eec17cbe0223076eb38fc4a4 M: a4a5de0be9bb5704eec17cbe0223076eb38fc4a4 10.10.1.130:7004 slots:4096-8191 (4096 slots) master S: bdc2f6b254459a6a6d038d93e5a3d3a67fe3e936 10.10.1.130:7005 replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 S: 4ae20400d02e57e274f9b9f29d4ba120aa2b574c 10.10.1.130:7006 replicates 2b0f974e151cd798f474107ac68a47e188cc88a2 M: 2b0f974e151cd798f474107ac68a47e188cc88a2 10.10.1.131:7007 slots:8192-12287 (4096 slots) master S: b240d86fdf6abc73df059baf64b930387664da15 10.10.1.131:7008 replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 S: 5b7989d5370aef41679e92a6bd34c30ac3be3581 10.10.1.131:7009 replicates 924d61969343b5cc2200bd3a2277e815dc76048c Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join........ >>> Performing Cluster Check (using node 10.10.1.129:7001) M: 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 10.10.1.129:7001 slots:0-4095 (4096 slots) master 2 additional replica(s) S: 4ae20400d02e57e274f9b9f29d4ba120aa2b574c 10.10.1.130:7006 slots: (0 slots) slave replicates 2b0f974e151cd798f474107ac68a47e188cc88a2 M: 924d61969343b5cc2200bd3a2277e815dc76048c 10.10.1.129:7002 slots:12288-16383 (4096 slots) master 1 additional replica(s) M: 2b0f974e151cd798f474107ac68a47e188cc88a2 10.10.1.131:7007 slots:8192-12287 (4096 slots) master 1 additional replica(s) S: 5b7989d5370aef41679e92a6bd34c30ac3be3581 10.10.1.131:7009 slots: (0 slots) slave replicates 924d61969343b5cc2200bd3a2277e815dc76048c S: b9ba251f575b5396da4bea307e25a98d85b3c504 10.10.1.129:7003 slots: (0 slots) slave replicates a4a5de0be9bb5704eec17cbe0223076eb38fc4a4 S: b240d86fdf6abc73df059baf64b930387664da15 10.10.1.131:7008 slots: (0 slots) slave replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 S: bdc2f6b254459a6a6d038d93e5a3d3a67fe3e936 10.10.1.130:7005 slots: (0 slots) slave replicates 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 M: a4a5de0be9bb5704eec17cbe0223076eb38fc4a4 10.10.1.130:7004 slots:4096-8191 (4096 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
出現(xiàn)上述的輸出則代表集群搭建成功啦!!!
說(shuō)明:
M: 7a047cfaae70c30d0d7e1a5d9854eb7f11afe957 為主節(jié)點(diǎn)id S: b240d86fdf6abc73df059baf64b930387664da15 為從節(jié)點(diǎn)的id
目前來(lái)看,7001、7002、7004、7007 為主節(jié)點(diǎn),7003、7005、7008、7009 為從節(jié)點(diǎn),并向你確認(rèn)是否同意這么配置。輸入 yes 后,會(huì)開(kāi)始集群創(chuàng)建。
四:redis cluster小結(jié)
redis cluster在設(shè)計(jì)的時(shí)候,就考慮到了去中心化、去中間件,也就是說(shuō),集群中的每個(gè)節(jié)點(diǎn)都是平等關(guān)系,都是對(duì)等的,每個(gè)節(jié)點(diǎn)都保存各自的數(shù)據(jù)和整個(gè)集群的狀態(tài)。每個(gè)節(jié)點(diǎn)都和其他所有節(jié)點(diǎn)連接,而且這些連接保持活躍,這樣就保證了我們只需要連接集群中的任意一個(gè)節(jié)點(diǎn),就可以獲取到其他節(jié)點(diǎn)的數(shù)據(jù)。
Redis 集群沒(méi)有并使用傳統(tǒng)的一致性哈希來(lái)分配數(shù)據(jù),而是采用另外一種叫做哈希槽 (hash slot)的方式來(lái)分配的。redis cluster 默認(rèn)分配了 16384 個(gè) slot,當(dāng)我們 set 一個(gè) key 時(shí),會(huì)用CRC16算法來(lái)取模得到所屬的 slot,然后將這個(gè) key 分到哈希槽區(qū)間的節(jié)點(diǎn)上,具體算法就是:CRC16(key) % 16384。所以我們?cè)跍y(cè)試的時(shí)候看到 set 和 get 的時(shí)候,直接跳轉(zhuǎn)到了7000端口的節(jié)點(diǎn)。
Redis 集群會(huì)把數(shù)據(jù)存在一個(gè) master 節(jié)點(diǎn),然后在這個(gè) master 和其對(duì)應(yīng)的 salve 之間進(jìn)行數(shù)據(jù)同步。當(dāng)讀取數(shù)據(jù)時(shí),也根據(jù)一致性哈希算法到對(duì)應(yīng)的 master 節(jié)點(diǎn)獲取數(shù)據(jù)。只有當(dāng)一個(gè) master 掛掉之后,才會(huì)啟動(dòng)一個(gè)對(duì)應(yīng)的 salve 節(jié)點(diǎn),充當(dāng) master 。
需要注意的是:必須要3個(gè)或以上的主節(jié)點(diǎn),否則在創(chuàng)建集群時(shí)會(huì)失敗,并且當(dāng)存活的主節(jié)點(diǎn)數(shù)小于總節(jié)點(diǎn)數(shù)的一半時(shí),整個(gè)集群就無(wú)法提供服務(wù)了。
Codis
Codis 是一個(gè)分布式?Redis?解決方案, 對(duì)于上層的應(yīng)用來(lái)說(shuō), 連接到 Codis Proxy 和連接原生的 Redis Server 沒(méi)有明顯的區(qū)別 (有一些命令不支持), 上層應(yīng)用可以像使用單機(jī)的 Redis 一樣使用, Codis 底層會(huì)處理請(qǐng)求的轉(zhuǎn)發(fā), 不停機(jī)的數(shù)據(jù)遷移等工作, 所有后邊的一切事情, 對(duì)于前面的客戶(hù)端來(lái)說(shuō)是透明的, 可以簡(jiǎn)單的認(rèn)為后邊連接的是一個(gè)內(nèi)存無(wú)限大的 Redis 服務(wù),當(dāng)然,前段時(shí)間redis官方的3.0出了穩(wěn)定版,3.0支持集群功能,codis的實(shí)現(xiàn)原理和3.0的集群功能差不多,我了解的現(xiàn)在美團(tuán)、阿里已經(jīng)用了3.0的集群功能了,
1.什么是Codis?
Codis 是一個(gè)分布式 Redis 解決方案, 對(duì)于上層的應(yīng)用來(lái)說(shuō), 連接到 Codis Proxy 和連接原生的 Redis Server 沒(méi)有明顯的區(qū)別
(不支持的命令列表), 上層應(yīng)用可以像使用單機(jī)的 Redis 一樣使用, Codis 底層會(huì)處理請(qǐng)求的轉(zhuǎn)發(fā), 不停機(jī)的數(shù)據(jù)遷移等工作,
所有后邊的一切事情, 對(duì)于前面的客戶(hù)端來(lái)說(shuō)是透明的, 可以簡(jiǎn)單的認(rèn)為后邊連接的是一個(gè)內(nèi)存無(wú)限大的 Redis 服務(wù)。
2.codis介紹
Codis是一個(gè)分布式Redis解決方案,對(duì)于上層的應(yīng)用來(lái)說(shuō),連接到Codis Proxy和連接原生的RedisServer沒(méi)有明顯的區(qū)別,有部分命令不支持。
Codis底層會(huì)處理請(qǐng)求的轉(zhuǎn)發(fā),不停機(jī)的數(shù)據(jù)遷移等工作,所有后邊的一切事情,
對(duì)于前面的客戶(hù)端來(lái)說(shuō)是透明的,可以簡(jiǎn)單的認(rèn)為后邊連接的是一個(gè)內(nèi)存無(wú)限大的Redis服務(wù).
Codis由四部分組成
| Codis-config | 是codis的管理工具,包括添加/刪除redis節(jié)點(diǎn)添加/刪除proxy節(jié)點(diǎn),發(fā)起數(shù)據(jù)遷移等操作,自帶httpserver,支持管理后臺(tái)方式管理配置 |
| Codis-server | 是codis維護(hù)的redis分支,基于2.8.21分支,加入了slot的支持和原子的數(shù)據(jù)遷移指令; codis-proxy和codis-config只能和這個(gè)版本的redis交互才能正常運(yùn)行 |
| Zookeeper | 用于codis集群元數(shù)據(jù)的存儲(chǔ),維護(hù)codis集群節(jié)點(diǎn) |
3.Codis的架構(gòu)
4.Codis的優(yōu)缺點(diǎn)
(1)優(yōu)點(diǎn)
對(duì)客戶(hù)端透明,與codis交互方式和redis本身交互一樣
支持在線數(shù)據(jù)遷移,遷移過(guò)程對(duì)客戶(hù)端透明有簡(jiǎn)單的管理和監(jiān)控界面
支持高可用,無(wú)論是redis數(shù)據(jù)存儲(chǔ)還是代理節(jié)點(diǎn)
自動(dòng)進(jìn)行數(shù)據(jù)的均衡分配
最大支持1024個(gè)redis實(shí)例,存儲(chǔ)容量海量
高性能
(2)缺點(diǎn)
采用自有的redis分支,不能與原版的redis保持同步
如果codis的proxy只有一個(gè)的情況下, redis的性能會(huì)下降20%左右
某些命令不支持,比如事務(wù)命令muti
國(guó)內(nèi)開(kāi)源產(chǎn)品,活躍度相對(duì)弱一些
常見(jiàn)問(wèn)題
1 如何解決緩存穿透與緩存雪崩
如何解決緩存穿透與緩存雪崩。這是基本問(wèn)題也是面試常問(wèn)問(wèn)題。
作為一個(gè)內(nèi)存數(shù)據(jù)庫(kù),redis也總是免不了有各種各樣的問(wèn)題,這篇文章主要是針對(duì)其中兩個(gè)問(wèn)題進(jìn)行講解:緩存穿透和緩存雪崩。
一、緩存穿透
1、概念
緩存穿透的概念很簡(jiǎn)單,用戶(hù)想要查詢(xún)一個(gè)數(shù)據(jù),發(fā)現(xiàn)redis內(nèi)存數(shù)據(jù)庫(kù)沒(méi)有,也就是緩存沒(méi)有命中,于是向持久層數(shù)據(jù)庫(kù)查詢(xún)。發(fā)現(xiàn)也沒(méi)有,于是本次查詢(xún)失敗。當(dāng)用戶(hù)很多的時(shí)候,緩存都沒(méi)有命中,于是都去請(qǐng)求了持久層數(shù)據(jù)庫(kù)。這會(huì)給持久層數(shù)據(jù)庫(kù)造成很大的壓力,這時(shí)候就相當(dāng)于出現(xiàn)了緩存穿透。
這里需要注意和緩存擊穿的區(qū)別,緩存擊穿,是指一個(gè)key非常熱點(diǎn),在不停的扛著大并發(fā),大并發(fā)集中對(duì)這一個(gè)點(diǎn)進(jìn)行訪問(wèn),當(dāng)這個(gè)key在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存,直接請(qǐng)求數(shù)據(jù)庫(kù),就像在一個(gè)屏障上鑿開(kāi)了一個(gè)洞。
為了避免緩存穿透其實(shí)有很多種解決方案。下面介紹幾種。
2、解決方案
(1)布隆過(guò)濾器
布隆過(guò)濾器是一種數(shù)據(jù)結(jié)構(gòu),垃圾網(wǎng)站和正常網(wǎng)站加起來(lái)全世界據(jù)統(tǒng)計(jì)也有幾十億個(gè)。網(wǎng)警要過(guò)濾這些垃圾網(wǎng)站,總不能到數(shù)據(jù)庫(kù)里面一個(gè)一個(gè)去比較吧,這就可以使用布隆過(guò)濾器。假設(shè)我們存儲(chǔ)一億個(gè)垃圾網(wǎng)站地址。
可以先有一億個(gè)二進(jìn)制比特,然后網(wǎng)警用八個(gè)不同的隨機(jī)數(shù)產(chǎn)生器(F1,F2, …,F8) 產(chǎn)生八個(gè)信息指紋(f1, f2, …, f8)。接下來(lái)用一個(gè)隨機(jī)數(shù)產(chǎn)生器 G 把這八個(gè)信息指紋映射到 1 到1億中的八個(gè)自然數(shù) g1, g2, …,g8。最后把這八個(gè)位置的二進(jìn)制全部設(shè)置為一。過(guò)程如下:
有一天網(wǎng)警查到了一個(gè)可疑的網(wǎng)站,想判斷一下是否是XX網(wǎng)站,首先將可疑網(wǎng)站通過(guò)哈希映射到1億個(gè)比特?cái)?shù)組上的8個(gè)點(diǎn)。如果8個(gè)點(diǎn)的其中有一個(gè)點(diǎn)不為1,則可以判斷該元素一定不存在集合中。
那這個(gè)布隆過(guò)濾器是如何解決redis中的緩存穿透呢?很簡(jiǎn)單首先也是對(duì)所有可能查詢(xún)的參數(shù)以hash形式存儲(chǔ),當(dāng)用戶(hù)想要查詢(xún)的時(shí)候,使用布隆過(guò)濾器發(fā)現(xiàn)不在集合中,就直接丟棄,不再對(duì)持久層查詢(xún)。
這個(gè)形式很簡(jiǎn)單。
2、緩存空對(duì)象
當(dāng)存儲(chǔ)層不命中后,即使返回的空對(duì)象也將其緩存起來(lái),同時(shí)會(huì)設(shè)置一個(gè)過(guò)期時(shí)間,之后再訪問(wèn)這個(gè)數(shù)據(jù)將會(huì)從緩存中獲取,保護(hù)了后端數(shù)據(jù)源;
但是這種方法會(huì)存在兩個(gè)問(wèn)題:
如果空值能夠被緩存起來(lái),這就意味著緩存需要更多的空間存儲(chǔ)更多的鍵,因?yàn)檫@當(dāng)中可能會(huì)有很多的空值的鍵;即使對(duì)空值設(shè)置了過(guò)期時(shí)間,還是會(huì)存在緩存層和存儲(chǔ)層的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致,這對(duì)于需要保持一致性的業(yè)務(wù)會(huì)有影響。
如何避免?
1:對(duì)查詢(xún)結(jié)果為空的情況也進(jìn)行緩存,緩存時(shí)間設(shè)置短一點(diǎn),或者該key對(duì)應(yīng)的數(shù)據(jù)insert了之后清理緩存。
2:對(duì)一定不存在的key進(jìn)行過(guò)濾。可以把所有的可能存在的key放到一個(gè)大的Bitmap中,查詢(xún)時(shí)通過(guò)該bitmap過(guò)濾。
二、緩存雪崩
1、概念
緩存雪崩是指,緩存層出現(xiàn)了錯(cuò)誤,不能正常工作了。于是所有的請(qǐng)求都會(huì)達(dá)到存儲(chǔ)層,存儲(chǔ)層的調(diào)用量會(huì)暴增,造成存儲(chǔ)層也會(huì)掛掉的情況。
2、解決方案
(1)redis高可用
這個(gè)思想的含義是,既然redis有可能掛掉,那我多增設(shè)幾臺(tái)redis,這樣一臺(tái)掛掉之后其他的還可以繼續(xù)工作,其實(shí)就是搭建的集群。
(2)限流降級(jí)
這個(gè)解決方案的思想是,在緩存失效后,通過(guò)加鎖或者隊(duì)列來(lái)控制讀數(shù)據(jù)庫(kù)寫(xiě)緩存的線程數(shù)量。比如對(duì)某個(gè)key只允許一個(gè)線程查詢(xún)數(shù)據(jù)和寫(xiě)緩存,其他線程等待。
(3)數(shù)據(jù)預(yù)熱
數(shù)據(jù)加熱的含義就是在正式部署之前,我先把可能的數(shù)據(jù)先預(yù)先訪問(wèn)一遍,這樣部分可能大量訪問(wèn)的數(shù)據(jù)就會(huì)加載到緩存中。在即將發(fā)生大并發(fā)訪問(wèn)前手動(dòng)觸發(fā)加載緩存不同的key,設(shè)置不同的過(guò)期時(shí)間,讓緩存失效的時(shí)間點(diǎn)盡量均勻。
當(dāng)緩存服務(wù)器重啟或者大量緩存集中在某一個(gè)時(shí)間段失效,這樣在失效的時(shí)候,會(huì)給后端系統(tǒng)帶來(lái)很大壓力。導(dǎo)致系統(tǒng)崩潰。
如何避免?
1:在緩存失效后,通過(guò)加鎖或者隊(duì)列來(lái)控制讀數(shù)據(jù)庫(kù)寫(xiě)緩存的線程數(shù)量。比如對(duì)某個(gè)key只允許一個(gè)線程查詢(xún)數(shù)據(jù)和寫(xiě)緩存,其他線程等待。
2:做二級(jí)緩存,A1為原始緩存,A2為拷貝緩存,A1失效時(shí),可以訪問(wèn)A2,A1緩存失效時(shí)間設(shè)置為短期,A2設(shè)置為長(zhǎng)期
3:不同的key,設(shè)置不同的過(guò)期時(shí)間,讓緩存失效的時(shí)間點(diǎn)盡量均勻。
2 什么是Redis持久化?Redis有哪幾種持久化方式?優(yōu)缺點(diǎn)是什么?
持久化就是把內(nèi)存的數(shù)據(jù)寫(xiě)到磁盤(pán)中去,防止服務(wù)宕機(jī)了內(nèi)存數(shù)據(jù)丟失。
Redis 提供了兩種持久化方式:RDB(默認(rèn)) 和AOF
RDB:
rdb是Redis DataBase縮寫(xiě)
功能核心函數(shù)rdbSave(生成RDB文件)和rdbLoad(從文件加載內(nèi)存)兩個(gè)函數(shù)
AOF:
Aof是Append-only file縮寫(xiě)
每當(dāng)執(zhí)行服務(wù)器(定時(shí))任務(wù)或者函數(shù)時(shí)flushAppendOnlyFile 函數(shù)都會(huì)被調(diào)用, 這個(gè)函數(shù)執(zhí)行以下兩個(gè)工作
aof寫(xiě)入保存:
WRITE:根據(jù)條件,將 aof_buf 中的緩存寫(xiě)入到 AOF 文件
SAVE:根據(jù)條件,調(diào)用 fsync 或 fdatasync 函數(shù),將 AOF 文件保存到磁盤(pán)中。
存儲(chǔ)結(jié)構(gòu):
內(nèi)容是redis通訊協(xié)議(RESP )格式的命令文本存儲(chǔ)。
比較:
1、aof文件比rdb更新頻率高,優(yōu)先使用aof還原數(shù)據(jù)。
2、aof比rdb更安全也更大
3、rdb性能比aof好
4、如果兩個(gè)都配了優(yōu)先加載AOF
3 什么是一致性哈希算法
Redis 集群會(huì)把數(shù)據(jù)存在一個(gè) master 節(jié)點(diǎn),然后在這個(gè) master 和其對(duì)應(yīng)的 salve 之間進(jìn)行數(shù)據(jù)同步。當(dāng)讀取數(shù)據(jù)時(shí),也根據(jù)一致性哈希算法到對(duì)應(yīng)的 master 節(jié)點(diǎn)獲取數(shù)據(jù)。只有當(dāng)一個(gè) master 掛掉之后,才會(huì)啟動(dòng)一個(gè)對(duì)應(yīng)的 salve 節(jié)點(diǎn),充當(dāng) master 。什么是一致性哈希算法?
一致哈希 是一種特殊的哈希算法。在使用一致哈希算法后,哈希表槽位數(shù)(大小)的改變平均只需要對(duì) K/n 個(gè)關(guān)鍵字重新映射,其中K是關(guān)鍵字的數(shù)量, n是槽位數(shù)量。然而在傳統(tǒng)的哈希表中,添加或刪除一個(gè)槽位的幾乎需要對(duì)所有關(guān)鍵字進(jìn)行重新映射。
簡(jiǎn)單的說(shuō),一致性哈希是將整個(gè)哈希值空間組織成一個(gè)虛擬的圓環(huán),如假設(shè)哈希函數(shù)H的值空間為0-2^32-1(哈希值是32位無(wú)符號(hào)整形),整個(gè)哈希空間環(huán)如下:
整個(gè)空間按順時(shí)針?lè)较蚪M織,0和2^32-1在零點(diǎn)中方向重合。
接下來(lái),把服務(wù)器按照IP或主機(jī)名作為關(guān)鍵字進(jìn)行哈希,這樣就能確定其在哈希環(huán)的位置。
然后,我們就可以使用哈希函數(shù)H計(jì)算值為key的數(shù)據(jù)在哈希環(huán)的具體位置h,根據(jù)h確定在環(huán)中的具體位置,從此位置沿順時(shí)針滾動(dòng),遇到的第一臺(tái)服務(wù)器就是其應(yīng)該定位到的服務(wù)器。
例如我們有A、B、C、D四個(gè)數(shù)據(jù)對(duì)象,經(jīng)過(guò)哈希計(jì)算后,在環(huán)空間上的位置如下:
根據(jù)一致性哈希算法,數(shù)據(jù)A會(huì)被定為到Server 1上,數(shù)據(jù)B被定為到Server 2上,而C、D被定為到Server 3上。
3.1 容錯(cuò)性和擴(kuò)展性
那么使用一致性哈希算法的容錯(cuò)性和擴(kuò)展性如何呢?
3.1.1 容錯(cuò)性
假如RedisService2宕機(jī)了,那么會(huì)怎樣呢?
那么,數(shù)據(jù)B對(duì)應(yīng)的節(jié)點(diǎn)保存到RedisService3中。因此,其中一臺(tái)宕機(jī)后,干擾的只有前面的數(shù)據(jù)(原數(shù)據(jù)被保存到順時(shí)針的下一個(gè)服務(wù)器),而不會(huì)干擾到其他的數(shù)據(jù)。
3.1.2 擴(kuò)展性
下面考慮另一種情況,假如增加一臺(tái)服務(wù)器Redis4,具體位置如下圖所示:
原本數(shù)據(jù)C是保存到Redis3中,但由于增加了Redis4,數(shù)據(jù)C被保存到Redis4中。干擾的也只有Redis3而已,其他數(shù)據(jù)不會(huì)受到影響。
因此,一致性哈希算法對(duì)于節(jié)點(diǎn)的增減都只需重定位換空間的一小部分即可,具有較好的容錯(cuò)性和可擴(kuò)展性
3.2 虛擬節(jié)點(diǎn)
前面部分都是講述到Redis節(jié)點(diǎn)較多和節(jié)點(diǎn)分布較為均衡的情況,如果節(jié)點(diǎn)較少就會(huì)出現(xiàn)節(jié)點(diǎn)分布不均衡造成數(shù)據(jù)傾斜問(wèn)題。
例如,我們的的系統(tǒng)有兩臺(tái)Redis,分布的環(huán)位置如下圖所示:
這會(huì)產(chǎn)生一種情況,Redis4的hash范圍比Redis3的hash范圍大,導(dǎo)致數(shù)據(jù)大部分都存儲(chǔ)在Redis4中,數(shù)據(jù)存儲(chǔ)不平衡。
為了解決這種數(shù)據(jù)存儲(chǔ)不平衡的問(wèn)題,一致性哈希算法引入了虛擬節(jié)點(diǎn)機(jī)制,即對(duì)每個(gè)節(jié)點(diǎn)計(jì)算多個(gè)哈希值,每個(gè)計(jì)算結(jié)果位置都放置在對(duì)應(yīng)節(jié)點(diǎn)中,這些節(jié)點(diǎn)稱(chēng)為虛擬節(jié)點(diǎn)。
具體做法可以在服務(wù)器IP或主機(jī)名的后面增加編號(hào)來(lái)實(shí)現(xiàn),例如上面的情況,可以為每個(gè)服務(wù)節(jié)點(diǎn)增加三個(gè)虛擬節(jié)點(diǎn),于是可以分為 RedisService1#1、 RedisService1#2、 RedisService1#3、 RedisService2#1、 RedisService2#2、 RedisService2#3,具體位置如下圖所示:
對(duì)于數(shù)據(jù)定位的hash算法仍然不變,只是增加了虛擬節(jié)點(diǎn)到實(shí)際節(jié)點(diǎn)的映射。例如,數(shù)據(jù)C保存到虛擬節(jié)點(diǎn)Redis1#2,實(shí)際上數(shù)據(jù)保存到Redis1中。這樣,就能解決服務(wù)節(jié)點(diǎn)少時(shí)數(shù)據(jù)不平均的問(wèn)題。在實(shí)際應(yīng)用中,通常將虛擬節(jié)點(diǎn)數(shù)設(shè)置為32甚至更大,因此即使很少的服務(wù)節(jié)點(diǎn)也能做到相對(duì)均勻的數(shù)據(jù)分布。
總結(jié)
以上是生活随笔為你收集整理的Redis的架构模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Redis--COW(Copy On W
- 下一篇: Redis cluster原理