1.Redis集群模式--解决redis单点故障
单点故障问题主要是由于redis是单机运行的,该机器出现故障后,会间接导致使用redis的程序出现错误。因此我们可以使用redis的集群模式来避免这个问题。
redis集群模式有哪些?
1.主从复制模式
2.哨兵模式
3.集群化模式
1.1 主从复制模式
主从模式:一主多从,一个主节点,多个从节点,其中主节点拥有读写操作权限,从节点只能拥有读权限,由此可以降低主节点的读取压力,降低损坏风险。
当主节点完成写操作后,会将数据同步到从节点中,从而实现数据共享。
(1)如何搭建主从关系(查看主从关系:info replication)
1.原则:配从不配主
2.准备:一主二从-----三台服务器----即开启三台虚拟机
3.开启三台机器的redis服务(此时查看三台机器的角色发现都是master)
4.改变两台从节点的角色------->命令:slaveof 主节点的ip 主节点redis服务的端口号
5.此时再看三台机器的角色发现,两台从节点的角色变成了slave
6.此时操作时发现,三台机器都拥有读权限,而只有主节点拥有写权限
(2)主从模式的相关问题
1.当主节点挂掉了,从节点是否会自动成为主节点?
答:不会,需要手动调整主从关系。
2.如果有新增的从节点,那么新的从节点是否会自动同步数据?
答:会,主从之间的数据会自动同步
1.2 哨兵模式
由于主从模式会出现从节点不会自动上位的问题,需要手动配置,相对比较麻烦,因此使用哨兵模式解决这个问题。
在原有的主从关系基础上,添加哨兵服务,当主节点出现问题(例如,宕机、服务器关闭等)以后,哨兵会从主节点下的从节点中,挑选一个从节点成为新的主节点[采用哨兵投票机制决定由谁升级为主节点]。
实现哨兵模式
1.完成主从复制模式搭建
2.修改sentinel.conf配置文件
3.启动哨兵服务------>命令:redis-sentinel sentinel.conf
启动成功
哨兵模式下会出现的问题是,原来的主节点恢复后,不再是主节点,而是作为新任主节点的从节点继续存在
1.3 集群化模式(多主多从)
以上的两种方式都无法解决单节点写操作的故障,此时就可以使用集群化模式(去中心化模式)来解决这个问题。
原理:
redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个整数结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。
当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。
注:crc16算法可参考如下博客:CRC16 - yueyuef - 博客园
(1)搭建集群
准备:三主三从(主节点的个数应为奇数个,因为当主节点故障数量超过半数时,判定redis集群故障),所有机器的redis都需要以下要求:
1.开启远程访问权限;2.同时开启aof持久化;3.开启集群模式
开始搭建:
1.开启六台机器的redis服务
2.分配槽:redis-cli --cluster create --cluster-replicas 1(此处的数字代表每个主节点有几个从节点) 机器1ip:机器1端口 机器2ip:机器2端口 机器3ip:机器3端口 机器4ip:机器4端口 机器5ip:机器5端口 机器6ip:机器6端口
其中前三台机器是主节点,紧接着是三台从节点分别对应给主节点(对应位:1-4,2-5,3-6)
(2) 测试集群
任意访问其中一台机器的端口都可以实现对集群的访问
2.Java操作redis---需要允许redis远程链接
从redis官网的文档中可以看到redis都支持那些语言,可以看到redis可以与很多语言进行对接,包括Java
点击java可以跳转到对应的位置,查看所封装的好的接口
使用java通过Jedis链接redis的前提是,添加Jedis依赖(也可以说导入jedis相关jar包)
2.1 单独链接redis
(1)创建Jedis类的实例化对象
Jedis jedis = new Jedis("192.168.1.78",6379);
(2)测试链接
@Test
void test01(){
Jedis jedis = new Jedis("192.168.1.78",6379);
String set = jedis.set("k12", "张三");
System.out.println(set);
}
2.2 使用连接池链接redis
(1)实例化JedisPool类,并初始化配置
//连接池配置类
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大空闲连接数
jedisPoolConfig.setMaxIdle(20);
//最小空闲连接数
jedisPoolConfig.setMinIdle(20);
//最大等待时间
jedisPoolConfig.setMaxWait(Duration.ofMillis(3000));
JedisPool jedisPool = new JedisPool(jedisPoolConfig,"192.168.1.78",6379);
(2)测试链接
@Test
void testJedisPool(){
//连接池配置类
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
//最大空闲连接数
jedisPoolConfig.setMaxIdle(20);
//最小空闲连接数
jedisPoolConfig.setMinIdle(20);
//最大等待时间
jedisPoolConfig.setMaxWait(Duration.ofMillis(3000));
JedisPool jedisPool = new JedisPool(jedisPoolConfig,"192.168.1.78",6379);
//从连接池中获取Jedis对象
Jedis jedis = jedisPool.getResource();
String set = jedis.set("k13", "李四");
System.out.println(set);
}
(3)为何要使用jedis连接池
当访问数量过多时,使用链接池的效率要远高于直接使用jedis对象;
2.3 链接redis集群
(1)实例化JedisCluster类
3.springboot整合redis
springboot对redis的操作封装了两个类,StringRedisTemplate类和RedisTemplate类
StringRedisTemplate是RedisTemplate的子类,StringRedisTemplate只能存储String类型,无法存储对象类型。要想用StringRedisTemplate存储对象,则必须将对象转为JSON字符串。
在springboot中使用以上所说的两个类,首先就需要引入相关依赖
3.1 StringRedisTemplate
3.2 RedisTemplate
3.3 二者如何选择
一般情况下都会使用,可以根据实际存储的内容进行选择,如果所存储的value值为String类型,则使用StringRedisTemplate较为方便,如果是其他类型,则使用RedisTemplate。
4.redis的使用场景
4.1 redis存储热点数据
4.2 redis实现分布式锁
4.2.1 redis实现分布式锁的缺陷
Redis实现分布式锁,无法解决超时问题,分布式锁有一个超时时间,程序的执行时间如果超过了锁的超时时间就会出现问题。
解决方案:使用redisson来解决;
redisson解决redis分布锁超时问题的原理:
为持有锁资源的线程开启一个守护线程,该守护线程,会每隔十秒检查当前线程时候还持有锁,如果仍持有,则会延长锁的持有时间。
4.2.2 使用redisson解决redis实现分布式锁的缺陷
(1)引入redisson对象
(2)
5.Redis常见面试题
5.1 什么是缓存穿透?如何解决?
缓存穿透:数据库中没有该记录,缓存中也没有该记录,此时有人恶意大量访问这样的数据,这样就会导致该请求绕过缓存直接访问数据库,从而造成数据库压力过大,这种现象就是缓存穿透。
解决方案:(建议三种都用)
1.在controller层添加数据校验,过滤非正常数据
2.我们可以在redis中存入一个空对象,而且给空对象设置过期时间,不宜过长否则将会存在大量的空对象,一般不超过5分钟
3.我们可以使用布隆过滤器,底层有一个bitmap数组,里面存储了某表中的所有id
5.2 什么是缓存雪崩?如何解决?
解决方案:
1.上线前预先将热点数据存入缓存中
2.将缓存过期时间设置为散列值
3.搭建redis集群
5.3 什么是缓存击穿?如何解决?
缓存击穿解决方案 :1. 设置永久不过期。【这种只适合访问量十分巨大的数据】2. 使用互斥锁 (mutex key) 业界比较常用的做法,是使用 mutex。简单 地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db ,而是先使用缓存工具的某些带成功操作返回值的操作(比如 Redis的 SETNX 或者 Memcache 的 ADD )去 set 一个 mutex key,当操作返回成功 时,再进行 load db 的操作并回设缓存;否则,就重试整个 get 缓存的方法。
5.4 redis的淘汰策略有哪些?