=============java
若是须要存储海量的内存数据,若是只使用一台redis,没法保证redis工做的效率. 大量的时间都浪费到了寻址当中.因此须要一种机制可以知足该要求.
采用分片机制实现:node
Redis服务的启动须要依赖于redis.conf的配置文件. 若是须要准备3台redis.则须要准备3个redis.conf的配置.redis
准备端口号:
1.6379
2.6380
3.6381算法
修改端口号: 将各自的端口号进行修改.spring
启动3台redis服务器
校验服务器是否正常运行数据库
1.问题描述:
当启动多台redis服务器以后,多台redis暂时没有必然的联系,各自都是独立的实体.能够数据数据的存储.如图所示.
2.若是将分片经过程序的方式进行操做,要把3台redis当作一个总体,因此与上述的操做彻底不一样.不会出现一个key同时保存到多个redis的现象.json
`/** * 测试Redis分片机制 * 思考: shards 如何肯定应该存储到哪台redis中呢??? */ @Test public void testShards(){ List<JedisShardInfo> shards = new ArrayList<>(); shards.add(new JedisShardInfo("192.168.126.129",6379)); shards.add(new JedisShardInfo("192.168.126.129",6380)); shards.add(new JedisShardInfo("192.168.126.129",6381)); //准备分片对象 ShardedJedis shardedJedis = new ShardedJedis(shards); shardedJedis.set("shards","redis分片测试"); System.out.println(shardedJedis.get("shards")); }`
常识1: 通常的hash是8位16进制数. 0-9 A-F (24)8 = 2^32
常识2: 若是对相同的数据进行hash运算 结果必然相同的.
常识3: 一个数据1M 与数据1G的hash运算的速度一致.数组
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,可以尽量小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 [2] 。缓存
概念:平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题 [4] 。(大体平均)
问题描述: 因为节点都是经过hash方式进行算计.因此可能出现如图中的现象.,致使负载严重不平衡
解决方法: 引入虚拟节点服务器
特色: 单调性是指在新增或者删减节点时,不影响系统正常运行 [4] 。
谚语: 鸡蛋不要放到一个篮子里.
③分散性是指数据应该分散地存放在分布式集群中的各个节点(节点本身能够有备份),没必要每一个节点都存储全部的数据 [4]
`# 配置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`
`@Configuration @PropertySource("classpath:/properties/redis.properties") public class JedisConfig { @Value("${redis.nodes}") private String nodes; //node,node,node..... //配置redis分片机制 @Bean public ShardedJedis shardedJedis(){ nodes = nodes.trim(); //去除两边多余的空格 List<JedisShardInfo> shards = new ArrayList<>(); String[] nodeArray = nodes.split(","); for (String strNode : nodeArray){ //strNode = host:port String host = strNode.split(":")[0]; int port = Integer.parseInt(strNode.split(":")[1]); JedisShardInfo info = new JedisShardInfo(host, port); shards.add(info); } return new ShardedJedis(shards); } }`
优势: 实现内存数据的扩容.
缺点: 若是redis分片中有一个节点出现了问题.,则整个redis分片机制用户访问必然有问题 直接影响用户的使用.
解决方案: 实现redis高可用.
策略划分: 1主2从 6379主 6380/6381从
1.将分片的目录复制 更名位sentinel
原理说明:
1.配置redis主从的结构.
2.哨兵服务启动时,会监控当前的主机. 同时获取主机的详情信息(主从的结构)
3.当哨兵利用心跳检测机制(PING-PONG) 连续3次都没有收到主机的反馈信息则判定主机宕机.
4.当哨兵发现主机宕机以后,则开启选举机制,在当前的从机中挑选一台Redis当作主机.
5.将其余的redis节点设置为新主机的从.
1).复制配置文件
cp sentinel.conf sentinel/
2).修改保护模式
3).开启后台运行
4).设定哨兵的监控
其中的1 表示投票生效的票数 当前只有一个哨兵因此写1
5).修改宕机的时间
6).选举失败的时间
说明:若是选举超过指定的时间没有结束,则从新选举.
7).启动哨兵服务
测试步骤:
1.检查主机的状态
2.将redis主服务器宕机 等待10秒 以后检查从机是否当选新的主机
3.重启6379服务器., 检查是否成为了新主机的从.
`/** * 测试Redis哨兵 */ @Test public void testSentinel(){ Set<String> set = new HashSet<>(); //1.传递哨兵的配置信息 set.add("192.168.126.129:26379"); JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster",set); Jedis jedis = sentinelPool.getResource(); jedis.set("aa","哨兵测试"); System.out.println(jedis.get("aa")); }`
`# 配置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`
`@Configuration @PropertySource("classpath:/properties/redis.properties") public class JedisConfig { @Value("${redis.sentinel}") private String sentinel; //暂时只有单台 @Bean public JedisSentinelPool jedisSentinelPool(){ Set<String> sentinels = new HashSet<>(); sentinels.add(sentinel); return new JedisSentinelPool("mymaster",sentinels); } }`
`package com.jt.aop; import com.jt.anno.CacheFind; import com.jt.config.JedisConfig; import com.jt.util.ObjectMapperUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisSentinelPool; import redis.clients.jedis.ShardedJedis; import java.lang.reflect.Method; import java.util.Arrays; @Aspect //我是一个AOP切面类 @Component //将类交给spring容器管理 public class CacheAOP { @Autowired //private Jedis jedis; //单台redis //private ShardedJedis jedis; //分片机制 private JedisSentinelPool jedisSentinelPool; /** * 切面 = 切入点 + 通知方法 * 注解相关 + 环绕通知 控制目标方法是否执行 * * 难点: * 1.如何获取注解对象 * 2.动态生成key prekey + 用户参数数组 * 3.如何获取方法的返回值类型 */ @Around("@annotation(cacheFind)") //参数传递变量的传递 //@Around("@annotation(com.jt.anno.CacheFind)") public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){ //从池中获取jedis对象 Jedis jedis = jedisSentinelPool.getResource(); Object result = null; try { //1.拼接redis存储数据的key Object[] args = joinPoint.getArgs(); String key = cacheFind.preKey() +"::" + Arrays.toString(args); //2. 查询redis 以后判断是否有数据 if(jedis.exists(key)){ //redis中有记录,无需执行目标方法 String json = jedis.get(key); //动态获取方法的返回值类型 向上造型 向下造型 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Class returnType = methodSignature.getReturnType(); result = ObjectMapperUtil.toObj(json,returnType); System.out.println("AOP查询redis缓存"); }else{ //表示数据不存在,须要查询数据库 result = joinPoint.proceed(); //执行目标方法及通知 //将查询的结果保存到redis中去 String json = ObjectMapperUtil.toJSON(result); //判断数据是否须要超时时间 if(cacheFind.seconds()>0){ jedis.setex(key,cacheFind.seconds(),json); }else { jedis.set(key, json); } System.out.println("aop执行目标方法查询数据库"); } } catch (Throwable throwable) { throwable.printStackTrace(); } jedis.close(); //将使用完成的连接记得关闭. return result; } }`
1.预习 Redis集群搭建步骤2.了解redis集群工做原理