Redis Cluster 叢集搭建與擴容、縮容

周星星、同學發表於2022-03-27

說明:仍然是偽叢集,所有的Redis節點,都在一個伺服器上,採用不同配置檔案,不同埠的形式實現

前提:已經安裝好了Redis,本文的redis的版本是redis-6.2.3

Redis的下載、安裝參考:https://www.cnblogs.com/rxx1005/p/15754565.html

文章中,Redis的安裝目錄為:/opt/app/redis/cluster/redis-6.2.3

本文使用到的工具:

前言

redis cluster 的兩種部署方式:

  • 方法1:自定義配置檔案安裝,下文中的第一種,推薦使用此種辦法
  • 方法2:使用redis自帶的cluster-create工具:不推薦使用,玩玩就行了

準備工作

將常用指令碼複製到bin目錄下

此步驟是個人習慣,可以不復制,後面使用命令時,直接使用src目錄的即可。

在編譯Redis之後,會在 redis的src目錄下生成一些指令碼

image

我把這些常用指令碼,使用 cp 命令,拷貝到 resdis 根目錄下的 bin 目錄下(bin目錄自己建立的)

image


安裝ruby(redis5.0之後不需要安裝)

此處可跳過!!! 我使用的是redis-6.2.3,建立叢集的時候,不再使用redis-trib.rb,而是用 redis-cli --cluster ,使用redis-cli
--cluster建立叢集,不需要安裝ruby,下面是安裝ruby的辦法。

注意:不要直接在Linux上執行 yum install ruby ,因為這樣安裝的ruby版本太低了

低版本redis搭建 cluster叢集,需要用到redissrc目錄下的 redis-trib.rb ,此指令碼需要依賴 ruby 語言,現安裝 ruby,在Linux上,直接使用wget
命令,下載ruby安裝包,也可以去官網下載之後,上傳到Linux,官網:http://www.ruby-lang.org/en/downloads/

[root@localhost conf]# wget https://cache.ruby-lang.org/pub/ruby/3.1/ruby-3.1.1.tar.gz

解壓,編譯,編譯約1~2分鐘

[root@localhost app]# tar -zxvf ruby-3.1.1.tar.gz
[root@localhost ruby-3.1.1]# cd ruby-3.1.1
[root@localhost ruby-3.1.1]# ./configure --prefix=/usr/local/ruby --enable-shared
[root@localhost ruby-3.1.1]# make & make install

新增環境變數

編輯 /etc/profile 檔案,在檔案末尾加上以下內容

export RUBY_HOME=/usr/local/ruby
export PATH=$RUBY_HOME/bin:$RUBY_HOME/lib:$PATH

儲存,退出,重新載入檔案

[root@localhost ~]# source /etc/profile

執行 ruby -v,如果出現版本號,說明安裝成功

image

安裝redis庫

[root@localhost ruby-3.1.1]# gem install redis

image

至此,ruby就安裝好了。


自定義配置檔案叢集搭建

配置

在redis根目錄下,建立一個conf資料夾,並在conf資料夾下,建立6個資料夾,用於存叢集每個節點的配置檔案、資料檔案等

[root@localhost redis-6.2.3]# mkdir conf
[root@localhost redis-6.2.3]# cd conf/
[root@localhost conf]# mkdir 7001 7002 7003 7004 7005 7006

在 7001 資料夾內,建立一個redis 的配置檔案:

[root@localhost conf]# cd 7001/
[root@localhost 7001]# touch redis.conf

在 redis.conf 配置檔案內,加入以下配置:

# 將bind這一行註釋掉,或者修改為0:0:0:0,這表示任意地址都可以連線此Redis服務 
# bind 127.0.0.1 

# 關閉保護模式,如果開啟的話,外部客戶端就連不上Redis 
protected-mode no 

# 配置redis的埠號(不同節點使用不同的埠號)
port 7001 

# 以守護程式執行(後臺執行redis) 
daemonize yes 

