springboot-分布式实例开发 (九)-Redis(续)


上一篇文章主要围绕着redis的持久化,过期策略和缓存清除策略进行讲解。这一节来介绍redis的分片机制,哨兵机制以及集群的搭建。

1.Redis分片机制

说明:将多台redis搭建成分片的结构.实现内存的扩容.
 如果没有分片机制,Redis就被局限于单机所支持的内存容量。Redis的分片机制允许数据拆分存放在不同的Redis实例上,每个Redis实例只包含所有键的子集。可以减轻单台Redis的压力,提升Redis扩展能力和计算能力。如果我们只使用一个Redis实例,当Redis宕机将会直接停止服务,所以我们可以采取分片机制,将原本一台Redis实例维护的数据,改为由多个Redis实例共同维护这部分数据。
 说白了就是更好的维护数据,提高响应效率。

1.1 分片实现

1.在redis根目录中创建shards文件
在这里插入图片描述
2. 分别复制redis.conf配置文件成3个

cp redis.conf shards/redis-6379.conf
cp redis.conf shards/redis-6380.conf
cp redis.conf shards/redis-6381.conf

在这里插入图片描述
3.修改端口号
根据配置文件的名称,修改各自的端口号。修改位置在92行
在这里插入图片描述
4.启动多台redis

redis-server redis-6379.conf
redis-server redis-6380.conf
redis-server redis-6381.conf

先关闭原先打开的redis,再打开刚配置的三个redis服务
在这里插入图片描述OK。我们实验一下

/**
 * redis分片测试
 */
@Test
public void testShards() {
   String host = "192.168.180.160";
   List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
   shards.add(new JedisShardInfo(host, 6379));
   shards.add(new JedisShardInfo(host, 6380));
   shards.add(new JedisShardInfo(host, 6381));
   ShardedJedis jedis = new ShardedJedis(shards);
   jedis.set("0419", "分片操作");
   System.out.println(jedis.get("0419"));
}

那这个key为0419的值放在了哪个服务上了呢?
我们去看一下。分别连接上redis的三个服务。去找一下
在这里插入图片描述
我这里是放在了6381 的服务上
在这里插入图片描述

1.2 分片的原理(了解)

看到这朋友可能想知道他是如何进行实现的?这里说明一下
redis采用了一个算法叫做哈希一致性算法
它所解决的正是数据存储一致性问题
一致性Hash算法使用取模的方法,一致性Hash算法是对 ( 2 32 ) \left(2^{32}\right) (232)取模,什么意思呢?简单来说,一致性Hash算法将整个哈希值空间组织成一个虚拟的圆环,如假设某哈希函数H的值空间为0~ ( 2 32 ) \left(2^{32}\right) (232)-1(即哈希值是一个32位无符号整形)
在这里插入图片描述
整个空间按顺时针方向组织,圆环的正上方的点代表0,0点右侧的第一个点代表1,以此类推,2、3、4、5、6……直到 ( 2 32 ) \left(2^{32}\right) (232)-1,也就是说0点左侧的第一个点代表 ( 2 32 ) \left(2^{32}\right) (232)-1, 0和 ( 2 32 ) \left(2^{32}\right) (232)-1在零点中方向重合,我们把这个由 ( 2 32 ) \left(2^{32}\right) (232)个点组成的圆环称为Hash环
这里我们有3个redis服务器,服务器A、服务器B、服务器C,那么,在生产环境中,这3台服务器肯定有自己的IP地址或主机名,我们使用它们各自的IP地址或主机名作为关键字进行哈希计算,使用哈希后的结果对2^32取模,可以使用如下公式示意:

hash(服务器IP地址) %  2^32

就会分别得到三个服务的哈希值。并将其服务映射到哈希环上。示范如下:
在这里插入图片描述
接下来将数据key使用相同的函数Hash计算出哈希值,并确定此数据在环上的位置,从此位置沿环顺时针“行走”,第一台遇到的服务器就是其应该定位到的服务器!
在这里插入图片描述
但这样也会暴露了一个问题。由于key的不确定性。那就可能会出现数据倾斜的问题。意思就是有的redis服务器数据偏多,而有的redis服务器数据偏少。也就出现了负载均衡不均的问题。所以这就不符合咱们的初衷了。

