1. AOP实现Redis缓存
1.1 需求
- 利用自定义注解 @cacheFind 指定key的名称,设定超时时间。
- 利用AOP动态拦截@cacheFind注解
- 要改变程序运行轨迹,需要使用环绕通知@around。
1.2 自定义注解
在jt-common添加自定义注解属性
@Retention(RetentionPolicy.RUNTIME) //运行期有效
@Target(ElementType.METHOD) //对方法有效
public @interface CacheFind {
String key(); //要求用户指定key
int seconds() default 0; //设置超时时间,默认为0
}
在jt-manage添加自定义注解
修改jt-common中AOP类的切入点及方法
// 定义通知方法
@Around("@annotation(com.jt.annotation.CacheFind)")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知开始");
Object result = proceedingJoinPoint.proceed();//执行目标方法
System.out.println("环绕通知结束");
return result;
}
动态获取自定义注解对象的id名称,注意ProceedingJoinPoint一定要放在第一位
1.3 Redis重构AOP
package com.jt.aop;
import com.jt.Util.ObjectMapperUtil;
import com.jt.annotation.CacheFind;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import java.util.Arrays;
@Component //交给spring管理
@Aspect //标识AOP切面类
public class RedisAOP {
@Autowired
private Jedis jedis;
// 定义切入点表达式
// 定义通知方法
/**
* 如何实现AOP缓存:
* 1. 准备key = 获取key前缀+动态拼接参数
* 2. 从redis获取数据
* 结果1:没有数据,查询数据库,并将数据保存到缓存中
* 结果2:有数据,直接将缓存数据返回
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
// @Around("@annotation(com.jt.annotation.CacheFind)")
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint proceedingJoinPoint, CacheFind cacheFind) throws Throwable {
String perkey = cacheFind.key();//获取key前缀(自定义注解中key的名称)
System.out.println("获取注解属性:"+perkey);
String args = Arrays.toString(proceedingJoinPoint.getArgs());//获取参数
String key = perkey+"::"+args;//获取完整的key
System.out.println(key);
Object result = null;
//判断redis中是否有数据
if(jedis.exists(key)){
String json = jedis.get(key);
//利用工具API动态获取返回值类型--List<EasyUITree>
MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
result = ObjectMapperUtil.toObj(json, methodSignature.getReturnType());
}else {
//表示缓存中没有数据,则查询数据库,动态获取,并存储到缓存中
result = proceedingJoinPoint.proceed();//调用目标方法/下一个通知
//将数据保存到缓存中
String json = ObjectMapperUtil.toJson(result);
if(cacheFind.seconds()>0){
jedis.setex(key, cacheFind.seconds(), json);
}else {
jedis.set(key, json);
}
System.out.println("AOP查询数据库!!!");
}
return result;
}
}
1.4 商品分类名称实现Redis缓存
添加@Cache注解即可
2. 关于Redis持久化策略说明
2.1 需求说明
如果redis服务器宕机或者断电,可能导致整个内存数据丢失,为防止该现象的发生,redis内部引入了持久化机制
持久化:将内存数据按照特定的规则保存到磁盘中
2.2 RDB模式
2.2.1 什么是RDB模式
- 是Redis默认持久化机制
- RDB模式记录的是内存数据的快照(只会保留当前最新数据),持久化效率更高
- RDB模式是定期持久化,也可以手动操作
- save 持久化(同步)
- bgsave 后端运行持久化(异步) - 由于是定期备份,可能存在数据丢失
2.2.2 RDB模式配置项
- 持久化策略
save 900 1 900s 1次更新,则持久化一次
save 300 10 300s 10次更新,则持久化一次
save 60 10000 60s 10000次更新,则持久化一次
2.2.3 持久化文件位置
2.2.4 持久化文件在redis.conf文件中
持久化文件名称
持久化文件位置
2.3 AOF模式
1.默认关闭状态,开启AOF配置
2.先重启redis,日志信息展现
2.3.1 说明
- 默认条件下AOF模式处于关闭状态,如果需要开启吗,需要手动配置
- 记录用户操作过程,可以实现实时的持久化操作,持久化文件相对较大,不易维护
- 如果同时开启RDB和AOF模式,默认以AOF为准
2.3.2 配置文件
- .aof日志文件会显示用户的操作
2.3.3 如何选择
- 允许少量数据丢失,首选RDB,速度快
- 不允许数据丢失,首选AOF
- 工作中一般如何选择: 主机选用RDB,从机选用AOF模式
2.3.4 关于持久化面试题
- 误操作flushall命令,如何处理?
- 进入AOF配置文件中修改文件信息,删除flushall命令,关闭reids,重启
2.4 Redis内存策略
2.4.1 需求
将Redis中的数据保存到内存中,但是内存资源相对较少,如何保证其中的数据都为热点数据?
2.4.2 内存优化-LRU算法
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的数据置换算法,选择最近最久未使用的数据予以淘汰。该算法赋予每个数据一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个数据时,选择现有数据中其 t 值最大的,即最近最少使用的数据予以淘汰。
参考维度:时间T
2.4.3 内存优化-LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数
参考维度:使用次数
2.4.4 内存优化-Random算法
2.4.5 内存优化-TTL算法
根据剩余的存活时间,排序,根据时间少的删除数据.
2.4.6 修改内存优化策略
- volatile-lru 在设定超时时间的数据中采用LRU算法
- allkeys-lru 在所有数据中采用LRU算法
- volatile-lfu 在设定了超时时间的数据采用LFU机制进行计算
- allkeys-lfu 在所有数据中采用LFU算法
- volatile-random 设定超时时间的随机
- allkeys-random 所有数据的随机算法
- volatile-ttl 在设定了超时时间的数据中,采用TTL算法
- noeviction 默认规则 如果内存满了,则不做任何操作 直接报错返回.
3. Redis分片机制
3.1 分片机制说明
由于电商网站数据量很大,使用单个redis节点进行数据存储,无法完成。故需要准备多态redis共同实现内存数据的扩容
3.2 准备3台redis
3.2.1 编辑配置文件
1.准备文件目录
2.修改端口号
3.2.2 启动多台redis
redis-server 6379.conf
redis-server 6380.conf
redis-server 6381.conf
3.2.3 多台Redis入门测试案例
/**
* 实现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", "测试分片机制");
System.out.println(shardedJedis.get("shards"));
}
3.3 一致性Hash算法
3.3.1 什么是一致性Hash算法
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问
3.3.2 一致性Hash算法原理说明
- 常识:常见hash函数8位16进制数 排列组合有2^32种
- 如果对相同数据进行Hash计算,值必然相同
- 如果值相同,则数据不一定相同 ——hash碰撞
3.3.3 一致性Hash算法特性
-
平衡性(均衡性):hash结果平均分配,通过虚拟节点实现数据平衡
-
单调性:在新增或者删除节点时,不影响正常运行
特点:如果实现数据迁移时,尽可能保证原有数据不变 -
分散性:数据应该分散地存放在分布式集群中的各个节点,不必每个节点都存储所有数据
3.4 SpringBoot整合Redis分片机制
3.4.1 编辑pro配置文件
3.4.2 编辑配置类
@Configuration //标识该类为配置类 一般和@Bean注解联用
@PropertySource("classpath:/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){
String host = node.split(":")[0];
int port = Integer.parseInt(node.split(":")[1]);
JedisShardInfo jedisShardInfo = new JedisShardInfo(host, port);
shards.add(jedisShardInfo);
}
return new ShardedJedis(shards);
}
}
3.4.3 修改CacheAOP注入
@Autowired
private ShardedJedis jedis;