redis無法像mysql敢艰、mongodb那樣基于同步的點(diǎn)位在主庫(kù)發(fā)生變化后從新的主庫(kù)繼續(xù)同步數(shù)據(jù)诬乞。 在redis集群中一旦從庫(kù)換主,redis的做法是將更換主庫(kù)的從庫(kù)清空然后從新主庫(kù)完整同步一份數(shù)據(jù)再進(jìn)行續(xù)傳钠导。
整個(gè)從庫(kù)重做流程是這樣的:
1震嫉,主庫(kù)bgsave自身數(shù)據(jù)到磁盤
2,主庫(kù)發(fā)送rdb文件到從庫(kù)
3牡属,從庫(kù)開始加載
4票堵,加載完畢開始續(xù)傳,同時(shí)開始提供服務(wù)
在這個(gè)過程中redis的內(nèi)存體積越大以上每一個(gè)步驟的時(shí)間都會(huì)被拉長(zhǎng)逮栅。為什么Redis內(nèi)存不宜過大
所以當(dāng)redis實(shí)例過大時(shí)悴势,我們可以嘗試采取分片集群,就是指啟動(dòng)多個(gè) Redis 實(shí)例組成一個(gè)集群措伐,然后按照一定的規(guī)則特纤,把收到的數(shù)據(jù)劃分成多份,每一份用一個(gè)實(shí)例來保存侥加。
Redis 應(yīng)對(duì)數(shù)據(jù)量增多的兩種方案:縱向擴(kuò)展和橫向擴(kuò)展捧存。
縱向擴(kuò)展:升級(jí)單個(gè) Redis 實(shí)例的資源配置,包括增加內(nèi)存容量担败、增加磁盤容量昔穴、使用更高配置的 CPU。就像下圖中提前,原來的實(shí)例內(nèi)存是 8GB吗货,硬盤是 50GB,縱向擴(kuò)展后狈网,內(nèi)存增加到 24GB宙搬,磁盤增加到 150GB。(不要求持久化保存 Redis 數(shù)據(jù)拓哺,是個(gè)不錯(cuò)的選擇害淤,不持久化的話,對(duì)于宕機(jī)后數(shù)據(jù)穿透服務(wù)器的壓力需重點(diǎn)關(guān)注下)
橫向擴(kuò)展:橫向增加當(dāng)前 Redis 實(shí)例的個(gè)數(shù) (切片)
Redis Cluster
在切片集群中拓售,數(shù)據(jù)需要分布在不同實(shí)例上,這個(gè)數(shù)據(jù)和實(shí)例的對(duì)應(yīng)關(guān)系镶奉,官方采用Redis Cluster解決础淤;
在 Redis Cluster 方案中崭放,一個(gè)切片集群共有 16384 個(gè)哈希槽,這些哈希槽類似于數(shù)據(jù)分區(qū)鸽凶,每個(gè)鍵值對(duì)都會(huì)根據(jù)它的 key币砂,被映射到一個(gè)哈希槽中。
Redis 會(huì)自動(dòng)把這些槽平均分布在集群實(shí)例上玻侥。例如决摧,如果集群中有 N 個(gè)實(shí)例,那么凑兰,每個(gè)實(shí)例上的槽個(gè)數(shù)為 16384/N 個(gè)掌桩。
hash槽可自動(dòng)分配,也可以根據(jù)實(shí)例的配置手動(dòng)分配姑食,配置較高的實(shí)例可分配更多的槽位以抵抗較大的讀寫壓力波岛。
redis的16個(gè)數(shù)據(jù)庫(kù)數(shù)量是默認(rèn)的,1個(gè)數(shù)據(jù)庫(kù)的地位相當(dāng)于mysql的一個(gè)表音半,可以理解為一個(gè)命名空間则拷。當(dāng)集群模式時(shí),只有一個(gè)db0的概念曹鸠,數(shù)據(jù)按照slot放置煌茬。
redis集群整體不可以支持事務(wù),每個(gè)節(jié)點(diǎn)可以彻桃。
客戶端如何定位數(shù)據(jù)?
在定位鍵值對(duì)數(shù)據(jù)時(shí)坛善,它所處的哈希槽是可以通過計(jì)算得到的,這個(gè)計(jì)算可以在客戶端發(fā)送請(qǐng)求時(shí)來執(zhí)行叛薯。但是浑吟,要進(jìn)一步定位到實(shí)例,還需要知道哈希槽分布在哪個(gè)實(shí)例上(即為查找的時(shí)候如果數(shù)據(jù)不在當(dāng)前節(jié)點(diǎn)下耗溜,會(huì)先給予數(shù)據(jù)所在的實(shí)例地址)组力。
實(shí)例如果知道數(shù)據(jù)分布在哪個(gè)實(shí)例地址
Redis 實(shí)例會(huì)把自己的哈希槽信息發(fā)給和它相連接的其它實(shí)例,來完成哈希槽分配信息的擴(kuò)散抖拴。當(dāng)實(shí)例之間相互連接后燎字,每個(gè)實(shí)例就有所有哈希槽的映射關(guān)系了。
客戶端收到哈希槽信息后阿宅,會(huì)把哈希槽信息緩存在本地候衍。當(dāng)客戶端請(qǐng)求鍵值對(duì)時(shí),會(huì)先計(jì)算鍵所對(duì)應(yīng)的哈希槽洒放,然后就可以給相應(yīng)的實(shí)例發(fā)送請(qǐng)求了蛉鹿。
但是,在集群中往湿,實(shí)例和哈希槽的對(duì)應(yīng)關(guān)系并不是一成不變的妖异,最常見的變化有兩個(gè):
在集群中惋戏,實(shí)例有新增或刪除,Redis 需要重新分配哈希槽他膳;
為了負(fù)載均衡响逢,Redis 需要把哈希槽在所有實(shí)例上重新分布一遍。
實(shí)例之間還可以通過相互傳遞消息棕孙,獲得最新的哈希槽分配信息舔亭,但是,客戶端是無法主動(dòng)感知這些變化的怎么辦蟀俊?
Redis Cluster 方案提供了一種重定向機(jī)制钦铺,所謂的“重定向”,就是指欧漱,客戶端給一個(gè)實(shí)例發(fā)送數(shù)據(jù)讀寫操作時(shí)职抡,這個(gè)實(shí)例上并沒有相應(yīng)的數(shù)據(jù),客戶端要再給一個(gè)新實(shí)例發(fā)送操作命令误甚。
GET hello:key
(error) MOVED 13320 172.16.19.5:6379
就是如果在客戶端此時(shí)請(qǐng)求哈希槽2的數(shù)據(jù)缚甩,此時(shí)會(huì)到實(shí)例2,發(fā)現(xiàn)數(shù)據(jù)不在該實(shí)例下窑邦,會(huì)讓你重定向?qū)嵗?擅威,并更新客戶端緩存。
哈希槽數(shù)據(jù)存在兩個(gè)實(shí)例怎么辦冈钦?
如果 Slot 2 中的數(shù)據(jù)比較多郊丛,就可能會(huì)出現(xiàn)一種情況:客戶端向?qū)嵗?2 發(fā)送請(qǐng)求,但此時(shí)瞧筛,Slot 2 中的數(shù)據(jù)只有一部分遷移到了實(shí)例 3厉熟,還有部分?jǐn)?shù)據(jù)沒有遷移。在這種遷移部分完成的情況下较幌,客戶端就會(huì)收到一條 ASK 報(bào)錯(cuò)信息揍瑟。
GET hello:key
(error) ASK 13320 172.16.19.5:6379
此時(shí),客戶端需要先給 172.16.19.5 這個(gè)實(shí)例發(fā)送一個(gè) ASKING 命令乍炉。這個(gè)命令的意思是绢片,讓這個(gè)實(shí)例允許執(zhí)行客戶端接下來發(fā)送的命令。然后岛琼,客戶端再向這個(gè)實(shí)例發(fā)送 GET 命令底循,以讀取數(shù)據(jù)。
和 MOVED 命令不同槐瑞,ASK 命令并不會(huì)更新客戶端緩存的哈希槽分配信息熙涤。所以,在上圖中,如果客戶端再次請(qǐng)求 Slot 2 中的數(shù)據(jù)灭袁,它還是會(huì)給實(shí)例 2 發(fā)送請(qǐng)求猬错。這也就是說,ASK 命令的作用只是讓客戶端能給新實(shí)例發(fā)送一次請(qǐng)求茸歧,而不像 MOVED 命令那樣,會(huì)更改本地緩存显沈,讓后續(xù)所有命令都發(fā)往新實(shí)例软瞎。
Redis Cluster不采用把key直接映射到實(shí)例的方式,而采用哈希槽的方式原因:
1拉讯、整個(gè)集群存儲(chǔ)key的數(shù)量是無法預(yù)估的涤浇,key的數(shù)量非常多時(shí),直接記錄每個(gè)key對(duì)應(yīng)的實(shí)例映射關(guān)系魔慷,這個(gè)映射表會(huì)非常龐大只锭,這個(gè)映射表無論是存儲(chǔ)在服務(wù)端還是客戶端都占用了非常大的內(nèi)存空間。
2院尔、Redis Cluster采用無中心化的模式(無proxy蜻展,客戶端與服務(wù)端直連),客戶端在某個(gè)節(jié)點(diǎn)訪問一個(gè)key邀摆,如果這個(gè)key不在這個(gè)節(jié)點(diǎn)上纵顾,這個(gè)節(jié)點(diǎn)需要有糾正客戶端路由到正確節(jié)點(diǎn)的能力(MOVED響應(yīng)),這就需要節(jié)點(diǎn)之間互相交換路由表栋盹,每個(gè)節(jié)點(diǎn)擁有整個(gè)集群完整的路由關(guān)系施逾。如果存儲(chǔ)的都是key與實(shí)例的對(duì)應(yīng)關(guān)系,節(jié)點(diǎn)之間交換信息也會(huì)變得非常龐大例获,消耗過多的網(wǎng)絡(luò)資源汉额,而且就算交換完成,相當(dāng)于每個(gè)節(jié)點(diǎn)都需要額外存儲(chǔ)其他節(jié)點(diǎn)的路由表榨汤,內(nèi)存占用過大造成資源浪費(fèi)蠕搜。
3、當(dāng)集群在擴(kuò)容件余、縮容讥脐、數(shù)據(jù)均衡時(shí),節(jié)點(diǎn)之間會(huì)發(fā)生數(shù)據(jù)遷移啼器,遷移時(shí)需要修改每個(gè)key的映射關(guān)系旬渠,維護(hù)成本高。
4端壳、而在中間增加一層哈希槽告丢,可以把數(shù)據(jù)和節(jié)點(diǎn)解耦,key通過Hash計(jì)算损谦,只需要關(guān)心映射到了哪個(gè)哈希槽岖免,然后再通過哈希槽和節(jié)點(diǎn)的映射表找到節(jié)點(diǎn)岳颇,相當(dāng)于消耗了很少的CPU資源,不但讓數(shù)據(jù)分布更均勻颅湘,還可以讓這個(gè)映射表變得很小话侧,利于客戶端和服務(wù)端保存,節(jié)點(diǎn)之間交換信息時(shí)也變得輕量闯参。
5瞻鹏、當(dāng)集群在擴(kuò)容、縮容鹿寨、數(shù)據(jù)均衡時(shí)新博,節(jié)點(diǎn)之間的操作例如數(shù)據(jù)遷移,都以哈希槽為基本單位進(jìn)行操作脚草,簡(jiǎn)化了節(jié)點(diǎn)擴(kuò)容赫悄、縮容的難度,便于集群的維護(hù)和管理馏慨。
Redis Cluster不采用一致性哈希算法的原因
一致性哈希算法:多增加一層虛擬映射層埂淮,數(shù)據(jù)與虛擬節(jié)點(diǎn)映射、虛擬節(jié)點(diǎn)與真實(shí)節(jié)點(diǎn)再映射熏纯。聽起來和哈希槽很像同诫。
但一致性哈希的ketama算法實(shí)現(xiàn)在擴(kuò)容或down的情況下,需要重新計(jì)算節(jié)點(diǎn)樟澜,這對(duì)之前的分配可能會(huì)有一些影響议双。所以可以引入hash slot的方式伴郁,即某些hash slot區(qū)間對(duì)應(yīng)一臺(tái)機(jī)器,對(duì)于擴(kuò)容或down機(jī)情況下,就改變某個(gè)hash slot區(qū)間就可以了稳其,改動(dòng)比較小皂股,對(duì)之前分配的影響也較小界弧。
虛擬桶是取模和一致性哈希二者的折中辦法伟阔。
采用固定節(jié)點(diǎn)數(shù)量,來避免取模的不靈活性觅玻。
采用可配置映射節(jié)點(diǎn)想际,來避免一致性哈希的部分影響。
一致性哈希算法原理詳解
Hash Tag
Hash Tag 是指加在鍵值對(duì) key 中的一對(duì)花括號(hào){}
這對(duì)括號(hào)會(huì)把 key 的一部分括起來溪厘,客戶端在計(jì)算 key 的 CRC16 值時(shí)胡本,只對(duì) Hash Tag 花括號(hào)中的 key 內(nèi)容進(jìn)行計(jì)算。如果沒用 Hash Tag 的話畸悬,客戶端計(jì)算整個(gè) key 的 CRC16 的值侧甫。
Hash Tag主要是用在 Redis Cluster中,集群本身并不支持跨實(shí)例的事務(wù)操作和范圍查詢。Hash Tag可以避開這個(gè)缺點(diǎn)披粟。不過要小心數(shù)據(jù)傾斜導(dǎo)致實(shí)例不穩(wěn)定咒锻。
摘抄:《Redis 核心技術(shù)與實(shí)戰(zhàn)》-第9節(jié)