# 服務啟動後記錄執行緒號的檔案
pidfile "redis.pid" 

# 日誌
logfile "/opt/app/redis/cluster/redis-6.2.3/conf/7001/log.log" 

# 資料庫的個數 
databases 16 

# 設定資料儲存到資料檔案中的save規則,3600秒內修改1次key,進行一次磁碟儲存操作 
save 3600 1 
save 300 100
save 60 10000 

# 指定儲存至本地資料庫時是否壓縮資料,預設是yes,redis採用LZF壓縮,需要消耗CPU資源 
rdbcompression yes 

# 儲存rdb檔案時,是否對rdb檔案進行校驗
rdbchecksum yes 

# 儲存資料的檔名字 
dbfilename "dump.rdb"

# 儲存資料的目錄,這個目錄需要提前建立出來
dir "/opt/app/redis/cluster/redis-6.2.3/conf/7001" 

# 是否開啟aof持久化
appendonly yes 

# aof檔名字 
appendfilename "appendonly.aof" 

# 叢集配置檔案,自動生成,不能人為維護 
cluster-config-file "nodes.conf"

 #開啟cluster叢集
cluster-enabled yes

 #Redis叢集節點超時時限
cluster-node-timeout 15000

將這個配置檔案,複製5份到 7002 7003 7004 7005 7006 目錄下

[root@localhost 7001]# cp redis.conf ../7002/
[root@localhost 7001]# cp redis.conf ../7003/
[root@localhost 7001]# cp redis.conf ../7004/
[root@localhost 7001]# cp redis.conf ../7005/
[root@localhost 7001]# cp redis.conf ../7006/

效果如下:

image

將每個目錄下的配置檔案裡面,所有的7001,改成和目錄一樣的數字(替換所有配置檔案中的7001),要替換三個位置。

啟動 redis 服務

[root@localhost conf]# pwd
/opt/app/redis/cluster/redis-6.2.3/conf
[root@localhost conf]# ../bin/redis-server 7001/redis.conf
[root@localhost conf]# ../bin/redis-server 7002/redis.conf
[root@localhost conf]# ../bin/redis-server 7003/redis.conf
[root@localhost conf]# ../bin/redis-server 7004/redis.conf
[root@localhost conf]# ../bin/redis-server 7005/redis.conf
[root@localhost conf]# ../bin/redis-server 7006/redis.conf

image

建立叢集

使用 redis-cli --cluster 搭建叢集,注意下面的IP,建議使用具體的IP,不要使用127.0.0.1,防止有坑

[root@localhost redis-6.2.3]# pwd
/opt/app/redis/cluster/redis-6.2.3
[root@localhost redis-6.2.3]# ./bin/redis-cli  --cluster create 192.168.3.100:7001  192.168.3.100:7002  192.168.3.100:7003  192.168.3.100:7004  192.168.3.100:7005  192.168.3.100:7006  --cluster-replicas  1

image

測試叢集

至此,叢集就搭建好了,可以測試一下,連線叢集,注意下面的命令,一定要帶上 -c ,表示以叢集的模式訪問:

[root@localhost redis-6.2.3]# ./bin/redis-cli -c -p 7001

image

如果叢集中,其中一個節點掛掉,從節點會自動變為主節點,若原主節點重連,會自動變為從節點

image

關閉叢集辦法:

[root@localhost bin]# pwd
/opt/app/redis/cluster/redis-6.2.3/bin
[root@localhost bin]# ./redis-cli -c -p 7001 shutdown
[root@localhost bin]# ./redis-cli -c -p 7002 shutdown
[root@localhost bin]# ./redis-cli -c -p 7003 shutdown
[root@localhost bin]# ./redis-cli -c -p 7004 shutdown
[root@localhost bin]# ./redis-cli -c -p 7005 shutdown
[root@localhost bin]# ./redis-cli -c -p 7006 shutdown
# 如果需要刪除叢集資料,看清楚當前位置
[root@localhost 7001]# pwd
/opt/app/redis/cluster/redis-6.2.3/conf/7001
[root@localhost 7001]# rm -rf appendonly.aof dump.rdb log.log nodes.conf
[root@localhost 7001]# cd ../7002/ ; rm -rf appendonly.aof dump.rdb log.log nodes.conf
[root@localhost 7002]# cd ../7003/ ; rm -rf appendonly.aof dump.rdb log.log nodes.conf
[root@localhost 7003]# cd ../7004/ ; rm -rf appendonly.aof dump.rdb log.log nodes.conf
[root@localhost 7004]# cd ../7005/ ; rm -rf appendonly.aof dump.rdb log.log nodes.conf
[root@localhost 7005]# cd ../7006/ ; rm -rf appendonly.aof dump.rdb log.log nodes.conf

PS:一開始,我用redis-trib.rb搭建叢集,但報出警告說,redis-trib.rb已經不可用了,讓使用redis-cli --cluster代替,下圖的示例後面的命令,是已經幫助我替換好的命令
image

使用 cluster-create 建立叢集

建議這塊只做瞭解,實踐中,使用上一種建立方式,其實這塊建立叢集,使用的仍然是redis-cli --cluster建立

指令碼位置

在redis的/redis-6.2.3/utils/create-cluster目錄下,有一個create-cluster指令碼,可以使用此指令碼建立叢集

image

指令碼配置

先編輯一下這個指令碼,開啟之後,可能需要修改前面幾行配置

image

注意:叢集節點的數量,必須大於6個,否則啟動叢集,會有如下錯誤:
image

啟動叢集

啟動redis服務,建立叢集

[root@localhost create-cluster]# ./create-cluster start
Starting 30001
Starting 30002
Starting 30003
Starting 30004
Starting 30005
Starting 30006
[root@localhost create-cluster]# ./create-cluster create

image

啟動之後,會在create-cluster指令碼的位置,自動生成配置檔案、資料檔案、log檔案等

image

測試叢集

仍然使用 redis-cli -c

[root@localhost redis-6.2.3]# ./bin/redis-cli -c -p 30001

至此,叢集搭建結束。


叢集擴容

叢集擴容時,先增加主節點,再給新增的主節點分配槽,然後增加從節點

在第一種叢集的基礎上,再增加兩個節點,7007和7008,7007作為新增的主節點,7008作為7007的從節點。

新增配置

把7001的配置檔案,複製給7007和7008,並把配置檔案裡面的7001,全部替換成7007和7008。

[root@localhost conf]# pwd
/opt/app/redis/cluster/redis-6.2.3/conf
[root@localhost conf]# mkdir 7007 7008
[root@localhost conf]# cp 7001/redis.conf 7007/redis.conf
[root@localhost conf]# cp 7001/redis.conf 7008/redis.conf
[root@localhost conf]# ll
總用量 0
drwxr-xr-x. 2 root root 112 3月  27 12:01 7001
drwxr-xr-x. 2 root root 112 3月  27 12:01 7002
drwxr-xr-x. 2 root root 112 3月  27 12:01 7003
drwxr-xr-x. 2 root root 112 3月  27 12:01 7004
drwxr-xr-x. 2 root root 112 3月  27 12:01 7005
drwxr-xr-x. 2 root root 112 3月  27 12:01 7006
drwxr-xr-x. 2 root root  24 3月  27 12:05 7007
drwxr-xr-x. 2 root root  24 3月  27 12:05 7008

增加主節點

擴容時,先增加主節點,再增加從節點

先啟動7007節點

[root@localhost redis-6.2.3]# pwd
/opt/app/redis/cluster/redis-6.2.3
[root@localhost redis-6.2.3]# ./bin/redis-server conf/7007/redis.conf

增加主節點之前的叢集狀態

image

向叢集中增加主節點

[root@localhost redis-6.2.3]# ./bin/redis-cli --cluster add-node 192.168.3.100:7007 192.168.3.100:7001

image

檢視叢集狀態