不过redis已经想到了这一点。redis又借此提出了虚拟节点的概念:再次 对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器IP或主机名的后面增加编号来实现。
接下来再为每台服务器计算2个虚拟节点,于是可以分别计算 “Node 6079#1”、“Node 6079#2”、“Node 6080#1”、“Node 6080#2”、“Node 6081#1”、“Node 6081#2”的哈希值,于是形成六个虚拟节点:
在这里插入图片描述当key的哈希值对应好虚拟节点后就会映射到真实的redis服务器上。
但是增加虚拟节点还是依然存在hash倾斜的问题,但是随着虚拟节点的大量增加,就会使得出现hash命中失败的现象降到最低。一致性hash算法公式:1-n/(n + m) * 100% n表示真实的redis节点,m表示增加的虚拟节点,可以看到随着虚拟节点的增加,命中失败越来越低。

1.3 分片机制分析

分片是由多台Redis实例共同运转,所以如果其中一个Redis实例宕机,则整个分片都将无法使用,所以分片机制无法实现高可用。
如果有不同的key映射到不同的Redis实例,这时候不能对这两个key做交集或者使用事务。
使用分片机制因为涉及多实例,数据处理比较复杂。

2.Redis哨兵机制

刚分析了若使用分片机制的话可以实现内存的扩容.但是如果某个redis节点宕机.则导致分片整体不能使用.采用redis高可用的形式.实现redis可持续性.

2.1 主从复制

哨兵的前提是主从机的存在。介绍如下:

  1. redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave),主数据库可以进行读写操作,当发生写操作的时候自动将数据同步到从数据库,而从数据库一般是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库,而一个从数据库只能有一个主数据库。
  2. 通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作。

2. 2 哨兵策略

Redis的哨兵是用于管理多个redis服务。
它一共做三个工作:

  1. 监控(Monitoring): 哨兵(sentinel) 会不断地检查你的Master(主机)和Slave(从机)是否运作正常。
  2. 提醒(Notification):当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知。
  3. 自动故障迁移(Automatic failover):当一个Master不能正常工作时,哨兵(sentinel) 会开始一次自动故障迁移操作,它会将失效Master的其中一个Slave升级为新的Master, 并让失效Master的其他Slave改为复制新的Master; 当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用Master代替失效Master。
    在这里插入图片描述工作流程:
    1.当哨兵启动时,会根据配置文件信息监控主机master.同时获取主机的状态信息(包含主从结构).
    2.哨兵利用心跳检测机制(PING-PONG)时常校验主机是否存活.当连续3次发现主机没有响应.说明主机可能宕机.
    3.哨兵根据获取的信息开始选举新的主机.同时修改其他服务器的主从配置.

2.3 哨兵配置

1.搭建主从同步
1).关闭分片所有redis服务器.
在这里插入图片描述
2). 复制分片信息

 cp -r shards sentinel 

3). 进入sentinel文件中删除持久化文件dump.rdb。(shards目录在redis根目录下)
在这里插入图片描述4)启动此目录下的三个redis服务
在这里插入图片描述
2检查主从状态:默认条件下所有的节点都是主机.

info replication

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3 实现主从挂载
规划: 6379主机 6380/6381从机
命令: slaveof IP port

SLAVEOF 192.168.180.160 6379

分别将6380/6381挂载到6379中
在这里插入图片描述
在这里插入图片描述
关于主从说明:
主机与从机可以互相通信.只有主机可以修改数据,从机只能同步数据.不能修改.

2.4 哨兵搭建

回到redis根目录,复制哨兵配置文件到sentinel目录下

cp sentinel.conf sentinel

在这里插入图片描述并编辑此文件
关闭保护模式
在这里插入图片描述
开启后台启动
在这里插入图片描述
配置哨兵监控

sentinel monitor mymaster 127.0.0.1 6379 1

