WilliamUUUU / LearningNote

0 stars 0 forks source link

Redis #1

Open WilliamUUUU opened 1 year ago

WilliamUUUU commented 1 year ago

init

WilliamUUUU commented 1 year ago

一、单机搭建Redis

1.1 windows

1.1.1 搭建环境

1.1.2 快速入门

下载地址: https://github.com/MicrosoftArchive/redis/releases

将下载好的文件解压

image

进入解压好的文件夹中,在地址栏输入"cmd",打开命令行窗口,输入以下命令

redis-server.exe redis.windows.conf

见到以下界面说明启动成功,Redis默认启动端口是6379。如果需要修改,可以在redis.windows.conf里修改

image

测试是否可以连接,在文件夹中双击打开redis-cli.exe,输入ping,如果输出PONG则表示连接成功。

image

1.1.3 作为windows服务启动

以命令行方式启动很多缺点,如每次电脑重启都要打开那个黑窗口,万一不小心关掉了,服务就停止了。所以把它制作成windows服务,就可以开机自动启动,也不会任务栏展示。

进入解压的文件夹中,在地址栏输入"cmd",打开命令行窗口,输入以下命令

redis-server --service-install redis.windows.conf

image

在服务里找到并启动Redis

image

使用redis-cli.exe进行测试。

卸载服务:redis-server --service-uninstall

1.2 linux

1.2.1 搭建环境

1.2.2 快速入门

下载地址:https://redis.io/download/

image

将下载好的文件通过xftp上传到服务器上,可以放在tmp目录下

image

使用下列命令进行解压,版本不一样,文件名也要变

tar -zvxf redis-7.0.8.tar.gz -C /opt

进入/opt目录,使用以下命令进行重命名(这个步骤可以不用)

mv -i redis-7.0.8/ redis

由于Redis是C语言编写的,所以需要gcc,可以先用

gcc -v

来查看是否已经安装了gcc,如果没有安装,使用以下命令进行安装

yum -y install gcc

进入redis目录,使用make命令进行编译,这时控制台会输出编译信息,等待编译完成。输入命令

make install

自此redis已经安装完成

在启动redis之前推荐修改配置文件(redis.conf)中以下内容

redis启动

redis-server redis.conf

使用RedisDesktopManager进行连接

1.2.3 制作成服务,开机自启

新建redis.service文件

vim /usr/lib/systemd/system/redis.service

写入以下内容

[Unit]
Description=Redis
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/var/run/redis.pid
ExecStart=/opt/redis/src/redis-server /opt/redis/redis.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

重载系统服务

systemctl daemon-reload

启动redis服务

systemctl start redis

设置开机自起

systemctl enable redis.service
WilliamUUUU commented 1 year ago

二、基本命令

以下测试都是在windows上进行的

2.1 String

get xxx
set key value [EX seconds|PX milliseconds] [NX|XX]
EX|PX表示设置值的生存时间,EX对应秒,PX对应毫秒
NX表示当key不存在时才进行设置值的操作
XX表示当key存在时才进行设置值的操作
mset key value [key value...] 
# 不包含NX、XX、PX、EX等参数
# 如果带上EX、PX参数,不会报错,但实际上不生效
# 如果带上NX、XX参数,会直接报错
mget key [key...]
incr key 对key对应的数值加一
incrby key increment 对key对应的数值加increment
decr key 对key对应的数据减一
decrby key decrement 对key对应的数值减decrement
append key value 把value追加到key原值的后面,数值型的也是直接追加到后面。1 append 2 -> 12
getrange key start end
setrange key offset value
getset key value
# 不能追加EX、PX、NX、XX参数
# 如果key对应的值存在,则会用value覆盖旧值,同时返回旧值;
# 如果key对应的值不存在,也会设值,但会返回nil。

2.2 Hash

hset key field value
# 只能一对一对的设置,同时多个设置会报错
hget key field
hsetnx key field value
hkeys key
hvals key
hgetall key
hexists key field
# 存在返回1,不存在返回0,不写field报错
hdel key field [field...]

2.3 List

lpush key element [element...]
rpush key element [element...]
lindex key index
# 不带索引会报错
# 索引从0开始
# 如果index超过会返回nil
# 如果index为负,会返回第一个元素
lpushx key element 从头部插入
rpushx key element 从尾部插入
# 只能一个个插入,多个元素会报错
lpop key 从头部出队
rpop key 从尾部出队
# key不存在则返回nil
lrange key start stop
# [start stop]是闭区间,0 2 会返回3个值
# 如果start > stop 会返回空
# stop大于结束索引会取结束索引
lset key index element
# index可以为负,-1表示修改最后一个
# 超过最大索引会报错
lrem key count element
# 当count等于0时,删除该列表例所有值是element的数据
# 当count大于0时,删除从头到尾方向数量为count个、值是element的数据
# 当count小于0时,删除从尾到头方向数量为count个、值是element的数据

