1.关于Spring整合Redis分片
分片----扩容
注意分片配置文件写法node节点,在进行遍历获取。
2. Redis哨兵机制 —高可用
注意高可用前提----主从挂载
哨兵脑裂 集群脑裂
3. Redis集群搭建----入门案例
宕机是计算机术语,口语里面我们简单的把停掉机器叫做down机,转换为汉字是“宕机”,但很多人都叫做“当机”/“死机”,虽然不规范但却流行。
宕机,指操作系统无法从一个严重系统错误中恢复过来,或系统硬件层面出问题,以致系统长时间无响应,而不得不重新启动计算机的现象。它属于电脑运作的一种正常现象,任何电脑都会出现这种情况。
Redis分片的主要的作用是实现内存数据的扩容,Redis分片如果宕机不能实现高可用!!!
Redis的分片的计算发生在业务服务器中,也就是tomact服务器. 将需要保存的数据存储到redis中.
Redis分片的执行的效率是最高的. 因为他把很多计算的规则放在别人家,而redis只负责存和取.所以效率高.
说明:
1).为了以后修改端口号方便,需要把ip+port写入到配置文件中,之后动态的注入到配置类即可。
2).如果像单台redis配置那样一条一条写太麻烦,直接通过nodes节点的方式写。
#添加redis的配置 #添加单台配置 #redis.host=192.168.126.129 #redis.port=6379 #配置redis分片机制 节点和节点区分加标示符","用来区分 nodes:节点 redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381
说明:把配置类中原来实现单台redis对象交给spring容器管理注释掉。现在实现分片对象交给spring容器去管理。
package com.jt.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import redis.clients.jedis.JedisShardInfo; import redis.clients.jedis.ShardedJedis; import java.util.ArrayList; import java.util.List; @Configuration //标识我是一个配置类 @PropertySource("classpath:/properties/redis.properties") public class JedisConfig {
@Value("${redis.nodes}") private String nodes; //node,node,node /** * 添加Redis分片的配置 * 需求1: 动态的获取IP地址/PORT端口 * 动态的获取多个的节点信息 . 方便以后扩展(即用配置文件的方式) */ @Bean public ShardedJedis shardedJedis(){
nodes = nodes.trim(); //去除两边多余的空格 List<JedisShardInfo> list = new ArrayList<>(); //按照','的方式进行拆分 拆分结果是数组 String[] strArray = nodes.split(","); //[node,node,node] for (String node : strArray){
//ip:port String host = node.split(":")[0]; //拆分后为string类型数字,不能用int类型来接受,把String类型的数字转化为int类型在接收。 int port = Integer.parseInt(node.split(":")[1]); list.add(new JedisShardInfo(host,port )); } return new ShardedJedis(list); } /* @Value("${redis.host}") private String host; @Value("${redis.port}") private Integer port; *//** * 将jedis对象交给spring容器管理 *//* @Bean public Jedis jedis(){
//由于将代码写死不利于扩展,所以将固定的配置添加到配置文件中 return new Jedis(host,port); }*/ }
说明:将AOP缓存中的注入项由单台改为分片对象,名字相同不用在改代码。
优点: 实现内存数据的扩容.
缺点: 如果redis分片中有一个节点出现了问题.,则整个redis分片机制用户访问必然有问题 直接影响用户的使用.
解决方案: 实现redis高可用.
问题分析: 如果需要实现Redis服务器的高可用,前提条件应该实现主从的配置.
原因:只有实现主从的配置,主机有数据同步给从库,将来主机宕机,使用从机(从机和主机数据一致),才能实现高可用的效果。
搭建规则:
1).先关闭Redis分片服务器.、
注意:可以直接通过进程关闭,也可以通过命令一个一个关闭。
2).复制shards文件为sentinel哨兵的目录
说明:现在要用3台服务器,和shards目录结构一样里面已经有3台redis了,所以只需要复制这个目录就行。
3).删除持久化文件
说明:如果不删除此时重新启动的读取的还是原先分片的持久化文件,后面没法测试。
4).运行4台Redis服务器
5).检查是否启动
1).检查主从的状态: info replication
,通过检查发现3台redis默认都是主机。
eg:
2). 实现主从的挂载 slaveof 主机IP 主机端口
说明:分别在80和81主机上进行挂载。
3).关于主从挂载的特点
检查从机中的状态, master:主机 slave:从机
检查主机的状态:, online:在线
说明: 可以看出redis中的主和从可以相互识别.
4).说明:操作redis缓存进行读写依然是主库redis,2个从库只是为了做备份,它不像数据库有读写分离的操作,也没有中间件。
说明:由于误操作可能导致主从的结构挂载异常.如何重新挂载呢???
答:可以将redis服务器全部关闭,之后重启 默认条件下的主从的挂载则会失效,之后重新挂载即可。(因为这些数据都在内存中储存,内存一关闭数据信息一释放就没有了。)
补充说明:由于slaveof指令在内存中生效.如果内存资源释放,则主从的关系将失效.为了实现永久有效,应该将主从的关系写在配置文件中即可.
新问题的产生: 如果主机意外宕机,则由谁来完成配置文件的修改呢? (配置文件不能写死)
如:
说明:
1).用户直接连接的是哨兵,哨兵连接的是主机。
2).主机宕机,从1当作主机,那么从2当作新主机的从机。
3).如果主机宕机,从机1变为主机,即使主机修好也只能当作新主机的从机.
4).分片解决的是扩容问题,哨兵解决的是高可用的问题,分片储存的是不同的数据,哨兵储存的是相同的数据。
哨兵工作原理说明:
1.配置redis主从的结构.
2.哨兵服务启动时,会监控当前的主机. 同时获取主机的详情信息(主从的结构)
3.当哨兵利用心跳检测机制(PING-PONG),检验主机是否正常.如果连续3次发现主机没有响应信息.则开始进行选举.
4.当哨兵发现主机宕机之后,则开启选举机制,在当前的从机中挑选一台Redis当做主机.
5.将其它的redis节点设置为新主机的从.
把哨兵配置文件复制到哨兵目录。
1).修改保护模式
2).开启后台运行
3).修改哨兵的监控为主机的ip,其中的1表示投票生效的数量.
一个哨兵写1,多个超半数即可,如:3个哨兵写2,5个哨兵写3。
4).哨兵宕机之后的选举时间
如果主机宕机10秒之后开始进行推选.
5…选举失败的时间
说明:如果选举超过指定的时间没有结束,则重新选举.
1).启动哨兵
[root@localhost sentinel]# redis-sentinel sentinel.conf
2).测试步骤:
1.检查主机的状态
2.将redis主服务器宕机 等待10秒 之后检查从机是否当选新的主机
3.重启6379服务器., 检查是否成为了新主机的从.
如果有多个哨兵进行选举,如果连续3次投票失败,可能引发脑裂现象的发生.
问题: 脑裂现象发生的概率是多大?? 1/8 = 12.5%
解决策略: 只要增加选举的节点的数量,可以有效的降低脑裂现象的发生. 概率论
脑裂:
redis的主从模式下脑裂是指因为网络问题,导致redis master节点跟redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到 master 的存在,就会将某一个 slave 节点提升为 master 节点。此时就存在两个不同的 master节点,就像一个大脑分裂成了两个。
集群脑裂问题中,如果客户端还在基于原来的 master 节点继续写入数据,那么新的master 节点将无法同步这些数据,当网络问题解决之后,sentinel 集群将原先的master节点降为 slave 节点,此时再从新的 master 中同步数据,将会造成大量的数据丢失。
2个哨兵才容易出现脑裂现象,因为每次投的票相同,选举失败. 3个不可停票.
参数说明:
哨兵连接池对象第一个参数,主机的变量名称
。在哨兵的配置文件中。第二个参数为String类型的set集合。
/** * 哨兵的测试 * 参数说明: masterName: 主机的变量名称 * sentinels: 链接哨兵的集合. * 理解: 哨兵虽然链接3台redis. 但是3台redis中存储的数据都是一样的.对于用户而言 * 就是一台. */ @Test public void testSentinel(){
Set<String> sets = new HashSet<>(); //1.传递哨兵的配置信息 sets.add("192.168.126.129:26379");//26379哨兵的默认端口号 //创建哨兵连接池对象 JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster",sets); //获取池里面的资源文件,因为哨兵将来连接的是某台redis进程操作,具体工作的redis是单台。 Jedis jedis = sentinelPool.getResource(); jedis.set("aaa", "哨兵测试"); System.out.println(jedis.get("aaa")); jedis.close(); //用完之后关闭 }
检索,并关闭哨兵 kill
# 配置redis单台服务器 redis.host=192.168.126.129 redis.port=6379 # 配置redis分片机制 redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381 # 配置哨兵节点 如果哨兵有多台按照分片的写法 redis.sentinel=192.168.126.129:26379
package com.jt.config; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.JedisSentinelPool; import java.util.HashSet; import java.util.Set; @Configuration //标识我是一个配置类 @PropertySource("classpath:/properties/redis.properties") public class JedisConfig {
@Value("${redis.sentinel}") private String sentinel;//192.168.126.129:26379 @Bean //@Scope("prototype") public JedisSentinelPool jedisSentinelPool(){
Set<String> sentinels = new HashSet<>(); sentinels.add(sentinel); //控制池中连接的数量 不写按照默认的 JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMaxTotal(100);//连接池 最大链接数量 jedisPoolConfig.setMaxIdle(40);//最大空闲数量 jedisPoolConfig.setMinIdle(20);//最小空闲数量 /** * 思考:为什么返回连接池对象而不是当个的redis对象? * 因为如果返回的是单个对象,相当于以后共用都是一个连接对象 不好。 * 解决:1,直接返回连接池对象。 * 2,返回redis单台对象,加上@Scope("prototype")创建多例对象,虽然这样可以解决, * 但是这样每次创建的是连接池对象,速度会降低,所以最优化写法为直接返回池对象。 */ return new JedisSentinelPool("mymaster",sentinels,jedisPoolConfig); } }
测试:
分片:
1.主要的作用实现内存数据的扩容.
2.由于运算发生在业务服务器中,所以执行的效率更高.
3.Redis的分片没有高可用的效果. 如果其中一个节点出现了问题则导致程序运行出错.
哨兵机制:
1.实现Redis高可用,当redis服务器发生宕机的现象时,哨兵可以灵活的监控.实现自动的选举实现 故障的迁移.
2.哨兵中所监控的redis节点中的数据都是相同的. 无法实现海量的数据存储.
3.哨兵虽然可以实现redis的高可用,但是由于哨兵的本身没有实现高可用.所以存在风险.
如果想要最大程度上减少损耗,则建议不要引入第三方的监控
升级:
需要Redis内容扩容同时需要Redis高可用性所以应该使用Redis集群.
说明:Redis集群搭建步骤 参见课前资料的文档.
集群就是把高可用和扩容结合起来了.
redis集群可以互相监控。
注意:搭建集群前提redis里面没有数据,因为redis集群中的数据需要按照特定的算法进行储存,如果没有计算就进行存储里面的算法和redis集群储存的算法不一致,所以报错。
前提条件: redis.conf的配置文件首先应该配置正确 码云中有redis配置.
搭建步骤:
#启动redis sh start.sh #集群挂载 redis-cli --cluster create --cluster-replicas 1 192.168.126.129:7000 192.168.126.129:7001 192.168.126.129:7002 192.168.126.129:7003 192.168.126.129:7004 192.168.126.129:7005
1).检查redis主机的状态
2).将主机关闭
redis-cli -p 7000 shutdown
3).检查主机是否切换
4).重启7000服务器.检查是否为7003的从
exit退出.
/** * redis集群的入门案例: * jedisCluster 操作整个redis集群,链接redis的所有的节点 * 思考:key 如何保存到redis中的3台主机??? 通过算法。 * redis分区算法??? */ @Test public void testCluster(){
Set<HostAndPort> sets = new HashSet<>(); sets.add(new HostAndPort("192.168.126.129", 7000)); sets.add(new HostAndPort("192.168.126.129", 7001)); sets.add(new HostAndPort("192.168.126.129", 7002)); sets.add(new HostAndPort("192.168.126.129", 7003)); sets.add(new HostAndPort("192.168.126.129", 7004)); sets.add(new HostAndPort("192.168.126.129", 7005)); //集群对象,要求传值HostAndPort类型的set集合 JedisCluster jedisCluster = new JedisCluster(sets); jedisCluster.set("cluster", "集群测试");//存值 System.out.println(jedisCluster.get("cluster"));//取值 }
原则: Redis的内存缺失则集群崩溃
宕机条件:如果不能维持主节点的个数则集群崩溃。即:集群中如果主机宕机,那么从机可以继续提供服务,当主机中没有从机时,则向其它主机借用多余的从机.继续提供服务.如果主机宕机时没有从机可用,则集群崩溃.
A. 1台 B.2台 C.3台 D.4台
A. 3台 B.4台 C.5台 D.6台 **说明: 如果没有子节点 则会借用其他主机的多余的从.**
说明: 当集群进行选举时,如果连续3次都出现了平票的结果的则可能出现脑裂的现象.
问题: 出现脑裂现象的概率是多少??? 1/8
数学建模:
抛银币连续3次出现平票的概念是多少? 1/8=12.5%
第一次: 正正 正反 反正 反反 1/2
第二次: 正正 正反 反正 反反 1/2
第三次: 正正 正反 反正 反反 1/2
预防: 增加主节点的数量可以有效的降低脑裂现象的发生.