mymaster:主机的变量名称
最后的参数2: 几票生效. 一般哨兵超过半数投票生效.(这里改为1.因为一共三个。若主机宕机了,就剩下俩个。本着选举奇数原则。只有有一个投票,就可以从机变为主机。避免“投票出现平局”的情况)
在这里插入图片描述
修改哨兵宕机选举时间
113行
在这里插入图片描述
修改推选失败的超时时间

在这里插入图片描述
退出编辑状态,并保存退出

2.5 哨兵高可用测试

  1. 启动哨兵
redis-sentinel sentinel.conf
  1. 关闭主节点6379
  2. 检查哨兵推选状态。
  3. 检查新的主从结构是否正常.
    在这里插入图片描述可以看到我关掉主机6379后。主机变成了6381.因此6380 的主机就成了6381.
    那要是6379重启了呢?
    看效果。刚重启了后,要稍等片刻才能看到这个。我们上面设置了选举的时间。故也许不能马上看到效果
    在这里插入图片描述
    测试成功

2.6 哨兵重置说明

有时候因误操作,会导致哨兵机制失败。那就需要重置
删除哨兵配置文件最后的配置
在这里插入图片描述
删除后需要重新按照步骤进行设置一下。主要修改的其实是哨兵检测的主机。误操作可能会导致
检测的主机发生变化。所以重置的时候需要检查一下。
在这里插入图片描述
改好好。直接关闭所有redis服务。重启即可。

2.4 分片哨兵总结

分片特点:
优点:

  1. 可以实现redis内存扩容
  2. redis分片效率是最高的(哨兵/集群)

缺点:
3. 如果节点宕机.则整个分片不能使用

哨兵特点:
优点:

  1. 可以实现redis高可用.

缺点:

  1. 哨兵不能实现内存扩容.
  2. 哨兵本身不能实现高可用.

2.5 实例测试

/**
* 测试哨兵
*/
@Test
public void testSentinel() {
	Set<String> sentinels = new HashSet<>();
	sentinels.add("192.168.180.160:26379");
	JedisSentinelPool pool =new JedisSentinelPool("mymaster", sentinels) ;
	Jedis jedis = pool.getResource();
	jedis.set("0420", "测试哨兵!!!!");
	System.out.println("获取数据:"+jedis.get("0420"));
}

运行,看效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到主机和从机都有数据。主机的数据一旦删除,那从机的数据也会自动删除。
我们来拿我们的项目试验一下
首先更改redis配置文件。nodes是放的哨兵的服务。不要写错

#配置redis哨兵
redis.masterName=mymaster
redis.nodes=192.168.180.160:26379

在这里插入图片描述
RedisConfig修改:
将原有的都注释,添加新的内容

 /**
     * 配置哨兵
     */
    @Value("${redis.masterName}")
    private String masterName;
    @Value("${redis.nodes}")
    private String nodes;
    @Bean("jedisSentinelPool")
    public JedisSentinelPool jedisSentinelPool() {
        Set<String> sentinels = new HashSet<>();
        sentinels.add(nodes);
        return new JedisSentinelPool(masterName, sentinels);
    }
    @Bean
    public Jedis jedis(@Qualifier("jedisSentinelPool") JedisSentinelPool jedisSentinelPool) {
        return jedisSentinelPool.getResource();
    }

修改AOP注入属性
在这里插入图片描述
重启服务,看效果
在这里插入图片描述
在这里插入图片描述

3.Redis 集群

从上面我们可以看到无论是分片还是哨兵都是针对一个主redis服务器。分片(主从模式)下不能实现高可用,有一个宕机,整个就完蛋。哨兵模式呢也有一个问题,不能扩容啊。
这里采用集群的形式。
 redis cluster是Redis的分布式解决方案,在3.0版本推出后有效地解决了redis分布式方面的需求
 自动将数据进行分片,每个master上放一部分数据提供内置的高可用支持,部分master不可用时,还是可以继续工作的
 支撑N个redis master node,每个master node都可以挂载多个slave node
 高可用,因为每个master都有salve节点,那么如果mater挂掉,redis cluster这套机制,就会自动将某个slave切换成master。
 通俗的说就是部署多个主从redis服务器。实现redis扩容以及高可用问题。