2.4 Set

sadd key member [member...]
smembers key
sismember key member
# 存在返回1,不存在返回0
sinter key [key ...]
# 交集:所有属于集合A且属于集合B的元素所组成的集合
sunion key [key ...]
# 并集:属于集合A与属于集合B的元素所有的元素合并在一起组成的集合
sdiff key [key ...]
# 差集:所有属于A但不属于B元素构成的集合
srem key member [member ...]
# 如果从一个不存在的集合中删除数据,会返回0
# 如果从一个非集合对象中删除数据,会报错

2.5 ZSet

在有序集合中,每个数据都会对应一个score参数,以此来描述该数据的分数

zadd key [NX|XX] [CH] [INCR] score member [score member ...]
# NX参数表示只有当key对应的有序集合不存在时才添加member元素
# XX参数表示当有序集合存在时才添加member元素
# CH参数指定该zadd命令修改时返回的个数(默认返回0)
# 当待插入的member不存在时,INCR参数不会起作用
# 当member存在时,会让score加上由INCR指定的数值
# score参数是用来描述元素的数值(也叫权重),即元素在集合中的重要程度
# zadd命令能够同时添加一个或多个score member元素对
同分数问题:不同的member有相同的分数,redis会按时ascill码的先后顺序进行排序
zrange key start stop [WITHSCORES]
# WITHSCORES参数控制读取后带不带scores信息输出
zreverange key start stop [WITHSCORES]
# WITHSCORES参数控制读取后带不带scores信息输出
zincrby key increment member
# 如果修改一个不存在的member,相当于添加一个元素,分数为increment
# increment表示为减
zscore key member
zrank key member
zrevrank key member
zrem key member [member ...]
zremrangebyrank key start stop
zremrangebyscore key min max

2.6 GeoSpatial

底层是一个zset

geoadd key [nx|xx] [ch] longitude latitude member [longitude latitude member ...]
geopos key member [member ...]
geodist key member1 member2 [m|km|ft|mi]
# 如果其中有一个不存在则返回nil
# m:米 km:千米 ft:英尺 mi:英里
geosearch key [frommember member] [fromlonlat longitude latitude] [byradius radius m|km|ft|mi] [bybox width height m|km|ft|mi] [asc|desc] [count count [any]] [withcoord] [withdist] [withhash]
key:提供一个geoadd的空间集合key
[frommember member]:在我们指定的空间集合中选择一个空间元素作为中心点
[fromlonlat longitude latitude]:我们指定经纬度来作为中心点
[byradius radius m|km|ft|mi]:根据给定的radius范围在圆形区域内搜素(参考雷达图)
[bybox width height m|km|ft|mi]:根据给定的width X坐标 height Y坐标 中轴对齐的矩形内中心点搜素
[asc|desc]:返回结果按照离中心节点的距离做升序或者降序
[count count [any]]:指定返回结果的数量
[withcoord]:返回的结果中包含经纬度
[withdist]:返回的结果中包含离中心点的位置距离
[withhash]:返回的结果中包含geohash(此值用来表示经纬度,但是用hash不是太准)
注:frommember与fromlonlat不能同时出现,只能选择其一
注:byradius与bybox不能同时出现,只能选择其一

2.7 BitMap位图

setbit key offset value
getbit key offset
# 不存在返回0
bitcount key [start end]
bitop operation destkey key [key...]
# opeartion可选操作为 AND(与)、OR(或)、XOR(异或)、NOT(非) 
# 除NOT外,其他操作都可以输入多个key
# destkey 运算结果保存的key值
bitfield key [get type offset] [set type offset value] [incrby type offset increment] [overflow wrap|sat|fail]
# get 返回指定的位域
# set 设置指定位域的值并返回它的原值
# incrby 自增或自减指定位域的值并返回它的新值
# overflow 设置溢出行为 WRAP: 回环算法 WRAP: 回环算法 FAIL: 失败算法
# 当需要一个整型时,有符号整型需在位数前加i,无符号在位数前加u

2.8 其他命令

