手动搭建redis-cluster集群,新增节点,下线节点,slot迁移

本文使用的redis版本为5.0.3,不适用4.x/3.x版本,4.x/3.x版本请使用redis-trib.rb脚本

写本文的目的

网上有很多的redis集群的方案,大体分为两类:一类是基于redis官方的cluster方案,一类是各家一线互联网大厂的内部方案。在满足项目需求的情况下,自然是选择官方的方案。理由很简单:有持续的维护,官方背景,文档资料充足,学习曲线平滑。但是网上大部分文章都是讲理论,很少有实战性的内容,因此基于目前项目的实战,整理此文。希望对有实战需求的同学有所帮助,能力有限,望大家多多提供好的建议!

术语

redis实例 = 平常大家开发时使用的单个redis服务实例。
redis节点 = 本文指的节点是一主一从两个配对的redis实例。

仿高吞吐量环境

为了验证集群节点变更情况下的可用性,我们使用一个程序来模拟高吞吐量的环境,这个程序会一直跑,我们做一个全程可用性监控:

@Service
public class CacheService {

    private AtomicLong atomicLong;
    private ReentrantLock reentrantLock;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @PostConstruct
    public void init() {
        atomicLong = new AtomicLong();
        reentrantLock = new ReentrantLock();
    }

    public Object r(String k) {
        Object v = redisTemplate.opsForValue().get(k);
        if (v == null) {
            reentrantLock.lock();
            try {
                Object vv = redisTemplate.opsForValue().get(k);
                if (vv != null) {
                    System.out.println("THEAD HAS WRITTEN KEY: "+ k);
                    return vv;
                }
                return w(k, "AABBCCDD");
            } finally {
                reentrantLock.unlock();
            }
        }
        return v;
    }

    public Object w(String k, Object v) {
        long calls = atomicLong.incrementAndGet();
        redisTemplate.opsForValue().set(k, v/*, Duration.ofSeconds(60*10)*/);
        if (calls%10000 ==0) {
            System.out.println("write call "+calls+" times.");
        }
        System.out.println("write KEY: "+k);
        return v;
    }
}

初始化部署

官方建议集群数量3节点起步,那么我们规划的目标机器如下,每个机器都部署一主一从两个redis实例

172.22.122.22 #初始部署
172.22.122.23 #初始部署
172.22.122.24 #初始部署
172.22.122.25 #扩容测试用

查阅官方提供的初始化部署方法如下:
每个节点启动2点redis实例,最后,运行如下命令
其中的参数--cluster-replicas 1指的是每个主实例附带一个从实例,构成主从。

redis-cli --cluster create --cluster-replicas 1 172.22.122.22:7000 172.22.122.22:7001 172.22.122.23:7002 172.22.122.23:7003 172.22.122.24:7004 172.22.122.24:7005

然后可以看到redis自动帮我们确定主实例和从实例,最后还自动分配好了三个节点各自的slot区间,这些slot区间都是连续的。

平滑新增节点

接下来,我们在172.22.122.25机器上启动两个redis实例。根据redis-cluster官方手册,我们做如下配置:

#在这里,172.22.122.25:7006节点为我们需要新加入的节点,而172.22.122.22:7000为6个老节点之一,可以从6个老节点中随意选一个
./redis-cli --cluster add-node 172.22.122.25:7006 172.22.122.22:7000
#在这里,172.22.122.25:7006节点为我们需要新加入的节点,而172.22.122.22:7000为6个老节点之一,可以从6个老节点中随意选一个
#而--cluster-master-id a35dce8fbe264d9f952ba9ae30700bf5869694e6 则为上面加入的节点的master-id
./redis-cli --cluster add-node 172.22.122.25:7007 172.22.122.22:7000 --cluster-slave --cluster-master-id 6b6b877d529f6661da049eb4e97e6a46d593a0bf
[root@yyy src] ./redis-cli --cluster add-node 172.22.122.25:7007 172.22.122.22:7000 --cluster-slave --cluster-master-id a35dce8fbe264d9f952ba9ae30700bf5869694e6
>>> Adding node 172.22.122.25:7007 to cluster 172.22.122.22:7000
>>> Performing Cluster Check (using node 172.22.122.22:7000)
M: bd49cb987a00ca814d5df1afa43c014fa982ddb4 172.22.122.22:7000
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
M: a35dce8fbe264d9f952ba9ae30700bf5869694e6 172.22.122.25:7006
   slots: (0 slots) master
M: 718a893d8af2a1c0df09eabe5f46e9bb06681202 172.22.122.23:7002
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 335a6f75823dd396d12183bb122c8ed080fbd413 172.22.122.24:7005
   slots: (0 slots) slave
   replicates bd49cb987a00ca814d5df1afa43c014fa982ddb4
