redis叢集(Sharding)和線上擴容(Pre-Sharding)
redis叢集分為服務端叢集和客戶端分片,redis3.0以上版本實現了叢集機制,即服務端叢集,3.0以下使用客戶端分片(Sharding)。redis3.0服務端叢集使用雜湊槽,計算key的CRC16結果再模16834。3.0以下版本採用Key的一致性hash演算法來區分key儲存在哪個Redis例項上。
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(500);
config.setTestOnBorrow(true);
List<JedisShardInfo> jdsInfoList = new ArrayList<>(2 );
jdsInfoList.add(new JedisShardInfo("192.168.2.128", 6379));
jdsInfoList.add(new JedisShardInfo("192.168.2.108", 6379));
pool = new ShardedJedisPool(config, jdsInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);
jds.set(key, value);
......
jds.close();
pool.close();
採用這種方式也存在兩個問題
- 擴容問題:
因為使用了一致性哈稀進行分片,那麼不同的key分佈到不同的Redis-Server上,當我們需要擴容時,需要增加機器到分片列表中,這時候會使得同樣的key算出來落到跟原來不同的機器上,這樣如果要取某一個值,會出現取不到的情況,之前的快取相當於全部失效。對於擴容問題,Redis的作者提出了一種名為Pre-Sharding的方式。即事先部署足夠多的Redis服務。 - 單點故障問題:
當叢集中的某一臺服務掛掉之後,客戶端在根據一致性hash無法從這臺伺服器取資料。對於單點故障問題,我們可以使用Redis的HA高可用來實現。利用Redis-Sentinal來通知主從服務的切換。
通過presharding進行Redis線上擴容。
通過主動複製我們解決了Redis單點故障問題,那麼還有一個重要的問題需要解決:容量規劃與線上擴容問題。
我們前面分析過Redis的適用場景是全部資料儲存在記憶體中,而記憶體容量有限,那麼首先需要根據業務資料量進行初步的容量規劃,比如你的業務資料需要100G儲存空間,假設伺服器記憶體是48G,那麼根據上一篇我們討論的Redis磁碟IO的問題,我們大約需要3~4臺伺服器來儲存。這個實際是對現有業務情況所做的一個容量規劃,假如業務增長很快,很快就會發現當前的容量已經不夠了,Redis裡面儲存的資料很快就會超過實體記憶體大小,那麼如何進行Redis的線上擴容呢?
Redis的作者提出了一種叫做presharding的方案來解決動態擴容和資料分割槽的問題,實際就是在同一臺機器上部署多個Redis例項的方式,當容量不夠時將多個例項拆分到不同的機器上,這樣實際就達到了擴容的效果。Pre-Sharding方法是將每一個臺物理機上,執行多個不同埠的Redis例項,假如有三個物理機,每個物理機執行三個Redis例項,那麼我們的分片列表中實際有9個Redis例項,當我們需要擴容時,增加一臺物理機來代替9箇中的一個redis,有人說,這樣不還是9個麼,是的,但是以前伺服器上面有三個redis,壓力很大的,這樣做,相當於單獨分離出來並且將資料一起copy給新的伺服器。值得注意的是,還需要修改客戶端被代替的redis的IP和埠為現在新的伺服器,只要順序不變,不會影響一致性雜湊分片。
拆分過程如下:
- 在新機器上啟動好對應埠的Redis例項。
- 配置新埠為待遷移埠的從庫。
- 待複製完成,與主庫完成同步後,切換所有客戶端配置到新的從庫的埠。
- 配置從庫為新的主庫。
- 移除老的埠例項。
- 重複上述過程遷移好所有的埠到指定伺服器上。
以上拆分流程是Redis作者提出的一個平滑遷移的過程,不過該拆分方法還是很依賴Redis本身的複製功能的,如果主庫快照資料檔案過大,這個複製的過程也會很久,同時會給主庫帶來壓力。所以做這個拆分的過程最好選擇為業務訪問低峰時段進行。
單點故障問題:
還是用到Redis主從複製的功能,兩臺物理主機上分別都執行有Redis-Server,其中一個Redis-Server是另一個的從庫,採用雙機熱備技術,客戶端通過虛擬IP訪問主庫的物理IP,當主庫宕機時,切換到從庫的物理IP。只是事後修復主庫時,應該將之前的從庫改為主庫(使用命令slaveof no one),主庫變為其從庫(使命令slaveof IP PORT),這樣才能保證修復期間新增資料的一致性。