select 1 选择数据库,从0开始
dbsize 查看当前数据库中key的数量
keys * 查看所有的key,* 正则表达式
flushall 清空所有数据库
flushdb 清空当前数据库
del key [key...] 删除key
exists key [key...] 指定key是否存在
expire key seconds 给指定key设置过期时间,单位:秒
pexpire key milliseconds 给指定key设置过期时间,单位:毫秒
ttl key 返回key的剩余时间,单位:秒 已过期返回-2,没有过期时间返回-1
pttl key 返回key的剩余时间,单位:毫秒 已过期返回-2,没有过期时间返回-1
WilliamUUUU commented 1 year ago

三、JAVA操作Redis几种常见的方式

3.1 Jedis

创建一个maven工程,将jedis依赖引入项目中

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

编写工具类

package com.growatt.redis1.util;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisUtil
{
    // jedis连接池
    private static JedisPool jedisPool;

    private static final String host = "127.0.0.1";

    private static final int port = 6379;

    private static final int timeout = 10000;

    private static final String password = "redispass";

    // 默认数据库
    private static final int defaultDatebase = 10;
    // 初始化
    static void init()
    {
        if (jedisPool == null)
        {
            // 配置连接参数
            JedisPoolConfig poolConfig = new  JedisPoolConfig();
            poolConfig.setMaxIdle(200);//最大空闲链接
            poolConfig.setMaxTotal(500);//最大连接数
            poolConfig.setMaxWaitMillis(1000 * 10);//最大等待毫秒数
            poolConfig.setTestOnBorrow(true);//获取链接检查有效性
            poolConfig.setTestOnReturn(false);//返回验证
            poolConfig.setBlockWhenExhausted(true);//链接好近是否阻塞
            poolConfig.setTestOnCreate(true);//部署时 为True;
            try
            {
                jedisPool = new JedisPool(poolConfig, host, port, timeout, password);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    // 获取一个jedis
    private synchronized static Jedis getJedis() {
        return getJedis(defaultDatebase);
    }

    /**
     * 获取一个jedis
     * @param dbIndex 需要操作的数据库索引
     * @return
     */
    private synchronized static Jedis getJedis(int dbIndex) {
        Jedis jedis = null;
        JedisUtil.init();
        if (jedisPool != null) {
            try {
                jedis = jedisPool.getResource();
                jedis.select(dbIndex);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
        return jedis;
    }

    // 释放redis资源
    private static void releaseResource(Jedis jedis) {
        if (jedis != null) {
            try {
                jedis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取对应key的值
     * @param key redis key
     * @return
     */
    public static String get(String key) {
        return get(key, defaultDatebase);
    }

    /**
     * 获取指定数据库中对应key的值
     * @param key redis key
     * @param dbIndex 数据库索引
     * @return
     */
    public static String get(String key, int dbIndex) {
        Jedis jedis = getJedis(dbIndex);
        if(jedis == null){
            return null;
        }
        try {
            return jedis.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            releaseResource(jedis);
        }
        return null;
    }

    /**
     * 插入数据
     * @param key redis key
     * @param value redis value
     * @param seconds 过期时长(秒)
     * @return
     */
    public synchronized static Boolean save(String key, String value, int seconds) {
        return save(key, value, seconds, defaultDatebase);
    }

    /**
     * 插入数据
     * @param key redis key
     * @param value redis value
     * @param seconds 过期时长(秒)
     * @param dbIndex 数据库索引
     * @return
     */
    public synchronized static Boolean save(String key, String value, int seconds, int dbIndex) {
        Jedis jedis = getJedis(dbIndex);
        if(jedis == null){
            return false;
        }
        try {
            jedis.del(key);
            jedis.set(key, value);
            if (seconds != 0) {
                jedis.expire(key, seconds);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            releaseResource(jedis);
        }
    }
}

这里实现了通过传入数据库的索引来访问不同数据库

测试默认数据库

public static void main(String[] args)
{
    JedisUtil.save("1", "张三", 100);
}

结果

image

测试其他数据库

public static void main(String[] args)
{
    JedisUtil.save("1", "张三", 1000, 12);
}

image

测试默认获取

public static void main(String[] args)
{
    System.out.println(JedisUtil.get("1"));
}

image

测试其他数据库获取

public static void main(String[] args)
{
    System.out.println(JedisUtil.get("1", 12));
}

3.2 Lettuce

Lettuce是一个高性能基于Java编写的Redis驱动框架,底层集成了Project Reactor提供自然的反应式编程,通讯框架集成了Netty使用了非阻塞IO,5.x版本以后融合了JDK1.8的异步编程特性,在保证高性能的同时提供了十分丰富易用的API。

创建一个maven工程,将lettuce-core依赖引入项目中

<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
    <version>6.1.8.RELEASE</version>
</dependency>

简单插值

public static void main(String[] args)
{
    RedisURI redisURI = RedisURI.builder().withHost("127.0.0.1").withPort(6379).withPassword("redispass").build();
    RedisClient redisClient = RedisClient.create(redisURI);
    StatefulRedisConnection<String, String> connect = redisClient.connect();
    try
    {
        RedisCommands<String, String> redisCommands = connect.sync();
        redisCommands.select(10);
        redisCommands.set("lettuce", "1234");
        redisCommands.expire("lettuce", 60 * 1000); // 以秒为单位
    } catch (Exception e)
    {
        e.printStackTrace();
    } finally
    {
        connect.close();
        redisClient.shutdown();
    }
}

取值

public static void main(String[] args)
{
    RedisURI redisURI = RedisURI.builder().withHost("127.0.0.1").withPort(6379).withPassword("redispass").build();
    RedisClient redisClient = RedisClient.create(redisURI);
    StatefulRedisConnection<String, String> connect = redisClient.connect();
    try
    {
        RedisCommands<String, String> redisCommands = connect.sync();
        redisCommands.select(10);
        String lettuce = redisCommands.get("lettuce");
        log.info("get redis = " + lettuce);
    } catch (Exception e)
    {
        e.printStackTrace();
    } finally
    {
        connect.close();
        redisClient.shutdown();
    }
}

// 输出
14:32:33.784 [main] get redis = 1234

3.3 spring-boot-starter-data-redis

创建一个springboot工程,将spring-boot-starter-data-redis依赖引入项目中

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.1</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

编写配置文件application.yml

server:
  port: 9745
spring:
  redis:
    host: 127.0.01
    port: 6379
    password: redispass
    jedis:
      pool:
        max-active: 50
        max-wait: -1
        min-idle: 0
    timeout: 10000
    database: 10

编写测试类

@SpringBootTest
public class RedisTest3
{
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Test
    public void test1()
    {
        redisTemplate.boundValueOps("redisTemplate").set("123456", 60, TimeUnit.SECONDS);
    }
}
WilliamUUUU commented 1 year ago

四、Redis集群

4.1 简介

redis作为目前主流的缓存服务器,同样会发生宕机的问题。为了实现redis的高可用,所以集群就应运而生。

redis为解决单点故障问题引入了主从模式,但是master节点如果故障就需要人工进行切换,才能够恢复服务。为了解决这一问题,redis引入了哨兵模式,哨兵是一个独立的进程,作为进程,它独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。当哨兵监测到Redis主机宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他服务器,修改配置文件,让他们换主机。主从模式和哨兵模式每个节点上都存着全量数据,对于资源的利用是比较差的,所以后来redis又发布了redis cluster,实现了真正的数据分片存储。

详细介绍见:https://blog.csdn.net/a745233700/article/details/112691126

4.2 集群的搭建

参考:https://juejin.cn/post/6922690589347545102#heading-1

4.2.1 基础搭建

本次搭建的集群为3主3从,共6个节点。其拓扑图如下

image

环境准备

首先去官网下载tar包,地址:https://redis.io/download/

使用xftp把tar包上传到服务器中,随便放个位置,我放在/data目录下

使用解压命令

tar -zxvf redis-7.0.9.tar.gz 

将解压好的包放到/usr/local/redis下

mv -i redis-7.0.9 /usr/local/redis/

进入redis的目录中使用命令

make install

等待安装完成

编写配置文件

vim redis-cluster-6379.conf
bind 0.0.0.0
port 6379 // 这里需要根据节点变动 
daemonize yes
requirepass "XXXX" // 密码
logfile "./cluster-6379.log" // 这里需要根据节点变动 
dbfilename "cluster-6379.rdb" // 这里需要根据节点变动 
dir "./"
masterauth "XXXX" // 密码
#是否开启集群
cluster-enabled yes
# 生成的node文件,记录集群节点信息,默认为nodes.conf
cluster-config-file nodes-6379.conf // 这里需要根据节点变动 
#节点连接超时时间
cluster-node-timeout 20000
#集群节点映射端口
cluster-announce-port 6379 // 这里需要根据节点变动 
#集群节点总线端口,节点之间互相通信,常规端口+1万
cluster-announce-bus-port 16379 // 这里需要根据节点变动 

后续5个节点都按上面配置好

配置好后就可以启动6个实例

./src/redis-server redis-cluster-6379.conf 
./src/redis-server redis-cluster-6380.conf 
./src/redis-server redis-cluster-6381.conf 
./src/redis-server redis-cluster-6479.conf 
./src/redis-server redis-cluster-6480.conf 
./src/redis-server redis-cluster-6481.conf 

此时可以查看6个实例的状态

ps -ef | grep redis

image

使用命令进行集群的创建

./src/redis-cli -a xxxx --cluster create 120.XX.XX.171:6379 120.XX.XX.171:6380 120.XX.XX.171:6381 120.XX.XX.171:6479 120.XX.XX.171:6480 120.XX.XX.171:6481 --cluster-replicas 1
# 这里有几个注意的点
# xxxx是你写在配置文件的密码
# 后面的 IP:PORT这里一定要写公网地址,如果你的项目也是在本机运行可以写成127.0.0.1
# 后面的 --cluster-replicas 1 表示一个主节点下面的从节点个数

如果想要使用想要远程连接记得把使用的端口全打开,不然只能在本机访问

至此简单的3主3从redis集群已经搭建完毕

4.2.2 采坑点

解决方法,首先确认是否6个端口全部开放。redis占用的端口不只默认(6379)的,还会占用+1W的端口用于通讯,所以也要将16379等几个端口开放

解决方法,先停掉实例,将生成的nodes-6379.conf与cluster-6379.rdb等全部删除掉,再重新启动,然后再执行命令

4.3 springboot连接redis集群

<!-- 集成redis依赖  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

编写配置文件

server:
  port: 8041

spring:
  redis:
    database: 10
    password: xxxx
    jedis:
      pool:
        max-active: 50
        max-wait: -1
        min-idle: 0
    timeout: 10000
    cluster:
      nodes:
        - 120.xx.xx.171:6379
        - 120.xx.xx.171:6380
        - 120.xx.xx.171:6381
        - 120.xx.xx.171:6479
        - 120.xx.xx.171:6480
        - 120.xx.xx.171:6481
      max-redirects

修改序列化方式

@Configuration
public class RedisTemplateConfig
{

    @Autowired
    private RedisConnectionFactory factory;

    @Bean
    public RedisTemplate<String, Object> redisTemplate()
    {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

写个controller调用

@RestController
@Slf4j
@RequestMapping("/clusterTest")
public class ClusterTestController
{

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("/write")
    public String write()
    {
        log.info("开始写入redis");
        for (int i = 0; i < 500; i++)
        {
            redisTemplate.opsForValue().set(""+i, String.valueOf(i));
        }
        return "success";
    }

}

启动项目,调用方法

image

如果你用的是云服务器,可能会出现的问题

image

读取的连接是本机127.0.0.1,这个问题就是在create的时候,ip写的127.0.0.1导致

解决方法:见4.2.2

image

配置里配的是外网地址,但连接的是内网的地址

解决方法:

image

先停掉实例,修改生成的nodes-6379.conf(全部都要改)里面的myself前面的ip,改成自己的外网地址。然后重启实例。

重启springboot服务,调用方法成功写入。

4.4 模拟故障

4.4.1 杀掉6379主节点

使用命令查看节点的pid

ps -ef | grep redis

image

使用命令杀掉进程

kill -9 17919

查看其他节点的日志

image

进入其中一个节点查看集群信息

6479节点已经切换成master节点,6379节点被标记为fail

此时redis依旧可以提供服务。

4.4.2 杀掉备用节点6479

image

此时redis已经停止服务了

4.4.3 启动6379节点

image

可以看到依旧是slave节点,此时redis并没有恢复

4.4.4 杀掉刚才启动的6379节点,启动原来的6479主节点

image

启动master节点,redis恢复服务

4.4.5 结论

杀掉一对主从节点的任一节点,redis都可以继续提供服务;

杀掉一对主从节点的所有节点,redis停止服务;

启动master节点而不启动slave节点,redis恢复服务;

启动slave节点而不启动master节点,redis不会恢复服务。

4.5 模拟新加入节点

4.5.1 新增一个主节点

复制一个配置文件

cp redis-cluster-6379.conf redis-cluster-6382.conf

编辑配置文件,修改内容如下

bind 0.0.0.0
port 6382
daemonize yes
requirepass "xxxx"
logfile "./cluster-6382.log"
dbfilename "cluster-6382.rdb"
dir "./"
masterauth "xxxx"
#是否开启集群
cluster-enabled yes
# 生成的node文件,记录集群节点信息,默认为nodes.conf
cluster-config-file nodes-6382.conf
#节点连接超时时间
cluster-node-timeout 20000
#集群节点映射端口
cluster-announce-port 6382
#集群节点总线端口,节点之间互相通信,常规端口+1万
cluster-announce-bus-port 16382

启动6382节点

./src/redis-server redis-cluster-6382.conf 

将新启动的节点加入到redis集群中

./src/redis-cli -a xxxx --cluster add-node 120.xx.xx.171:6382 120.xx.xx.171:6379

查看节点信息

image

新的节点已经加入,并成为主节点,此时这个节点并不会提供服务,因为没有给它分配槽位,所以我们需要重新分配

 ./src/redis-cli -a xxxx --cluster reshard 120.xx.xx.171:6382

执行命令后会询问你下列问题:

How many slots do you want to move (from 1 to 16384)?
表示需要移动槽的数量
What is the receiving node ID?
表示哪个id来接收它
Source node #1
从那个节点id上移动散列插槽。如果需要平均节点插槽,那么每个主节点的id都写上或者写all,然后需要移动的节点数量处理主节点数。
Do you want to proceed with the proposed reshard plan (yes/no)?
确定要移动这些槽的计划吗?输入yes或者no

查看是否添加成功

image

最后不要忘记修改nodes-6379.conf等几个文件的内容,把内网地址改成外网地址,重启集群

修改springboot配置文件,重启项目,写入数据

image

进入6382节点,查看节点内的数据

image

4.5.2 新增一个从节点

前几步和4.5.1相同

复制一个配置文件

cp redis-cluster-6379.conf redis-cluster-6482.conf

编辑配置文件,修改内容如下

bind 0.0.0.0
port 6482
daemonize yes
requirepass "xxxx"
logfile "./cluster-6482.log"
dbfilename "cluster-6482.rdb"
dir "./"
masterauth "xxxx"
#是否开启集群
cluster-enabled yes
# 生成的node文件,记录集群节点信息,默认为nodes.conf
cluster-config-file nodes-6482.conf
#节点连接超时时间
cluster-node-timeout 20000
#集群节点映射端口
cluster-announce-port 6482
#集群节点总线端口,节点之间互相通信,常规端口+1万
cluster-announce-bus-port 16482

启动节点

./src/redis-server redis-cluster-6482.conf 

添加到集群中设置为从节点

./src/redis-cli -a xxxx --cluster add-node 120.xx.xx.171:6482 120.xx.xx.171:6382  -a xxxx --cluster-slave 382e8a64bca8a26e2efd8ed94b6733052cc84700
# 命令
redis-cli -a [password] --cluster add-node [new_host:new_port] [old_host:old_port] -a [password] --cluster-slave [old_id] 
-a [password] redis的密码
[new_host:new_port] 新的节点ip:port
[old_host:old_port] 集群中任一ip:port
[old_id] 如果没有ID则随机分配

查看集群信息

image

最后不要忘记修改nodes-6379.conf等几个文件的内容,把内网地址改成外网地址,重启集群

4.6 模拟删除一个节点

4.6.1 删除一个从节点

./src/redis-cli -a xxxx --cluster del-node 120.xx.xx.171:6482 80d479b32adc4d689ac4314b5d86487683ba0e24

查看集群状态

image

此时可以看到6482节点已经不在集群内了,再kill掉6482的进程就完成删除了

4.6.2 删除一个从节点

删除一个主节点会比较麻烦,因为主节点上存放着数据,如果直接删除会导致数据丢失,所以需要先将数据给转移到节点中

./src/redis-cli -a xxxx --cluster reshard 120.xx.xx.171:6382 --cluster-from 382e8a64bca8a26e2efd8ed94b6733052cc84700 --cluster-to 0bb8cef1c276292da7d3d25b49f1f95385028040 --cluster-slots 1356 --cluster-yes
# --cluster-from 表示从哪个节点转移
# --cluster-to 表示哪个节点接收
# --cluster-slots 1356 表示要移动的槽数,由于我们是删除节点,所以要全部移出去
# --cluster-yes 不需要确认直接移动

等待槽移动完毕,查看节点信息会发现原来的主节点已经变成了从节点,直接从集群中删除节点即可

image

如果移动之后发现三个节点的槽数相差过大,可以使用命令进行平均

./src/redis-cli -a xxxx --cluster rebalance 120.xx.xx.171:6380