3.1 集群搭建

我们先来操作一下进行理解。将原有的redis服务全部关闭
在这里插入图片描述
准备
主从划分:3台主机 3台从机共6台 端口划分7000-7005
回到redis跟目录,创建目录cluster

mkdir cluster

在这里插入图片描述
在cluster文件夹中分别创建7000-7005文件夹
在这里插入图片描述
复制配置文件
将redis根目录中的redis.conf文件复制到cluster/7000/目录下 并以原名保存

cp redis.conf cluster/7000/

编辑配置文件 ,使用vim [文件名] 命令。此命令会在右下角部分显示行数

  1. 注释本地绑定IP地址
    在这里插入图片描述
  2. 关闭保护模式
    在这里插入图片描述
  3. 修改端口号
    在这里插入图片描述
  4. 启动后台启动
    在这里插入图片描述
  5. 修改pid文件 。修改目录为自己的目录
    在这里插入图片描述
  6. 修改持久化文件路径
    在这里插入图片描述
  7. 设定内存优化策略
    在这里插入图片描述
  8. 关闭AOF模式
    在这里插入图片描述
  9. 开启集群配置
    在这里插入图片描述
  10. 开启集群配置文件,修改名字
    在这里插入图片描述
  11. 修改集群超时时间
    在这里插入图片描述

复制修改后的配置文件
说明:将7000文件夹下的redis.conf文件分别复制到7001-7005中

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

批量修改
说明:分别将7001-7005文件中的7000改为对应的端口号的名称,修改时注意方向键的使用
使用vim进入文件后,直接输入:%s/7000/7001/g。切记不要进入编辑状态
在这里插入图片描述
回车后,再次输入:wq。保存
依次修改其他文件。改成各自的端口号

创建启动脚本 vim start.sh

#!/bin/sh
redis-server 7000/redis.conf &
redis-server 7001/redis.conf &
redis-server 7002/redis.conf &
redis-server 7003/redis.conf &
redis-server 7004/redis.conf &
redis-server 7005/redis.conf &

编辑关闭的脚本 vim shutdown.sh

#!/bin/sh
redis-cli -p 7000 shutdown &
redis-cli -p 7001 shutdown &
redis-cli -p 7002 shutdown &
redis-cli -p 7003 shutdown &
redis-cli -p 7004 shutdown &
redis-cli -p 7005 shutdown &

启动redis节点

sh start.sh

检查redis节点启动是否正常。可以看到都启动成功
在这里插入图片描述
创建redis集群
#5.0版本执行 使用C语言内部管理集群

redis-cli --cluster create --cluster-replicas 1 192.168.180.160:7000 192.168.180.160:7001 192.168.180.160:7002 192.168.180.160:7003 192.168.180.160:7004 192.168.180.160:7005

在这里插入图片描述在这里插入图片描述
标记的是哈希槽。这个一会解释

3.2 Redis集群高可用测试

  1. 关闭redis主机.检查是否自动实现故障迁移.
  2. 再次启动关闭的主机.检查是否能够实现自动的挂载.
    一般情况下 能够实现主从挂载
    个别情况: 宕机后的节点重启,可能挂载到其他主节点中(7001-7002) 正确的
    在这里插入图片描述

3.3 集群原理

刚才操作了一下,那他具体是怎么工作的呢?
在这里插入图片描述
Redis的所有节点都会保存当前redis集群中的全部主从状态信息.并且每个节点都能够相互通信.当一个节点发生宕机现象.则集群中的其他节点通过PING-PONG检测机制检查Redis节点是否宕机.当有半数以上的节点认为宕机.则认为主节点宕机.同时由Redis剩余的主节点进入选举机制.投票选举链接宕机的主节点的从机.实现故障迁移.

