1. Redis分片机制
1.1 为什么需要分片机制
如果需要存储海量的内存数据,如果只使用一台redis,无法保证redis工作的效率. 大量的时间都浪费到了寻址当中.所以需要一种机制能够满足该要求.
采用分片机制实现:
1.2 Redis分片搭建
1.2.1 搭建注意事项
Redis服务的启动需要依赖于redis.conf的配置文件. 如果需要准备3台redis.则需要准备3个redis.conf的配置.
准备端口号:
1.6379
2.6380
3.6381
1.2.2 分片实现
修改端口号: 将各自的端口号进行修改.
启动3台redis服务器
校验服务器是否正常运行
1.2.3 关于分片的注意事项
1.问题描述:
当启动多台redis服务器之后,多台redis暂时没有必然的联系,各自都是独立的实体.可以数据数据的存储.如图所示.
2.如果将分片通过程序的方式进行操作,要把3台redis当做一个整体,所以与上述的操作完全不同.不会出现一个key同时保存到多个redis的现象.
1.3 分片入门案例
@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.4 一致性hash算法
1.4.0 常识说明
常识1: 一般的hash是8位16进制数. 0-9 A-F (24)8 = 2^32
常识2: 如果对相同的数据进行hash运算 结果必然相同的.
常识3: 一个数据1M 与数据1G的hash运算的速度一致.
1.4.1 一致性hash算法介绍
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 [2] 。
1.4.2 特性1-平衡性
概念:平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题 [4] 。(大致平均)
问题描述: 由于节点都是通过hash方式进行算计.所以可能出现如图中的现象.,导致负载严重不平衡
解决方法: 引入虚拟节点
1.4.3 特性2-单调性
特点: 单调性是指在新增或者删减节点时,不影响系统正常运行 [4] 。
1.4.4 特性3-分散性
谚语: 鸡蛋不要放到一个篮子里.
③分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据 [4]
1.5 SpringBoot整合Redis分片
1.5.1 编辑配置文件
# 配置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
1.5.2 编辑配置类
@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);
}
}
1.5.3 修改AOP注入项
2 Redis哨兵机制
2.1 关于Redis分片说明
优点: 实现内存数据的扩容.
缺点: 如果redis分片中有一个节点出现了问题.,则整个redis分片机制用户访问必然有问题 直接影响用户的使用.
解决方案: 实现redis高可用.
2.2 配置redis主从的结构
策略划分: 1主2从 6379主 6380/6381从
1.将分片的目录复制 改名位sentinel
- 重启三台redis服务器
3.检查redis节点的主从的状态
4.实现主从挂载
5.检查主机的状态
2.3 哨兵的工作原理
原理说明:
1.配置redis主从的结构.
2.哨兵服务启动时,会监控当前的主机. 同时获取主机的详情信息(主从的结构)
3.当哨兵利用心跳检测机制(PING-PONG) 连续3次都没有收到主机的反馈信息则断定主机宕机.
4.当哨兵发现主机宕机之后,则开启选举机制,在当前的从机中挑选一台Redis当做主机.
5.将其他的redis节点设置为新主机的从.
2.4 编辑哨兵配置文件
1).复制配置文件
cp sentinel.conf sentinel/
2).修改保护模式
3).开启后台运行
4).设定哨兵的监控
其中的1 表示投票生效的票数 当前只有一个哨兵所以写1
5).修改宕机的时间
6).选举失败的时间
说明:如果选举超过指定的时间没有结束,则重新选举.
7).启动哨兵服务
2.5 Redis哨兵高可用实现
测试步骤:
1.检查主机的状态
2.将redis主服务器宕机 等待10秒 之后检查从机是否当选新的主机
3.重启6379服务器., 检查是否成为了新主机的从.
2.6 哨兵入门案例
@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"));
}
2.7 SpringBoot整合Redis哨兵 (10分钟)
2.7.1 编辑pro配置文件
# 配置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
2.7.2 编辑redis配置类
@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);
}
}
2.7.3 修改CacheAOP中的注入项
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;
@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集群工作原理