如今,緩存系統(tǒng)的應(yīng)用非常廣泛,能夠用來(lái)提高并發(fā)數(shù)、數(shù)據(jù)吞吐量,提高快速響應(yīng)能力。那么當(dāng)數(shù)據(jù)量達(dá)到一定程度,單機(jī)環(huán)境可能就顯得有些力不從心了,就需要一個(gè)分布式緩存系統(tǒng)。
1.緩存系統(tǒng)的選擇
圖1-1
1.1緩存分類(lèi)
如上圖所示,首先緩存大致可以分為四大類(lèi)。
CDN緩存:CDN即內(nèi)容分發(fā)網(wǎng)絡(luò),CDN邊緣節(jié)點(diǎn)將數(shù)據(jù)緩存起來(lái)。
反向代理緩存:如Nginx的緩存。
本地緩存:代表的有EhCache和GuavaCache。
分布式緩存:各緩存系統(tǒng)。
1.2分布式緩存
本文主要探討各分布式緩存系統(tǒng),如圖1-1所示,列出了五種:
其中EvCache和Aerospike使用場(chǎng)景不是那么通用和廣泛。
EvCache:是Netflix的基于Memcached&Spymemcached的緩存方案。
Aerospike:是可基于SSD的KVNoSQL數(shù)據(jù)庫(kù)。
除此之外,還有三種常見(jiàn)緩存系統(tǒng)。
Tair:阿里開(kāi)源,跨機(jī)房、性能隨結(jié)點(diǎn)添加線(xiàn)性上升、適用大數(shù)據(jù)量。Tair還有三種引擎。
LDB:基于googlelevelDB,支持KV和類(lèi)HashMap結(jié)構(gòu),性能稍低,持久化可靠性最高。
MDB:基于Memcache,支持KV和類(lèi)HashMap,性能最優(yōu),不支持持久化存儲(chǔ)。
RDB:基于Redis。
Memcache:不支持?jǐn)?shù)據(jù)同步、分布式支持較差。
Redis:社區(qū)活躍、使用最多。
綜上所述,在一般情況下,考慮到適用性和穩(wěn)定性,Redis是搭建緩存系統(tǒng)的最優(yōu)選擇。以下將基于Redis介紹。
2.Redis集群緩存方案
如頂部圖1-1所示,列出了Redis的集群高可用的方案,基本可以分為三種。
2.1主從機(jī)制
常見(jiàn)的集群架構(gòu),搭建簡(jiǎn)單,主要實(shí)現(xiàn)讀寫(xiě)分離和備份,可以由Master負(fù)責(zé)讀寫(xiě),Slave負(fù)責(zé)備份。但存在故障恢復(fù)復(fù)雜、水平拓展難、寫(xiě)能力受限等問(wèn)題。結(jié)構(gòu)圖如下:
2.2哨兵機(jī)制
RedisSentinel是社區(qū)版本推出的原生高可用解決方案。由一或多個(gè)哨兵實(shí)例監(jiān)視任意個(gè)主從服務(wù)器,且在Master宕機(jī)時(shí),自動(dòng)將宕機(jī)服務(wù)器屬下的Slave服務(wù)器升級(jí)為主服務(wù)器,從而保證系統(tǒng)的可用性。較主從實(shí)現(xiàn)的監(jiān)控、選主。但問(wèn)題主要是要保證Master的HA切換。結(jié)構(gòu)圖如下:
2.3“分布式”
到這里以上兩種機(jī)制其實(shí)只能算作“集群”,并非嚴(yán)格意義上的“分布式”。接著來(lái)看看分布式方案。
集群強(qiáng)調(diào)高可用,分布式在集群的基礎(chǔ)上又強(qiáng)調(diào)協(xié)作。
3.Redis分布式緩存方案
任何分布式存儲(chǔ)系統(tǒng),首先面臨的就是sharding(分片)問(wèn)題,如頂部圖1-1所示該問(wèn)題有為三種解決方法。
3.1客戶(hù)端分片
顧名思義,將數(shù)據(jù)分片的路由功能交給客戶(hù)端,但這是一種靜態(tài)分片,維護(hù)性差?;臼遣挥杩紤]的。
3.2代理分片
通過(guò)代理分發(fā)到具體的redis實(shí)例。有兩個(gè)常用解決方案。
Twemproxy:Twitter開(kāi)源,輕量級(jí),不再維護(hù),無(wú)法平滑地?cái)U(kuò)容/縮容,運(yùn)維也不是很友好,性能一般。
Codis:豌豆莢開(kāi)源,支持水平拓展,運(yùn)維平臺(tái)完善,性能較Twemproxy快。Codis在國(guó)內(nèi)使用的較多,同時(shí)代理分片的思路也有很多公司在此基礎(chǔ)開(kāi)發(fā)了自己的二次方案。不過(guò)Codis也不再維護(hù)。
其實(shí),這兩種代理分片的方案,都是在Redis官方未推出良好的分布式方案時(shí)的產(chǎn)生的,在官方更新提供更優(yōu)策略后都不再維護(hù)。
3.3服務(wù)器端分片
這就要談到Redis官方方案Redis-cluster。
在Redis3.0之前是沒(méi)有較好的分布式方案的,這也是第三方方案出現(xiàn)的原因。3.0開(kāi)始,官方推出了去中心化的分布式方案。集群中包含16384個(gè)散列槽,每個(gè)節(jié)點(diǎn)負(fù)責(zé)其中一部分。
先看下拓?fù)鋱D:
每個(gè)節(jié)點(diǎn)打開(kāi)兩個(gè)TCP連接,一個(gè)負(fù)責(zé)給客戶(hù)端提供服務(wù),一個(gè)負(fù)責(zé)節(jié)點(diǎn)間通信。
此刻要說(shuō)說(shuō)CAP了:Consistency(一致性)、Availability(可用性)、Partitiontolerance(分區(qū)容錯(cuò)性)。對(duì)分布式系統(tǒng)而言,CAP必須犧牲一者。RedisCluster的設(shè)計(jì)目標(biāo)主要是高性能、高可用和高擴(kuò)展,只好拋棄一部分?jǐn)?shù)據(jù)一致性。
數(shù)據(jù)一致性:由于RedisCluster使用異步復(fù)制,在某些情況下如Master宕機(jī)但未同步至Slave,可能會(huì)導(dǎo)致丟失寫(xiě)入。在絕對(duì)需要支持同步寫(xiě)入時(shí),可通過(guò)WAIT命令實(shí)現(xiàn),可使得丟失寫(xiě)入的可能性大大降低。
可用性:當(dāng)集群中一部分節(jié)點(diǎn)故障后,集群整體能響應(yīng)客戶(hù)端讀寫(xiě)請(qǐng)求。
節(jié)點(diǎn)間定時(shí)互ping,當(dāng)超過(guò)一半Master判定某節(jié)點(diǎn)失敗,則標(biāo)記為FAIL,且會(huì)向集群廣播節(jié)點(diǎn)下線(xiàn)的消息。如下線(xiàn)節(jié)點(diǎn)是帶有槽的主節(jié)點(diǎn),則要從它的從節(jié)點(diǎn)選出一個(gè)替換。
高性能和拓展:操作某個(gè)key時(shí),不會(huì)先找到節(jié)點(diǎn)再處理,而是直接直接重定向到該節(jié)點(diǎn),同時(shí)相較代理分片也少了proxy的連接損耗。
但是在進(jìn)行multiplekey操作時(shí)需要keys位于同一個(gè)slot上,需要使用hashtags,使用{}強(qiáng)制將某些key映射到每個(gè)slot,以便進(jìn)行multiple。
在拓展方面,RedisCluster最大支持線(xiàn)性拓展1000個(gè)節(jié)點(diǎn),將新節(jié)點(diǎn)加入集群后可以通過(guò)命令指定和平均的從已有節(jié)點(diǎn)分配slot。
4.緩存常見(jiàn)問(wèn)題
以上介紹了簡(jiǎn)單介紹了常見(jiàn)緩存系統(tǒng),并具體列出了基于Redis的集群方案。下面談一談緩存系統(tǒng)常見(jiàn)的問(wèn)題。
如下圖所示,列出七個(gè)常見(jiàn)問(wèn)題。
4.1.緩存穿透
指訪(fǎng)問(wèn)不存在的數(shù)據(jù),從而繞過(guò)緩存,直接請(qǐng)求到了數(shù)據(jù)源,當(dāng)請(qǐng)求過(guò)多,就會(huì)對(duì)DB造成壓力。
空key:指對(duì)于不存在的數(shù)據(jù)也將key存空值入緩存系統(tǒng),這樣下次訪(fǎng)問(wèn)也會(huì)得到返回。但只適用于空數(shù)據(jù)key有限、key重復(fù)請(qǐng)求概率高,如果量大且不重復(fù),就會(huì)造成很多無(wú)用key的創(chuàng)建。
布隆過(guò)濾器:布隆過(guò)濾器是一個(gè)很長(zhǎng)的二進(jìn)制向量和一系列隨機(jī)映射函數(shù)??捎糜跈z索一個(gè)元素是否在一個(gè)集合中加一層對(duì)空值的過(guò)濾器,空間和時(shí)間效率都很高。但由于hash產(chǎn)生的碰撞可能存在誤判,以及因不存儲(chǔ)key導(dǎo)致的無(wú)法刪除。適用于空數(shù)據(jù)key各不同、重復(fù)請(qǐng)求概率低。
4.2.緩存擊穿
緩存擊穿實(shí)際是緩存雪崩的一個(gè)特例。指當(dāng)某些熱點(diǎn)key過(guò)期時(shí),就會(huì)有大量的請(qǐng)求擊穿到DB。
互斥鎖:在緩存失效的時(shí)候,不立即loaddb,可以先用如SETNX等命令去set一個(gè)mutexkey,當(dāng)操作返回成功時(shí),說(shuō)明拿到鎖,此刻該線(xiàn)程進(jìn)行l(wèi)oaddb的操作并更新緩存;否則未拿到鎖就(可休眠一段)重試get緩存的方法。但要注意死鎖風(fēng)險(xiǎn)。
不過(guò)期
這里的不過(guò)期有兩個(gè)概念,一個(gè)指未設(shè)過(guò)期時(shí)間,那是真的不過(guò)期,那沒(méi)事了。
另一個(gè)是指通過(guò)業(yè)務(wù)邏輯,將key的過(guò)期時(shí)間進(jìn)行存儲(chǔ),請(qǐng)求是判斷是否小于值,是則后臺(tái)異步更新。
4.3.緩存雪崩
同一時(shí)刻大量緩存失效(故障),請(qǐng)求到了DB。
隨機(jī)時(shí)間:在設(shè)置過(guò)期時(shí)間時(shí),可以在基礎(chǔ)時(shí)間上+一個(gè)隨機(jī)的時(shí)間,等于實(shí)現(xiàn)了分批過(guò)期。
后臺(tái)更新:將更新失效的工作交給后臺(tái)定時(shí)線(xiàn)程。
限流+本地緩存:如ehcache本地緩存+Hystrix限流。
雙緩存:類(lèi)似于設(shè)置主從緩存,從key不過(guò)期。
4.4.緩存更新與一致性
如果保證數(shù)據(jù)一致性。列出四種更新策略:
CacheAside:最常用的。失效時(shí)回源取數(shù)據(jù),更新;命中時(shí),返回緩存數(shù)據(jù);更新時(shí)先數(shù)據(jù)源更新,再更新緩存。
WriteBack:更新數(shù)據(jù)時(shí),只更新緩存,不更新數(shù)據(jù)源。緩存異步批量更新數(shù)據(jù)庫(kù)。
Read/WriteThrough
WriteThrough:當(dāng)有數(shù)據(jù)更新時(shí),如未命中緩存,直接更新數(shù)據(jù)庫(kù),并返回。如命中緩存,則更新緩存,再由Cache自己更新數(shù)據(jù)庫(kù)。
ReadThrough:更新數(shù)據(jù)源由緩存系統(tǒng)操作,讀取數(shù)據(jù)時(shí)如緩存失效,則取回源數(shù)據(jù)更新緩存。
4.5.熱點(diǎn)數(shù)據(jù)
對(duì)于熱點(diǎn)數(shù)據(jù)的處理方法。
拆分復(fù)雜結(jié)構(gòu):如二級(jí)數(shù)據(jù)結(jié)構(gòu),進(jìn)行拆分,這樣熱點(diǎn)key就被拆為若干個(gè)的key分布到不同節(jié)點(diǎn)。
遷移熱點(diǎn):對(duì)于RedisCluster而言可以將熱點(diǎn)key所在的slot單獨(dú)遷移到一個(gè)節(jié)點(diǎn),降低其他節(jié)點(diǎn)壓力。
多副本:復(fù)制多份緩存副本,將請(qǐng)求分散到多個(gè)節(jié)點(diǎn)上,減輕單臺(tái)緩存服務(wù)器壓力,適合多讀少寫(xiě)。
4.6.緩存預(yù)熱
指可以將某些的緩存數(shù)據(jù)提前加載到緩存系統(tǒng),提前避免在如熱點(diǎn)數(shù)據(jù)大量請(qǐng)求到庫(kù)。
4.7.緩存降級(jí)
指當(dāng)訪(fǎng)問(wèn)量劇增、服務(wù)出現(xiàn)問(wèn)題或非核心服務(wù)影響到核心流程的性能時(shí),仍需保證主服務(wù)可用。可根據(jù)一些關(guān)鍵數(shù)據(jù)自動(dòng)降級(jí),也可配置開(kāi)關(guān)人工降級(jí)。
5.RedisCluster使用
對(duì)于RedisCluster環(huán)境的搭建和基礎(chǔ)使用非常簡(jiǎn)單。
無(wú)論基于何種方式,只要搭建好n臺(tái)redis服務(wù)并保證各服務(wù)間可以互相通訊后,任意進(jìn)入一個(gè)redis服務(wù)鍵入:
redis-cli--clustercreateIP1:port1IP2:port2IP3:port3IP4:port4IP5:port5IP6:port6。。。--cluster-replicas1即可。之后可以使用clusternode和clusterinfo命令查看集群、節(jié)點(diǎn)信息。
而對(duì)于廣大JAVA開(kāi)發(fā),SpringDataRedis從1.7起即支持RedisCluster,只需配置Master節(jié)點(diǎn)地址(和密碼)。
spring.redis.cluster.nodes=ip1:port1,ip2:port2,ip3:port3
加入依賴(lài)
compile(“org.springframework.boot:spring-boot-starter-data-redis”)即可通過(guò)RedisTemplate使用。
6.總結(jié)
本文從緩存系統(tǒng)的選擇出發(fā),基于Redis介紹了幾種集群方案并重點(diǎn)說(shuō)明了RedisCluster方案。之后列出緩存系統(tǒng)常見(jiàn)問(wèn)題及常見(jiàn)解決方案,最后對(duì)使用做了簡(jiǎn)單說(shuō)明。
當(dāng)然,如何去落地,如何解決這些問(wèn)題還需要根據(jù)實(shí)際場(chǎng)景具體分析和處理。
責(zé)任編輯人:CC
評(píng)論
查看更多