文章目錄
-
-
-
- 1、為什麼需要用redis叢集
- 2、主從複制
-
- a、主從複制配置
- 3、可用性保證之哨兵機制(Sentinel)
- 4、redis分布式方案
-
- a、用戶端Sharding
- b、代理Proxy之Twemproxy
- c、代理Proxy之Codis
- d、Redis Cluster
- e、分片方案總結
-
-
1、為什麼需要用redis叢集
老套路,性能、擴充、可用性。下面幾個說法就當故事聽聽了。
性能
- 第一個是因為Redis 本身的QPS已經很高了,但是如果在一些缤紛咖喱昂非常高的情況下,性能還是會受到影響。這個時候我們希望有更多的redis服務來完成工作。
擴充
- 第二個是出于存儲的考慮。因為redis所有的資料都放在記憶體中,如果資料量大,很容易受到硬體的限制。更新硬體收效和成本比太低,是以我們需要有一種橫向擴充的方法。
可用性
- 第三個是可用性和安全的問題了。如果隻有一個redis服務,一旦服務當機,那麼所有的用戶端都無法通路,會對業務造成很大的影響。另一個,如果硬體發生故障,而單機的資料無法恢複的話,帶來的影響也是災難性的。
可用性、資料安全、性能…。。。。是在編寫不下去了。。。其實就幾句話
- 一台性能有限、是以隻能擴充多台,擴充多台,就涉及到分布式叢集問題,自然就延伸出cap理論了。就這麼簡單。真的是個人都喜歡去改個詞 弄的千奇百怪。。哎。。
2、主從複制
a、主從複制配置
隻是針對主從模式下,例如加入了sentinel 的主從關系。沒有這樣的中間件幫忙。自身切換很麻煩。
redis -cluster 叢集模式不能使用主從複制相關指令
- 會報錯:(error) ERR REPLICAOF not allowed in cluster mode.
配置方法
- 一種:在每個slave節點的redis.conf配置檔案增加一行
-
slaveof 192.168.20.202 6379
-
- 二種:啟動時候通過啟動參數指定master節點
-
redis-server --slaveof 192.168.20.202 6379
-
切換變化
- 主從切換
-
這樣自己就變成從了slaveof xx xx
-
- 主從切換的時候,這個配置會被重寫成
-
# slaveof 192.168.20.202 6379 # Generated by CONFIG REWRITE replicaof 192.168.20.202 6379
-
- 檢視叢集狀态
-
info replication
-
- 斷開主從複制
-
此刻就斷開聯系了。不再複制資料slaveof no one
-
3、可用性保證之哨兵機制(Sentinel)
就是在主庫故障了。重新選舉主節點 ; 分别是斷開連接配接時長、優先級排序、複制數量、程序id 來決定選舉結果。Sentinel是沒有主從隻分,但是當redis叢集master挂了,會采用Ratf算法選擇一個Sentinel臨時主節點去做主從切換
《快速搭建一主二從Sentinel監控配置》
功能描述:
- 監控:Sentinel 會不斷檢查主伺服器和從伺服器是否正常運作
- 通知:如果某一個被監控的執行個體出現問題,Sentinel可以通過API發出通知
- 自動故障轉移(failover):如果主伺服器發生故障,Sentinel可以啟動故障轉移過程。把某台伺服器更新為主伺服器,并發出通知
- 配置管理:用戶端連接配接到Sentinel,擷取目前的Redis主伺服器的位址
4、redis分布式方案
所有用戶端連接配接一個proxy或者用分區架構來實作資料分片(存儲在不同redis伺服器)
a、用戶端Sharding
采用2個獨立的redis 然後用Sharding來操作測試
使用 ShardedJedis 之類的用戶端分片代碼的優勢是配置簡單,不依賴于其他中間
件,分區的邏輯可以自定義,比較靈活。但是基于用戶端的方案,不能實作動态的服務
增減,每個用戶端需要自行維護分片政策,存在重複代碼。
第二種思路就是把分片的代碼抽取出來,做成一個公共服務,所有的用戶端都連接配接
到這個代理層。由代理層來實作請求和轉發。
示例:
package org.example;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
import java.util.ArrayList;
import java.util.List;
@RunWith(JUnit4.class)
public class ShardingTest {
@Test
public void test() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
// redis伺服器
JedisShardInfo shardInfo1 = new JedisShardInfo("192.168.170.3", 1000);
JedisShardInfo shardInfo2 = new JedisShardInfo("192.168.170.3", 1001);
// 連接配接池
List<JedisShardInfo> infoList = new ArrayList<>(2);
infoList.add(shardInfo1);
infoList.add(shardInfo2);
ShardedJedisPool jedisPool = new ShardedJedisPool(poolConfig, infoList);
try (ShardedJedis shardedJedis = jedisPool.getResource()) {
for (int i = 0; i < 10000; i++) {
shardedJedis.set("k" + i, "" + i);
}
for (int i = 0; i < 10000; i++) {
shardedJedis.get("k" + i);
}
} catch (Exception e) {
throw e;
}
}
}
b、代理Proxy之Twemproxy
twitter 開源的。two-em-proxy
比較穩定,成本效益高
出現故障不能自動轉移,架構複雜,需要借助其他元件(LVS/HAproxy+Keepalived)實作HA
擴縮容需要修改配置,不能實作平滑地擴縮容(需要重新分布資料)。
c、代理Proxy之Codis
國内豌豆莢開源的Codis,是一個代理中間件,由Go語言開發
codis不支援的指令:https://github.com/CodisLabs/codis/blob/release3.2/doc/unsupported_cmds.md
分片原理:Codis把所有的key分成了N個槽(例如1024),每個槽對應一個分組,一個分組對應于一個或者一組 Redis 執行個體。Codis 對 key 進行 CRC32 運算,得到一個32位的數字,然後模以N(槽的個數),得到餘數,這個就是key對應的槽,槽後面就是Redis的執行個體。比如4個槽:
Codis的槽位映射關系是儲存在 Proxy中的,如果要解決單點的問題,Codis 也要做叢集部署, 多個Codis節點怎麼同步槽和執行個體的關系呢?需要運作一個Zookeeper (或者etcd/本地檔案)
d、Redis Cluster
3.0開始正式推出的,同時也可以實作高可用,跟Codis不一樣,它是去中心化的。用戶端可以連接配接到任意一個可用節點。
Redis既沒有用哈希取模,也沒有用一緻性哈希,而是用虛拟槽來實作的。
Redis建立了16384個槽(slot),每個節點負責一定區間的slot。比如Node1負
責0-5460,Node2負責5461-10922,Node3負責10923-16383
注意:key與slot的關系是永遠不會變的,會變的隻有slot和Redis節點的關系。
《1 秒搭建Redis-Cluster叢集》
- 檢視key屬于那個slot
cluster keyslot lilei
- 多key操作的hash tags{}
multi key 操作是不能跨節點的,是以要讓資料分布到一個節點,就可以如下:
# 這樣{} 的标志,讓他們都會落在桶一個slot裡面 set a{qs}a 1 set a{qs}b 1 set a{qs}c 1 set a{qs}d 1 set a{qs}e 1
- 用戶端重定向
在使用redis-cli時,可以加上-c參數,這樣redis會自動幫我們連接配接到正确的節點執行指令。
redis-cli -p 7292 -c
- 資料遷移
因為key 和slot的關系是永遠不會變的,當新增了節點的時候,需要把原有的slot 配置設定給新的節點服務,并且把相關的資料遷移過來。
添加新節點(新增一個7297)
新增的節點沒有哈希槽,不能分布資料,在原來的任意一個節點上執行:redis-cli--cluster add-node 127.0.0.1:7291 127.0.0.1:7297
redis-cli--cluster reshard 127.0.0.1:7291
- 高可用和主從切換原理
當slave 發現自己的master 變為fail狀态時,便嘗試進行failover,以期成為新的master。由于挂掉的master可能會有多個slave進而存在多個slave競争成為master節點的過程。
自動實作主從角色配置設定,主從自動切換
過程如下:
1、slave發現自己的master 變為fail
2、将自己記錄的叢集currentEpoch 加1,并廣播FAILOVER_AUTH_REQUEST 資訊
3、其他節點收到該資訊,隻有master響應,判斷請求者的合法性,并發送FAILOVER_AUTH_ACK,對每個epoch隻發送一次ack
4、嘗試failover的slave手機FAILOVER_AUTH_ACK
5、超過半數後變成新Master
6、廣播Pong通知其他叢集節點
e、分片方案總結
功能 | Codis | Tewmporyx | Redis Cluster |
---|---|---|---|
重新分片不需要重新開機 | Yes | NO | Yes |
pipeline | Yes | Yes | |
多key操作的hash tags{} 讓指令落在同一節點 | Yes | Yes | Yes |
重新分片時的多key操作 | Yes | - | NO |
用戶端支援 | 所有 | 所有 | 支援cluster協定的用戶端 |