復(fù)習(xí)
單機(jī)有三個問題:
- 單點故障 (用主備/主從的方式,屬于 AKF 的 X 軸)
- 容量
- 壓力
關(guān)于容量問題解決的一些方案
1. 按業(yè)務(wù)分實例
客戶端處理,將不同業(yè)務(wù)訪問不同 Redis 實例。
但對于業(yè)務(wù)不可再拆分而單個業(yè)務(wù)容量需要很大時,就無法處理了。
2. 分片算法
2.1 客戶端做 hash 區(qū)分訪問那個實例
客戶端做 hash 取模算法。
但影響擴(kuò)展性。因為當(dāng)擴(kuò)展的時候,取模結(jié)果會發(fā)生變化,此時就需要將以往的所有數(shù)據(jù)進(jìn)行取模運算再分配
2.2 Random 方式
存取方便,但查不方便。
那為什么還有這種方案呢?因為消息隊列的場景可以用它,同一個 topic 下不同的 partition 可以是 random 選擇的。
2.3 一致性哈希算法
哈希運算是一種映算法。通過算法將數(shù)據(jù)實現(xiàn)映射。參考
一致性哈希算法要求 key 和 設(shè)備 都需要進(jìn)行哈希運算。
Redis 哈希槽
Redis-Cluster 自己實現(xiàn)了用哈希槽的概念,預(yù)先分為 16384 個哈希槽。然后分片的機(jī)器平均(默認(rèn))領(lǐng)取這些槽位。
Redis 集群式無主模型,即客戶端可以連任意的 Redis,而 Redis 自己通過 Mapping 關(guān)系,將數(shù)據(jù)存在對于節(jié)點中。
分片的問題:整合
整合是常常用到的運算,比如 accountId:actionId:key1
和 accountId:actionId:key2
就可能常常需要在一起運算。
由于分片,所以數(shù)據(jù)分?jǐn)傇诓煌钠希窃谡系臅r候,還需要數(shù)據(jù)移動。這樣是很影響效率的。
Redis 的解決策略是提供了 Hash Tag 的概念,讓開發(fā)者自己去選擇。如上方可以將 accountId
指定為 Hash Tag,Redis 只對 Hash Tag 進(jìn)行運算,找到片的位置,這樣就可以將有同樣 Tag 的數(shù)據(jù)放在同一片中。
操作實例
- 操作下 twitter 的
twemproxy
代理。不支持事務(wù) - 操作下
predixy
代理。只支持單 group 的事務(wù)。 - 操作下
redis-cluster
(用redis-cli --cluster
連接)。支持事務(wù),但需要{hashTag}key
才能成功。reshard
解決數(shù)據(jù)傾斜。
文檔舊的部分
AKF 拆分
由來,Redis DB 存全量的數(shù)據(jù)很耗內(nèi)存,解決辦法:
- 硬件括展內(nèi)存,但不現(xiàn)實。
- 根據(jù)業(yè)務(wù)多實例劃分。但會有數(shù)據(jù)傾斜的問題:某部分?jǐn)?shù)據(jù)量太大。
- AKF 拆分原則(是 微服務(wù)設(shè)計的四個原則[參考] 之一)
AKF 的拆分最主要的是如何按 key 拆分到哪一個 redis 中:拆分算法。
AKF 拆分算法最主要的問題:
- 分片問題:增加物理節(jié)點如何去做。(如模 2 和模 3 結(jié)果不同)
- 負(fù)載問題:不需要代理層后怎么做。
對于分片,Redis 的做法是提供了個槽位的概念,當(dāng)你未來增加物理節(jié)點,直接將槽位遷移過去。
對于負(fù)載,Redis 每個服務(wù)加個代理,
擴(kuò)展:
- HBase 預(yù)分區(qū)實現(xiàn),將數(shù)據(jù)存在分區(qū)上,當(dāng)數(shù)據(jù)變大增加節(jié)點后,直接將某一部分區(qū)移動到新節(jié)點
- ElasticSearch 分片的實現(xiàn),即便公司只有一點點數(shù)據(jù)量,但還是推薦你用高倍數(shù)的數(shù)據(jù)量壓測它,多幾個分片,日后增加節(jié)點便可以方便擴(kuò)容。
- 無主模型(負(fù)載問題):就像 Redis 這種實現(xiàn),ElasticSearch 也一樣,在使用環(huán)節(jié)是無主的(不分主從,連哪一個都一樣),服務(wù)自己做映射找到正確的節(jié)點。
分布式鎖
場景:并發(fā)下想操作某一個資源,Redis 來創(chuàng)建一個 key 做為鎖,誰拿到了這個鎖,誰就能操作它。
問題:
- 在上鎖狀態(tài)中,若使用者掛了,不釋放鎖,若不釋放鎖,則造成死鎖。可以用 Redis 過期時間來解決。
- 在上鎖狀態(tài)中,若Redis掛了而后又恢復(fù)了,中間可能鎖丟失了,導(dǎo)致另一個服務(wù)拿到了這個鎖,這里變成了兩個鎖。可以開啟 Redis 的 AOF 持持久化來解決,要每操作級別的。(但這里 Redis 的性能已經(jīng)退化到 MySQL 的性能了)。另外還可以開啟 Redis 主從強(qiáng)一致,但這樣會破壞可用性。
想法:用 RedLock 算法,這是一個算法,下面是實現(xiàn)邏輯。
- 多個 Redis 鎖,誰搶到過半的鎖,誰就有權(quán)限。問題:3 個服務(wù)器搶 3 個 Redis,造成都搶不到。
- 增加銷毀功能,沒搶到過半的鎖時立即銷毀自己的鎖,重新?lián)尅?梢詫崿F(xiàn),但搶鎖的邏輯是在客戶端實現(xiàn)的算法。
所以問題的關(guān)鍵是在于并發(fā),有并發(fā)就會有很惡心的事情發(fā)生。比如,RedLock 中,如果各服務(wù)搶到鎖后設(shè)定的過期時間不同步,都設(shè)定 30 秒過期,但一個 20 秒就會過期,一個 10 秒就會過期。。。所有問題都想到了,邏輯也變得更龐大了。
解決方式:串行化。
zookeeper 在并發(fā)時是串行化的。它在分布式時,有一個 Leader,規(guī)定 增/刪/改 操作一定要在 Leader 中按順序處理。即集群模式下,串行化處理。
為保證讀時也是最新的,可以要求同步,即達(dá)到強(qiáng)一致。
(zookeeper 也是內(nèi)存型DB,它是做分布式協(xié)調(diào)的,在設(shè)計時就考慮到集群,在集群中它們是可以互相通信。另外它還有回調(diào)的功能,在搶鎖過程中回復(fù)客戶端可以搶了。而 Redis 是需要客戶端主動做。)
(圖片表述有誤,客戶端寫可以連到 Follower,但會轉(zhuǎn)移到 leader;leader 也可以讀)