{{π型人才培养计划}}Redis 缓存
作者:互联网
Redis 缓存
1.为什么使用缓存
缓存就是在内存中存储的数据备份,当数据没有发生本质变化的时候,我们避免数据的查询操作直接连接数据库,而是去内容中读取数据,这样就大大降低了数据库的读写次数,而且从内存中读数据的速度要比从数据库查询要快很多,极大的提升了应用程序的性能和效率,特别是数据查询方面
2.使用缓存存在的问题
2.1缓存穿透
概念:
是指查询数据库中一定不存在的数据。先在缓存中查询,如果key不存在或者key已经过期,再对数据库进行查询,并把查询到的对象,放进缓存中。如果数据库查询对象空,则不放进缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决办法:
1.布隆过滤
最常见的则是采用布隆过滤器,将所有可能存在的数据到一个足够大的bitmap中,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,一个一定不存在的数据会被bitmap拦截掉从而避免了对底层存储系统的查询压力。
2.强而有力
访问key未在DB查询到的空值写进缓存,设置较短过期时间
2.2缓存雪崩
概念:
大量的key设置了相同的过期时间,导致在缓存在同一时刻全部失效,造成瞬时DB请求量大、压力骤 增,引起雪崩。
解决办法:
可以给缓存设置过期时间时加上一个随机值时间,使得每个key的过期时间分布开来,不会集中在同一时 刻失效。
2.3缓存的击穿
概念:
一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量 大、压力骤增。
解决办法:
“永远不过期”:
这里的“永远不过期”包含两层意思: (1) 从redis上看,确实没有设置过期时间,这就保证了,不会出现热点key过期问题,也就是“物理”不过期。 (2) 从功能上看,如果不过期,那不就成静态的了吗?所以我们把过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的构建,也就是“逻辑”过期 从实战看,这种方法对于性能非常友好,唯一不足的就是构建缓存时候,其余线程(非构建缓存的线程)可能访问的是老数据,但是对于一般的互联网功能来说这个还是可以忍受。
3.mybatis一级,二级缓存
一级缓存:
一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap 用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的
二级缓存:
二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的s ql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数 据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率,多表联合查询时会造 成脏读的情况
4.redis
4.1简介
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供string,list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
4.2Redis的5种数据类型及其适用场景
(1)String:可以包含任何数据,比如jpg图片或者序列化的对象.。
(2)List:链表(双向链表),增删快,提供了操作某一段元素的API。适用于:最新消息排行等功能;消息队列。
(3)Set:集合。哈希表实现,元素不重复,为集合提供了求交集、并集、差集等操作。适用于:共同好友;利 用唯一性,统计访问网站的所有独立ip;好友推荐时,根据tag求交集,大于某个阈值就可以推荐。
(4)Hash 键值对集合,即编程语言中的Map类型。适合存储对象,并且可以像数据库中update一个属 性一样只修改某一项属性值。适用于:存储、读取、修改用户属性。
(5)Sorted Set:有序集合。将Set中的元素增加一个权重参数score,元素按score有序排列。数据插入集合时, 已经进行天然排序。适用于:排行榜;带权重的消息队列。
4.3优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性.
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
4.4其他key-value存储有什么不同?
- Redis有着更为复杂的数据结构并且提供对他们的原子性操作,这是一个不同于其他数据库的进化路径。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
- Redis运行在内存中但是可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存,因为数据量不能大于硬件内存。在内存数据库方面的另一个优点是,相比在磁盘上相同的复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂性很强的事情。同时,在磁盘格式方面他们是紧凑的以追加的方式产生的,因为他们并不需要进行随机访问。
5.redis实现缓存
5.1redis实现mybatis的二级缓存
用redis做mybatis的缓存,只能于mybatis
5.2.基于aop,实现通用的缓存
aop的机制:
AOP是Spring框架面向切面的编程思想,AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能 抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中。
6.Redis的安装
1. 安装gcc运行环境
yum -y install gcc
或手动导入安装
2. 上传redis的资料包
在CRT中 alt+p , 直接拖进
3. 解压redis的压缩包
tar -zxvf xxx.zip
4. 进入redis包执行指令
make
5. 编译完成之后安装redis
make install PREFIX=/usr/redis
6. 启动redis
./redis-server /root/redis-4.0.9/redis.conf
7. 使用redis的客户端连接redis服务
./redis-cli –p 6379
8. 需要后台链接redis
vi redis-4.0.9/redis.conf
9. 关闭redis
./redis-cli –p 6379 shutdown
10. 启动redis的服务
./redis-server /root/redis-4.0.9/redis.conf
9.Redis缓存后台实现
-
思路
-
需要的jar
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
-
实现redis缓存
3.1基于切方法
@Configuration
@Aspect//声明当前类是一个切面供容器读取
public class RedisCache {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Around("execution(* com.baizhi.service.*.select*(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
RedisSerializer stringSerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
StringBuilder sb = new StringBuilder();
//获取类名
String className = proceedingJoinPoint.getTarget().getClass().getName();
sb.append(className);
//获取方法名
String methodName = proceedingJoinPoint.getSignature().getName();
sb.append(methodName);
//获取所有的参数
Object[] args = proceedingJoinPoint.getArgs();
for (Object arg : args) {
sb.append(arg);
}
String s = sb.toString();
ValueOperations valueOperations = redisTemplate.opsForValue();
Object result = null;
if(redisTemplate.hasKey(s)){
result = valueOperations.get(s);
}else{
result = proceedingJoinPoint.proceed();
valueOperations.set(s,result);
}
return result;
}
@After("execution(* com.baizhi.service.*.delete*(..))")
public void after(JoinPoint joinPoint){
Set<String> keys = stringRedisTemplate.keys("*");
for (String key : keys) {
stringRedisTemplate.delete(key);
}
}
注意:
发现key值出现 \xac\xed\x00\x05t\x00\tb,redisTemplate 默认的序列化方式为 jdkSerializeable, StringRedisTemplate的默认序列化方式为StringRedisSerializer
3.2基于切自定义注解
1.自定义注解
//添加缓存
@Target(ElementType.METHOD)//声明注解使用的位置
@Retention(RetentionPolicy.RUNTIME)
public @interface SaveRedisCache {
//可以加属性
//public String/int/... value();
}
//移除缓存
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RemoveRedisCache {
//可以加属性
//public String/int/... value();
}
@Configuration//当前类为配置类
@Aspect//作用是把当前类标识为一个切面供容器读取
public class RedisCache {
@Autowired
private RedisTemplate redisTemplate;
@Around("@annotation(com.baizhi.annotation.SaveRedisCache)")
public Object arround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
StringBuilder sb = new StringBuilder();
//获取类名
String className = proceedingJoinPoint.getTarget().getClass().getName();
//获取方法名
String methodName = proceedingJoinPoint.getSignature().getName();
sb.append(methodName);
//获取所有的参数
Object[] args = proceedingJoinPoint.getArgs();
for (Object arg : args) {
sb.append(arg);
}
String s = sb.toString();
HashOperations hashOperations = redisTemplate.opsForHash();
Boolean aBoolean = redisTemplate.hasKey(className);
Object result = null;
if(aBoolean){
result = hashOperations.get(className,s);
}else {
result = proceedingJoinPoint.proceed();
HashMap<String, Object> map = new HashMap<>();
map.put(s, result);
hashOperations.putAll(className, map);
redisTemplate.expire(className,10, TimeUnit.SECONDS);
}
return result;
}
2. 写的操作需要清除缓存
@After("@annotation(com.baizhi.annotation.RemoveRedisCache)")
public void after(JoinPoint joinPoint){
String className = joinPoint.getTarget().getClass().getName();
redisTemplate.delete(className);
}
return result;
}
2. 写的操作需要清除缓存
~~~java
@After("@annotation(com.baizhi.annotation.RemoveRedisCache)")
public void after(JoinPoint joinPoint){
String className = joinPoint.getTarget().getClass().getName();
redisTemplate.delete(className);
}
标签:缓存,String,Redis,redis,人才培养,key,redisTemplate 来源: https://blog.csdn.net/qq_40828705/article/details/104057003