1 Redis持久化策略
1.1 为什么要持久化
Redis中的记录都保存在内存中,如果内存断电或者服务器宕机,则内存数据直接丢失.业务中不允许发生. 所以需要将数据定期进行维护.
1.2 RDB模式
说明: RDB模式是Redis的默认的持久化策略.无需手动的开启.
特点:
1.Redis会定期的执行RDB持久化操作. 缺点:可能导致内存数据丢失.
2.RDB记录的是内存数据的快照,并且后续的快照会覆盖之前的快照.每次只保留最新数据.效率更高.
命令:
1).save 命令 要求立即执行持久化操作 save会造成线程的阻塞.
2).bgsave 命令 后台执行持久化操作 后台运行不会造成阻塞. 异步操作, 不能保证立即执行
1.3 AOF模式
说明: AOF模式默认条件下是关闭的,需要手动的开启,如果开启了AOF模式则RDB模式将失效.但是如果手动执行save命令,则也会生成RDB文件.
1).开启AOF模式
特点:
1.AOF模式记录程序的执行的过程.所以可以保证数据不丢失.
2.由于AOF记录程序运行的过程,所以整个持久化文件相对大,所以需要定期维护. 效率低
1.4 RDB与AOF模式持久化对比
1).RDB模式
save 900 1 如果在900秒内,执行了一次更新操作则持久化一次
save 300 10
save 60 10000 操作越快 ,持久化的周期越短.
2).AOF模式
appendfsync always 用户执行一次更新操作,则持久化一次 异步操作
appendfsync everysec 每秒操作一次
appendfsync no 不主动操作 一般不用.
1.5 关于RDB与AOF总结
策略: 如果数据允许少量丢失,首选RDB模式,
如果数据不允许丢失则首选AOF模式.
企业策略: 又要满足效率,同时满足数据不丢失.
主机: 采用RDB模式
从机: 采用AOF模式
1.6面试题
题目: 小丽是公司特别漂亮的妹子,误操作将redis服务器执行了flushAll命令,问你作为项目经理如何处理??
A. 训斥一顿,之后HR开除.
B. 秀一下自己的技术,让小丽崇拜 一起过上了幸福的生活
解决方案: 需要将从库中的AOF文件 进行编辑,删除多余的flushAll命令,之后重启redis即可.
问题2: 小丽在执行完上述操作之后,由于好奇 误将aof文件一并删除,问如何处理???
答: 杀人祭天!!!
2 Redis内存策略
2.1 LRU算法
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
判断维度: 时间T
2.2 LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
判断维度: 使用次数
2.3 随机算法
随机算法
2.4 TTL算法
将剩余时间短的数据,提前删除.
2.5 Redis的内存优化策略
volatile-lru 在设定超时时间的数据中采用LRU算法
allkeys-lru 所有的数据采用LRU算法删除
volatile-lfu 设定了超时时间的数据采用LFU算法删除
allkeys-lfu 所有数据采用LFU算法删除
volatile-random 设定了超时时间的数据采用随机算法
allkeys-random 所有数据的随机算法
volatile-ttl 设定了超时时间之后采用TTL算法
noeviction 不做任何操作,只是返回报错信息.
3 关于Redis常见面试题
业务场景: 高并发环境下.用户长时间对服务器进行操作,可能产生如下的问题.
3.1 什么是缓存穿透
说明: 用户高并发环境下访问数据库和缓存中都不存在的数据称之为缓存穿透现象.
解决方案:
1). 禁用IP 限制IP访问.
2). 限流 每秒最多访问3次
3). 布隆过滤器
布隆过滤器
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
布隆过滤器优化:
问题:如何解决hash碰撞问题
知识点: 由于hash碰撞问题,可能由多个key有相同的位置,所以得出结论,布隆过滤器认为数据存在,那么数据可能存在.如果布隆过滤器认为数据不存在,则数据一定不在.
如何降低hash碰撞的几率:
答:
1.扩容二进制向量位数.
2.增加hash函数的个数
当位数增加/函数适当增加,则可以有效的降低hash碰撞的几率. 默认值 0.03
3.2 什么是缓存击穿
说明: **某个(一个)热点数据在缓存中突然失效.**导致大量的用户直接访问数据库.导致并发压力过高造成异常.
解决方案:
1.尽可能将热点数据的超时时间 设定的长一点
2.设定多级缓存 超时时间采用随机算法.
3.3 什么是缓存雪崩
说明: 在缓存服务器中,由于大量的缓存数据失效,导致用户访问的命中率过低.导致直接访问数据库.
问题分析:
- fluashAll命令可能导致缓存雪崩.
- 设定超时时间时,应该采用随机算法
- 采用多级缓存可以有效防止.
4 redis分片机制
4.1 redis性能优化
说明: 单台redis内存容量是有限的.但是如果有海量的数据要求实现缓存存储,则应该使用多个Redis节点.
4.2 Redis分片机制定义
4.3 Redis分片机制配置
4.3.1 配置规划
说明: 准备3台redis服务器 分别为 6379/6380/6381
4.3.2 准备3个配置文件
修改各自端口号 改为6380 6381
4.3.3 启动redis服务器
4.3.4 检查redis启动状态
4.3.5 redis分片入门案例
4.3.6 编辑redis.properties配置文件
说明:根据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.3.7 编辑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.3.8 修改CacheAOP中的注入
5 Redis分片机制说明
5.1一致性hash算法
5.1.1 一致性Hash算法介绍
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 [2] 。
5.1.2 一致性Hash原理说明
常识:
1).对相同的数据hash得到的结果相同
2).常见hash值 8位16进制数 2^32种可能性
在这里插入图片描述
5.1.3 平衡性说明
说明: 平衡性是指hash的结果应该平均分配到各个节点,这样从算法上解决了负载均衡问题 .
如果发现数据分布不均匀,则采用虚拟节点的方式,实现数据的平衡. 如果一次平衡不能达到目标则多次生成虚拟节点.但是数据平衡没有办法做到绝对的平均.
5.1.4 单调性说明
单调性是指在新增或者删减节点时,不影响系统正常运行
如果新增或者删除节点,则尽可能不要改变原有的数据结构.
5.1.5 分散性
分散性是指数据应该分散地存放在分布式集群中的各个节点(节点自己可以有备份),不必每个节点都存储所有的数据
鸡蛋不要放到一个篮子里.
5.2 Redis哨兵机制
5.2.1 Redis分片机制问题
说明: 如果redis分片中有一个节点宕机,则可能会影响整个服务的运行. redis分片没有实现高可用.
5.2.2 Redis主从结构搭建
5.2.2.1 复制配置文件
5.2.2.2 准备3台redis
5.2.2.3 主从搭建
命令1: info replication
命令2: slaveof IP PORT 主从挂载命令
检查主从结构状态
关于主从结构说明:
主和从都知道 当前的主从的状态, 并且只有主机可以写操作.从机只能读操作.
5.3 Redis哨兵机制工作原理
1).当哨兵启动时,首先会监控主机,从主机中获取当前所有的节点的状态,同时哨兵开启心跳检测机制.
2).当主机发生宕机的现象时,由于哨兵有PING-PONG机制 发现主机宕机,则哨兵开始进行选举.
3).当选举成功之后,新的主机当选之后,其他的节点当新主机的从.
5.4 编辑哨兵配置文件
1)关闭保护模式
2).开启后台运行
3).编辑监控配置
4).修改选举时间
5.5 启动哨兵服务
命令: redis-sentinel sentinel.conf
高可用检查:
1).先关闭主机 等待10秒检查是否进行选举.
2).启动6379. 检查是否当选新主机的从.
5.6 哨兵测试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();
}
5.7 SpringBoot整合哨兵机制
5.7.1 编辑pro配置文件
5.7.2 编辑配置类
5.7.3 编辑CacheAOP
5.7.4 关于分片/哨兵总结
1.Redis分片主要的作用实现内存的扩容,缺点:没有实现高可用的效果.
2.Redis哨兵主要的作用实现了redis节点高可用. 缺点:没有实现内存扩容
Redis哨兵机制实质就是引入第三方的监控,但是需要保证第三方的高可用.就必须引入更多的资源.
6 Redis集群
参见搭建文档.
6.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
6.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"));
}
6.3 面试问题
1).redis集群中一共可以存储16384个数据?
不对: redis中存储多少数据完全由内存决定
hash(key1)=3000
hash(key2)=3000
2).redis集群中最多可以有多少个redis主机? 16384台主机 一台主机管理一个槽位.
6.4 SpringBoot整合Redis集群
6.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
6.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);
}
}