image

新增的節點,已成為叢集中的主節點,但還沒有給新主節點分配槽,0~16383(共16384個)這個範圍的槽,全部被分配在了原來的三個主節點上,即使現在向叢集中set資料,資料仍會被分配到原來的三個主節點上。

image

分配槽(slot)

現在,為新增的主節點分配槽,執行以下命令:

[root@localhost redis-6.2.3]# ./bin/redis-cli --cluster reshard 192.168.3.100:7001

此時會詢問給新增master節點分配多少個槽,總共16384個,平均分配給4個主節點,每個節點分配4096,就輸入4096

然後詢問接收節點的ID,輸入新增的master節點的ID

然後輸入從哪幾個節點來分,輸入前三個主節點的ID,最後輸入done表示結束。

image

輸入yes開始資料遷移,等待結束

image

檢查

使用redis-cli --cluster check檢查當前的叢集狀態,看槽是否已經分配完成

[root@localhost redis-6.2.3]# ./bin/redis-cli --cluster check 192.168.3.100:7001

image

測試

檢視下面的資料所在的槽和所屬的redis服務埠

image

至此,叢集增加主節點結束。

增加從節點

增加從節點有兩個步驟:1、將新增節點7008加入到叢集中,暫時作為主節點。2、將新增的節點7008掛接到從節點7007上去。

啟動新增的從節點

[root@localhost redis-6.2.3]# pwd
/opt/app/redis/cluster/redis-6.2.3
[root@localhost redis-6.2.3]# ./bin/redis-server  conf/7008/redis.conf

將節點加入到叢集

將新增的節點7008,加入到叢集中,暫時作為主節點

[root@localhost redis-6.2.3]# pwd
/opt/app/redis/cluster/redis-6.2.3
[root@localhost redis-6.2.3]# ./bin/redis-cli --cluster add-node 192.168.3.100:7008 192.168.3.100:7001

image

掛接節點

將新節點7008,掛接到叢集的7007主節點上,作為其從節點。在7008的客戶端裡面執行以下命令,最後面的ID是主節點7007的ID:

127.0.0.1:7008> cluster replicate 1d708c5042d53b6bc1e855ea41755782b6692e1a

image

檢視狀態、測試

此時檢視狀態,7008已經成為7007的從節點,而且7008上的資料,和7007上的資料完全一樣

image

至此,叢集擴容已完成。


叢集縮容

縮容的步驟,正好與擴容相反,先刪除從節點,再將主節點的槽,分配給其他三個主節點的其中一個,然後刪除主節點

刪除從節點

以刪除7008從節點為例,使用redis-cli --cluster del-node命令刪除從節點,192.168.3.100:7001 表示要從哪個叢集刪除(注意這並不是要刪除的節點IP和埠),後面的cd26feeb271c1260ec134d85dcdeaf4c72bfc3ad才表示要刪除的節點ID,也就是7008的ID

[root@localhost redis-6.2.3]# ./bin/redis-cli --cluster del-node 192.168.3.100:7001 cd26feeb271c1260ec134d85dcdeaf4c72bfc3ad
>>> Removing node cd26feeb271c1260ec134d85dcdeaf4c72bfc3ad from cluster 192.168.3.100:7001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.

此時再檢視叢集狀態,叢集中已經沒有7008節點了,但是7008的服務還啟動著,現在就可以關掉了

image

關掉7008服務

image

將要刪除的主節點的槽分配給其他主節點

在刪除叢集中的主節點7007之前,需要先將其槽分給其他的三個主節點中的某一個,這裡以分配給7001節點為例:

image

image

刪除主節點

此時,7007上已經沒有資料和槽了,可以從叢集中刪除7007節點了,刪除方式和刪除從節點一樣,直接執行刪除命令:

[root@localhost redis-6.2.3]# pwd
/opt/app/redis/cluster/redis-6.2.3
[root@localhost redis-6.2.3]# ./bin/redis-cli --cluster del-node 192.168.3.100:7001 1d708c5042d53b6bc1e855ea41755782b6692e1a
>>> Removing node 1d708c5042d53b6bc1e855ea41755782b6692e1a from cluster 192.168.3.100:7001
>>> Sending CLUSTER FORGET messages to the cluster...
>>> Sending CLUSTER RESET SOFT to the deleted node.
[root@localhost redis-6.2.3]#

再檢視叢集狀態,叢集中已經沒有7007節點了

image

再關閉7007服務即可:

image

至此,redis叢集縮容完成


Java連線cluster叢集

使用Jedis連線Redis

新建一個簡單的maven專案,在pom檔案中引入jedis依賴:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.1.1</version>
</dependency>

測試:

public class RedisClusterTest {
    public static void main(String[] args) {
        Set<HostAndPort> nodes = new HashSet();
        //叢集的所有節點
        nodes.add(new HostAndPort("192.168.3.100", 7001));
        nodes.add(new HostAndPort("192.168.3.100", 7002));
        nodes.add(new HostAndPort("192.168.3.100", 7003));
        nodes.add(new HostAndPort("192.168.3.100", 7004));
        nodes.add(new HostAndPort("192.168.3.100", 7005));
        nodes.add(new HostAndPort("192.168.3.100", 7006));
        //JedisCluster客戶端
        JedisCluster jedisCluster = new JedisCluster(nodes);
        //set值
        jedisCluster.set("a", "a");
        jedisCluster.set("b", "b");
        jedisCluster.set("c", "c");
        jedisCluster.set("d", "d");
        //get值
        System.out.println(jedisCluster.get("a"));
        System.out.println(jedisCluster.get("b"));
        System.out.println(jedisCluster.get("c"));
        System.out.println(jedisCluster.get("d"));
        //關閉jedisCluster
        jedisCluster.close();
    }
}

SpringBoot專案連線Redis

加入依賴

新建一個SpringBoot專案,在專案中引入以下依賴:

<!--        Spring web的依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

        <!--        SpringBoot專案pom檔案中,加入redis依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>2.6.4</version>
</dependency>

        <!--        test   -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-test</artifactId>
    <version>2.6.5</version>
    <scope>compile</scope>
</dependency>

        <!--        test   -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>RELEASE</version>
    <scope>compile</scope>
</dependency>

        <!--        fastjson,設定序列化-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.79</version>
</dependency>

配置叢集IP和埠

在application.properties配置檔案中,加入叢集的節點的配置資訊(這裡做了簡化)

spring.redis.cluster.nodes=192.168.3.100:7001,192.168.3.100:7002,192.168.3.100:7003,192.168.3.100:7004,192.168.3.100:7005,192.168.3.100:7006

配置RedisTemplate的序列化:

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jsonRedisSerializer.setObjectMapper(om);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(jsonRedisSerializer);

        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

測試

@SpringBootTest(classes = RedisBootApplication.class)
public class TestRedisClusterClient {

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Test
    public void test1() {
        redisTemplate.opsForValue().set("a", 1);
        redisTemplate.opsForValue().set("b", 2);
        redisTemplate.opsForValue().set("c", 3);
        redisTemplate.opsForValue().set("d", 4);
        System.out.println(redisTemplate.opsForValue().get("a"));
        System.out.println(redisTemplate.opsForValue().get("b"));
        System.out.println(redisTemplate.opsForValue().get("c"));
        System.out.println(redisTemplate.opsForValue().get("d"));
    }
}

在redis-cli中檢視

127.0.0.1:7001> get a
-> Redirected to slot [15495] located at 192.168.3.100:7003
"1"
192.168.3.100:7003> get b
-> Redirected to slot [3300] located at 192.168.3.100:7001
"2"
192.168.3.100:7001> get c
-> Redirected to slot [7365] located at 192.168.3.100:7002
"3"
192.168.3.100:7003> get d
-> Redirected to slot [11298] located at 192.168.3.100:7001
"4"

相關文章