Redis 集群没有并使用传统的一致性哈希来分配数据,而是采用另外一种叫做哈希槽 (hash slot)的方式来分配的。redis cluster 默认分配了 16384 个slot,当我们set一个key 时,会用CRC16算法来取模得到所属的slot,然后将这个key 分到哈希槽区间的节点上,具体算法就是:CRC16(key) % 16384。所以我们在测试的时候看到set 和 get 的时候,直接跳转到了7000端口的节点。

Redis 集群会把数据存在一个 master 节点,然后在这个 master 和其对应的salve 之间进行数据同步。当读取数据时,也根据一致性哈希算法到对应的 master 节点获取数据。只有当一个master 挂掉之后,才会启动一个对应的 salve 节点,充当 master 。

需要注意的是:必须要3个或以上的主节点,否则在创建集群时会失败,并且当存活的主节点数小于总节点数的一半时,整个集群就无法提供服务了。

3.4 Redis集群宕机条件

特点:集群中如果主机宕机,那么从机可以继续提供服务,
当主机中没有从机时,则向其它主机借用多余的从机.继续提供服务.如果主机宕机时没有从机可用,则集群崩溃.
答案:9个redis节点,节点宕机5-7次时集群才崩溃.
在这里插入图片描述

3.5 Redis hash槽存储数据原理

说明: RedisCluster采用此分区,所有的键根据哈希函数(CRC16[key]&16383)映射到0-16384槽内,共16384个槽位,每个节点维护部分槽及槽所映射的键值数据.根据主节点的个数,均衡划分区间.
算法 :哈希函数: Hash()=CRC16[key]&16384按位与
在这里插入图片描述
当向redis集群中插入数据时,首先将key进行计算.之后将计算结果匹配到具体的某一个槽的区间内,之后再将数据set到管理该槽的节点中.
在这里插入图片描述

3.6 spring整合集群入门案例

 @Test
    public void testCluster() {
        Set<HostAndPort> nodes = new HashSet<>();
        nodes.add(new HostAndPort("192.168.180.160", 7000));
        nodes.add(new HostAndPort("192.168.180.160", 7001));
        nodes.add(new HostAndPort("192.168.180.160", 7002));
        nodes.add(new HostAndPort("192.168.180.160", 7003));
        nodes.add(new HostAndPort("192.168.180.160", 7004));
        nodes.add(new HostAndPort("192.168.180.160", 7005));

        JedisCluster cluster = new JedisCluster(nodes);
        cluster.set("0421", "redis集群搭建完成!!!!");
        System.out.println(cluster.get("0421"));
    }

在这里插入图片描述

3.7 项目应用

编辑Pro文件。注意是自己虚拟机的ip。

#配置redis集群
redis.nodes=192.168.180.160:7000,192.168.180.160:7001,192.168.180.160:7002,192.168.180.160:7003,192.168.180.160:7004,192.168.180.160:7005

编辑配置类.将上节的哨兵的配置注释掉。换成集群的相关配置

@Configuration
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {
    @Value("${redis.nodes}")
    private String nodes;

    @Bean
    public JedisCluster jedisCluster() {
        Set<HostAndPort> nodes = getNodes();
        return new JedisCluster(nodes);
    }

    private Set<HostAndPort> getNodes() {
        Set<HostAndPort> set = new HashSet<>();
        String[] nodesArray = nodes.split(",");	//获取node数组
        for (String node : nodesArray) {
            String host = node.split(":")[0];
            int port = Integer.parseInt(node.split(":")[1]);
            set.add(new HostAndPort(host, port));
        }
        return set;
    }
}

修改AOP属性注入
在这里插入图片描述
重启服务
我这出现了原来serviceImpl引用的Jedis错误。我们将此涉及到的方法注释到就可以了。
在这里插入图片描述在这里插入图片描述
查看缓存
在这里插入图片描述可以看到我这是7002,7003是放了缓存,而其他没有。这俩谁是主机和从机呢。我们去看看
在这里插入图片描述
在这里插入图片描述

github

https://github.com/lmy1965673628/jingtao.git

总结

这一节围绕着redis的核心技术分片,哨兵,集群进行讲解。
除了这些,redis还在单点登录,授权验证等业务还有重要作用,往后的教程将逐步进行讲解。
敬请期待。。。。。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值