M: 5ba08a9724cc6f9fd261bba1d5021972192f5f12 172.22.122.24:7004
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
S: aa82018ca28eaeab19e9282d3144ce68e1c3150f 172.22.122.22:7001
   slots: (0 slots) slave
   replicates 718a893d8af2a1c0df09eabe5f46e9bb06681202
S: f0cd581693a3bbf3636228e6dbb2c9f98f1462ed 172.22.122.23:7003
   slots: (0 slots) slave
   replicates 5ba08a9724cc6f9fd261bba1d5021972192f5f12
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 172.22.122.25:7007 to make it join the cluster.
Waiting for the cluster to join

>>> Configure node as replica of 172.22.122.25:7006.
[OK] New node added correctly.

添加完节点之后不会自动生成slot,需要做集群间的slot迁移,这块可以做热迁移,在不停服的情况下完成。测试在小批量数据下(万条)毫无影响,在比较大的数据量情况下,例如亿条级别,需要做谨慎的测试,这块留到今后补充。

分配slot尽量防止碎片化

一种简单的重新分配slot的方法如下命令:

./redis-cli --cluster reshard 172.22.122.22:7000
# 需要输入槽数量信息等,

最终重分配结果如下:

172.22.122.22:7000> cluster slots
1) 1) (integer) 0
   2) (integer) 665
   3) 1) "172.22.122.25"
      2) (integer) 7006
      3) "a35dce8fbe264d9f952ba9ae30700bf5869694e6"
   4) 1) "172.22.122.25"
      2) (integer) 7007
      3) "78f99e7847b257807e58b6efc5614cb6934fc76d"
2) 1) (integer) 5461
   2) (integer) 6127
   3) 1) "172.22.122.25"
      2) (integer) 7006
      3) "a35dce8fbe264d9f952ba9ae30700bf5869694e6"
   4) 1) "172.22.122.25"
      2) (integer) 7007
      3) "78f99e7847b257807e58b6efc5614cb6934fc76d"
3) 1) (integer) 10923
   2) (integer) 11588
   3) 1) "172.22.122.25"
      2) (integer) 7006
      3) "a35dce8fbe264d9f952ba9ae30700bf5869694e6"
   4) 1) "172.22.122.25"
      2) (integer) 7007
      3) "78f99e7847b257807e58b6efc5614cb6934fc76d"
4) 1) (integer) 6128
   2) (integer) 10922
   3) 1) "172.22.122.23"
      2) (integer) 7002
      3) "718a893d8af2a1c0df09eabe5f46e9bb06681202"
   4) 1) "172.22.122.22"
      2) (integer) 7001
      3) "aa82018ca28eaeab19e9282d3144ce68e1c3150f"
5) 1) (integer) 11589
   2) (integer) 16383
   3) 1) "172.22.122.24"
      2) (integer) 7004
      3) "5ba08a9724cc6f9fd261bba1d5021972192f5f12"
   4) 1) "172.22.122.23"
      2) (integer) 7003
      3) "f0cd581693a3bbf3636228e6dbb2c9f98f1462ed"
6) 1) (integer) 666
   2) (integer) 5460
   3) 1) "172.22.122.22"
      2) (integer) 7000
      3) "bd49cb987a00ca814d5df1afa43c014fa982ddb4"
   4) 1) "172.22.122.24"
      2) (integer) 7005
      3) "335a6f75823dd396d12183bb122c8ed080fbd413"

这种分配方式会随机的从已经在运行的节点中获取一部分slot分配到新的主从节点中,容易造成slot的碎片化,对今后的迁移下线会造成更加复杂的操作。因此在这里,个人推荐增加主从节点时,只从一个老的节点中分配一半的slot数量,新增节点数按双倍的方式扩容。优点是不会产生碎片,slot分配连续,回收slot也更加方便。

我们使用以下命令就可以做到在节点间的连续slot迁移:

./redis-cli --cluster reshard --cluster-from bd49cb987a00ca814d5df1afa43c014fa982ddb4 --cluster-to 718a893d8af2a1c0df09eabe5f46e9bb06681202 --cluster-slots 5460 --cluster-yes 172.22.122.22:7000

接下来我们模拟某台机器挂了,直接选择

192.168.1.4

停掉这台机器上的redis主从实例,然后我们选择任意一台存活机器查看集群信息,可以看到标注为fail的节点:

172.22.122.24:7005> cluster nodes
f0cd581693a3bbf3636228e6dbb2c9f98f1462ed 172.22.122.23:7003@17003 slave 5ba08a9724cc6f9fd261bba1d5021972192f5f12 0 1547709963620 5 connected
78f99e7847b257807e58b6efc5614cb6934fc76d 172.22.122.25:7007@17007 slave,fail a35dce8fbe264d9f952ba9ae30700bf5869694e6 1547702940350 1547702939850 8 disconnected
718a893d8af2a1c0df09eabe5f46e9bb06681202 172.22.122.23:7002@17002 master - 0 1547709962616 3 connected 6128-10922
335a6f75823dd396d12183bb122c8ed080fbd413 172.22.122.24:7005@17005 myself,slave bd49cb987a00ca814d5df1afa43c014fa982ddb4 0 1547709961000 6 connected
a35dce8fbe264d9f952ba9ae30700bf5869694e6 172.22.122.25:7006@17006 master,fail - 1547702940350 1547702939000 8 disconnected 0-665 5461-6127 10923-11588
bd49cb987a00ca814d5df1afa43c014fa982ddb4 172.22.122.22:7000@17000 master - 0 1547709962113 1 connected 666-5460
5ba08a9724cc6f9fd261bba1d5021972192f5f12 172.22.122.24:7004@17004 master - 0 1547709960000 5 connected 11589-16383
aa82018ca28eaeab19e9282d3144ce68e1c3150f 172.22.122.22:7001@17001 slave 718a893d8af2a1c0df09eabe5f46e9bb06681202 0 1547709963000 3 connected

此时因为192.168.1.4上的主从节点都被停掉了,因此其实这个集群已经不可用了(slot已经不再完整,因此Java客户端的连接都会报集群不可用的错误),我们需要想办法恢复集群。

查阅redis的官方手册,我们发现有个命令,CLUSTER FORGET可以让集群下线fail的节点,需要注意的是这个命令要在每个redis实例上执行,主节点,从节点都需要。

所有的实例都执行这个命令之后,我们再次检查集群状态:

172.22.122.24:7004> cluster nodes
bd49cb987a00ca814d5df1afa43c014fa982ddb4 172.22.122.22:7000@17000 master - 0 1547709998840 1 connected 666-5460
aa82018ca28eaeab19e9282d3144ce68e1c3150f 172.22.122.22:7001@17001 slave 718a893d8af2a1c0df09eabe5f46e9bb06681202 0 1547709997135 3 connected
335a6f75823dd396d12183bb122c8ed080fbd413 172.22.122.24:7005@17005 slave bd49cb987a00ca814d5df1afa43c014fa982ddb4 0 1547709996131 6 connected
718a893d8af2a1c0df09eabe5f46e9bb06681202 172.22.122.23:7002@17002 master - 0 1547709998000 3 connected 6128-10922
f0cd581693a3bbf3636228e6dbb2c9f98f1462ed 172.22.122.23:7003@17003 slave 5ba08a9724cc6f9fd261bba1d5021972192f5f12 0 1547709996833 5 connected
5ba08a9724cc6f9fd261bba1d5021972192f5f12 172.22.122.24:7004@17004 myself,master - 0 1547709997000 5 connected 11589-16383

但是集群的状态还是fail的,这是因为,我们简单的下线了那台机器,却没有将损失的虚拟slot回收,导致整个集群依旧处于缺失slot状态,我们需要将丢失的slot再找回来。

./redis-cli --cluster fix 172.22.122.24:7004

使用修复命令,将16384个slot重新分配到三点集群中

再平衡:

./redis-cli --cluster reshard --cluster-from bd49cb987a00ca814d5df1afa43c014fa982ddb4 --cluster-to 718a893d8af2a1c0df09eabe5f46e9bb06681202 --cluster-slots 5460 --cluster-yes 172.22.122.22:7000

登陆集群,查看节点生存状况

[root@172 src]# ./redis-cli -h 172.22.122.22 -p 7000 -c
172.22.122.22:7000> cluster nodes
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • 从事Redis Cluster相关工具开发半年多, 记录一下对它的理解和集群管理的想法吧. 这里不复述Redis ...
    doyoubi阅读 1,629评论 0 2
  • 1.1 Redis集群的设计原则和初衷 在官方文档Cluster Spec中,作者详细介绍了Redis集群为什么要...
    Flame_1109阅读 2,106评论 1 5
  • 1 Redis介绍1.1 什么是NoSql为了解决高并发、高可扩展、高可用、大数据存储问题而产生的数据库解决方...
    克鲁德李阅读 5,257评论 0 36
  • 本文是对Redis的集群部署模式一个学习总结,共包括如下章节内容: 概述 主从集群模式 “哨兵”集群模式 Clus...
    我是老薛阅读 921评论 0 4
  • 半夏组织了一下语言,吞了吞唾沫,被胖子的眼光盯得汗都冒了出来。 “她刚搬来的时候我就知道会是这样,只是没想到这么快...
    S菩提只吃半碗饭阅读 297评论 0 2