一、Redis 分片机制阐明
1. 一致性 hash 算法
1.1 一致性 Hash 算法介绍
一致性哈希算法在 1997 年由麻省理工学院提出,是一种非凡的哈希算法,目标是解决分布式缓存的问题。[1] 在移除或者增加一个服务器时,可能尽可能小地扭转已存在的服务申请与解决申请服务器之间的映射关系。一致性哈希解决了简略哈希算法在分布式哈希表(Distributed Hash Table,DHT) 中存在的动静伸缩等问题 [2]。
1.2 一致性 Hash 原理阐明
常识:
1). 对雷同的数据 hash 失去的后果雷同
2). 常见 hash 值 8 位 16 进制数 2^32 种可能性
1.3 平衡性阐明
阐明: 平衡性是指 hash 的后果应该平均分配到各个节点,这样从算法上解决了负载平衡问题 .
如果发现数据分布不平均, 则采纳虚构节点的形式, 实现数据的均衡. 如果一次均衡不能达到目标则屡次生成虚构节点. 然而数据均衡没有方法做到相对的均匀.
1.4 枯燥性阐明
枯燥性是指在新增或者删减节点时,不影响零碎失常运行
如果新增或者删除节点, 则尽可能不要扭转原有的数据结构.
1.5 分散性
分散性是指数据应该扩散地寄存在分布式集群中的各个节点 (节点本人能够有备份),不用每个节点都存储所有的数据
鸡蛋不要放到一个篮子里.
二、redis 分片机制配置
1. redis 性能优化
阐明: 单台 redis 内存容量是无限的. 然而如果有海量的数据要求实现缓存存储, 则应该应用多个 Redis 节点.
2. Redis 分片机制定义
3. 配置布局
阐明: 筹备 3 台 redis 服务器 别离为 6379/6380/6381
3.1 筹备 3 个配置文件
批改各自端口号 改为 6380 6381
3.2 启动 redis 服务器
3.3 查看 redis 启动状态
3.4 redis 分片入门案例
4. Redis 分片机制配置
阐明: 依据 redis 节点个数. 拼接字符串
# 配置单台 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`
4.1 编辑 RedisConfig 配置类
@Configuration // 标识我是一个配置类 个别与 @Bean 注解联用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {@Value("${redis.nodes}")
private String nodes; //node,node,node
@Bean
public ShardedJedis shardedJedis(){List<JedisShardInfo> shards = new ArrayList<>();
String[] nodeArray = nodes.split(",");
for(String node :nodeArray){ //node=host:port
String host = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
JedisShardInfo info = new JedisShardInfo(host,port);
shards.add(info);
}
return new ShardedJedis(shards);
}
}
4.2 批改 CacheAOP 中的注入
二、Redis 哨兵机制
1. Redis 分片机制问题
阐明: 如果 redis 分片中有一个节点宕机, 则可能会影响整个服务的运行. redis 分片没有实现高可用.
2. Redis 主从构造搭建
2.1 复制配置文件
2.2 筹备 3 台 redis
2.3 主从搭建
命令 1: info replication
命令 2: slaveof IP PORT 主从挂载命令
查看主从构造状态
对于主从构造阐明:
主和从都晓得 以后的主从的状态, 并且只有主机能够写操作. 从机只能读操作.
3. Redis 哨兵机制工作原理
1). 当哨兵启动时, 首先会监控主机, 从主机中获取以后所有的节点的状态, 同时哨兵开启心跳检测机制.
2). 当主机产生宕机的景象时, 因为哨兵有 PING-PONG 机制 发现主机宕机, 则哨兵开始进行选举.
3). 当选举胜利之后, 新的主机入选之后, 其余的节点当新主机的从.
3.1 编辑哨兵配置文件
1)敞开保护模式
2). 开启后盾运行
3). 编辑监控配置
4). 批改选举工夫
3.2 启动哨兵服务
命令: redis-sentinel sentinel.conf
高可用查看:
1). 先敞开主机 期待 10 秒查看是否进行选举.
2). 启动 6379. 查看是否入选新主机的从.
3.3 哨兵测试 API
/**
* 测试哨兵
*/
@Test
public void testSentinel(){Set<String> set = new HashSet<>();
set.add("192.168.126.129:26379");
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(1000);
poolConfig.setMaxIdle(40);
poolConfig.setMinIdle(10);
JedisSentinelPool sentinelPool =
new JedisSentinelPool("mymaster",set,poolConfig);
Jedis jedis = sentinelPool.getResource();
jedis.set("sentinel", "哨兵机制测试");
System.out.println(jedis.get("sentinel"));
jedis.close();}
4.springBoot 整合哨兵机制
4.1 编辑 pro 配置文件
4.2 编辑配置类
4.3 编辑 CacheAOP
4.4 对于分片 / 哨兵总结
1.Redis 分片次要的作用实现内存的扩容, 毛病: 没有实现高可用的成果.
2.Redis 哨兵次要的作用实现了 redis 节点高可用. 毛病: 没有实现内存扩容
Redis 哨兵机制本质就是引入第三方的监控, 然而须要保障第三方的高可用. 就必须引入更多的资源.
三、Redis 集群
参见搭建文档.
1. 集群搭建谬误解决方案
注意事项: redis 集群搭建要求节点的数据必须为 null.
1). 将所有的 redis 节点全副敞开 sh stop.sh
2). 删除多余的文件
3). 重启 redis 集群
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
2 Redis 集群入门案例
@Test
public void testCluster(){Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.126.129", 7000));
nodes.add(new HostAndPort("192.168.126.129", 7001));
nodes.add(new HostAndPort("192.168.126.129", 7002));
nodes.add(new HostAndPort("192.168.126.129", 7003));
nodes.add(new HostAndPort("192.168.126.129", 7004));
nodes.add(new HostAndPort("192.168.126.129", 7005));
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(1000);
poolConfig.setMaxIdle(40);
poolConfig.setMinIdle(20);
JedisCluster jedisCluster = new JedisCluster(nodes,poolConfig);
jedisCluster.set("jedisCluster", "redis 集群搭建");
System.out.println(jedisCluster.get("jedisCluster"));
}
3. 面试问题
1).redis 集群中一共能够存储 16384 个数据?
不对: redis 中存储多少数据齐全由内存决定
hash(key1)=3000
hash(key2)=3000
2).redis 集群中最多能够有多少个 redis 主机? 16384 台主机 一台主机治理一个槽位.
4. SpringBoot 整合 Redis 集群
4.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 哨兵机制
#redis.node=192.168.126.129:26379
#配置 redis 集群
redis.nodes=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`
4.2 编辑配置类
@Configuration // 标识我是一个配置类 个别与 @Bean 注解联用
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig {@Value("${redis.nodes}")
private String nodes; //node,node,node
@Bean
public JedisCluster jedisCluster(){Set<HostAndPort> nodesSet = new HashSet<>();
String[] nodeArray = nodes.split(",");
for (String node : nodeArray){ //host:port
String host = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
HostAndPort hostAndPort = new HostAndPort(host, port);
nodesSet.add(hostAndPort);
}
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(1000);
config.setMaxIdle(60);
config.setMinIdle(20);
return new JedisCluster(nodesSet,config);
}
}
4.3 编辑 CacheAOP
@Aspect // 标识是个切面
@Component // 将对象交给 spring 容器治理
public class CacheAop {
@Autowired
//private Jedis jedis;// 单台测试
//private ShardedJedis jedis;// 实现分片操作
//private JedisSentinelPool sentinelPool; // 注入池对象 哨兵机制
private JedisCluster jedis;// 注入集群对象
/**
* 实现思路:
* 1. 动静获取 key 用户自定义的前缀 + 用户的参数[0,xx]
* 2. 判断 key 是否存在
* 存在: 间接从 redis 中获取数据 JSON, 须要将 json 转化为具体对象 依照返回值类型进行转化
* 不存在: 执行指标办法. 取得返回值数据. 将返回值后果转化为 JSON 格局. 之后保留到缓存中.
* @param joinPoint
* @return
*/ @Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){//Jedis jedis=sentinelPool.getResource();
Object result=null;
//1. 获取 key 的前缀
String key = cacheFind.key();
//2. 获取办法参数
String argString = Arrays.toString(joinPoint.getArgs());
key = key + "::" + argString;
try {
//3. 判断缓存在是否有 key
if (jedis.exists(key)){String json=jedis.get(key);
//5. 获取返回值类型
MethodSignature methodSignature= (MethodSignature) joinPoint.getSignature();
result=ObjectMapperUtil.toObject(json,methodSignature.getReturnType());
System.out.println("AOP 查问 redis 缓存");
}else {
// 示意缓存中没有数据
result=joinPoint.proceed();
String json= ObjectMapperUtil.toJson(result);
//4. 判断数据中是否有超时工夫
if (cacheFind.seconds()>0){jedis.setex(key,cacheFind.seconds(),json);
}else {jedis.set(key,json);
}
System.out.println("AOP 执行数据库调用!!!!");
}
result = joinPoint.proceed();} catch (Throwable throwable) {throwable.printStackTrace();
throw new RuntimeException(throwable);
}
//jedis.close(); // 还池操作
return result;
}