- A+
SpringBoot整合redis缓存使用
carson0408
于 2020-03-05 00:05:33 发布
12311
已收藏 98
分类专栏: Redis 文章标签: redis spring boot
版权
Redis
专栏收录该内容
7 篇文章2 订阅
订阅专栏
一开始写增删改查基本都是直接对库操作,接口调用量少的时候,性能问题几乎不存在,但是数据量级上升之后,这些增删改查接口的压力也会大大上升,甚至会出现慢查询的情况,出现较大的延迟。这时候机智的小伙伴会使用索引,没错,索引可以解决一部分查询造成的性能问题。那么如何才能进一步提升查询的性能呢?对于读多写少的表可以使用缓存,那么将大大减少读取数据库的次数。
一.Redis(缓存)常见的问题
1.缓存穿透
缓存穿透指查询一个一定不存在的数据,由于缓存不命中就会从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据都要到数据库查询,造成缓存穿透。这种情况其实就是因为数据库不存在的数据无法写入缓存,解决这个问题的方法,就是把这种数据库查不到值的情况也考虑进去。
解决方案:
缓存空值,将数据库查询不到的则作为空值存入,那么下次可以从缓存中获取key值是空值可以判断出数据库不存在这个值。
使用布隆过滤器,布隆过滤器能判断一个 key 一定不存在(不保证一定存在,因为布隆过滤器结构原因,不能删除,但是旧值可能被新值替换,而将旧值删除后它可能依旧判断其可能存在),在缓存的基础上,构建布隆过滤器数据结构,在布隆过滤器中存储对应的 key,如果存在,则说明 key 对应的值为空。
2.缓存击穿
缓存击穿针对某些高频访问的key,当这个key失效的瞬间,大量的请求击穿了缓存,直接请求数据库。
解决方案:
设置二级缓存
设置高频key缓存永不过期
使用互斥锁,在执行过程中,如果缓存过期,那么先获取分布式锁,在执行从数据库中加载数据,如果找到数据就加入缓存,没有就继续该有的动作,在这个过程中能保证只有一个线程操作数据库,避免了对数据库的大量请求。
3.缓存雪崩
缓存雪崩指缓存服务器重启、或者大量缓存集中在某一个时间段失效,这样失效的时候,会给后端系统带来很大压力,造成数据库的故障。
解决方法:
缓存高可用设计,Redis sentinel和Redis Cluster等
请求限流与服务熔断降级机制,限制服务请求次数,当服务不可用时快速熔断降级。
设置缓存过期时间一定的随机分布,避免集中在同一时间缓存失效,可以在设计时将时间定为一个固定值+随机值。
定时更新缓存策略,对于实时性要求不高的数据,定时进行更新。
4.分布式锁
我们知道目前常见的分布式锁有基于zookeeper和基于redis的,基于zookeeper是一个持久节点下的临时顺序节点的抢占,是一个队列机制。而基于redis则是对某个redis缓存key的抢占。两者优缺点如下:
优点 缺点
zookeeper
1.有封装好的框架,容易实现
2.有等待锁的队列,大大提升抢锁效率。
添加和删除节点性能较低
redis Set和Del指令性能较高
1.实现复杂,需要考虑超时,原子性,误删等情形。
2.没有等待锁的队列,只能在客户端自旋来等待,效率低下。
可以看出,redis实现分布式锁需要设置超时时间,如果不设置超时时间会出现什么问题呢?如果获取锁之后在解锁过程中出现宕机,则会导致死锁现象。因此需要设置超时时间来避免死锁现象。在redis2.8版本之前获取锁及设置超时时间分为setnx和expire两个步骤完成,如果这两个步骤之间出现宕机现象,依然会存在死锁现象。因此,redis2.8版本做了修改,将setnx和expire两个作为一个方法实现,避免了宕机引起的死锁现象。
二.缓存的使用(springboot整合redis)
1.引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2.配置
application.yml文件
spring:
redis:
## Redis数据库索引(默认为0)
database: 0
## Redis服务器地址
host: 127.0.0.1
## Redis服务器连接端口
port: 6379
## Redis服务器连接密码(默认为空)
password: root
jedis:
pool:
## 连接池最大连接数(使用负值表示没有限制)
#spring.redis.pool.max-active=8
max-active: 8
## 连接池最大阻塞等待时间(使用负值表示没有限制)
#spring.redis.pool.max-wait=-1
max-wait: -1
## 连接池中的最大空闲连接
#spring.redis.pool.max-idle=8
max-idle: 8
## 连接池中的最小空闲连接
#spring.redis.pool.min-idle=0
min-idle: 0
## 连接超时时间(毫秒)
timeout: 1200
Application类----@EnableCaching启动缓存注解
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.carson.cachedemo.mapper")
@EnableScheduling
@EnableCaching
public class CachedemoApplication {
public static void main(String[] args) {
SpringApplication.run(CachedemoApplication.class, args);
}
}
提供使用缓存的接口类,该类对user对象做了缓存,即user表中的每一条记录做了缓存,缓存key值是userid,缓存value值为user对象。其中常用的缓存注解
其中获取记录需要添加注解:@Cache(value="xxx",key="xxxx",unless="#result==null")
删除则用@CacheEvict(value="xxx",key="xxxxx")
更新则用 @CachePut(value="xxx",key="xxxx")
@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@CacheEvict 清空缓存
@CachePut 保证方法被调用,又希望结果被缓存。
@Service
public class UserManageServiceImpl implements UserManageService{
@Autowired
UserManageMapper userManageMapper;
/**
* 根据用户id获取用户信息
* @param userid
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
@Cacheable(value = "user",key = "#userid",unless = "#result==null")
public User findUser(int userid) {
return userManageMapper.findByUserid(userid);
}
/**
* 删除的时候需要把好友关系表中的关系也删除。
* @param userid
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
@CacheEvict(value = "user",key = "#userid")
public int deleteUser(int userid) {
int res2 = userManageMapper.deleteFriendByUserid(userid);
int res3= userManageMapper.deleteFriendByFriendid(userid);
int res1= userManageMapper.deleteByUserid(userid);
return res1;
}
/**
* 查询所有好友
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public List<User> findAllUser() {
return userManageMapper.findAllUser();
}
/**
* 保存用户
* @param user
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public int saveUser(User user) {
return userManageMapper.insertUser(user);
}
/**
* 更新用户
* @param user
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
@CachePut(value = "user",key = "#userid")
public int updateUser(User user) {
return userManageMapper.updateUser(user);
}
/**
* 获取所有好友信息
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public List<Friends> getFriends() {
return userManageMapper.getFriends();
}
/**
* 根据userid获取好友信息
* @param userid
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public Friends getFriendsByUserid(int userid) {
return userManageMapper.getFriendsByUserid(userid);
}
/**
* 先对friendid进行查询,如果用户表中没有friendid,以防安全,禁止添加,成对添加
*
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public int addFriend(Relation relation) {
Relation search=userManageMapper.getRelation(relation);
if(search!=null){
throw new OperationException(Result.FRIENDS_EXIST);
}
Relation newRelation = new Relation();
newRelation.setUserid(relation.getFriendid());
newRelation.setFriendid(relation.getUserid());
int res1 = userManageMapper.insertFriend(relation);
int res2 = userManageMapper.insertFriend(newRelation);
return res1*res2;
}
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public User findByFriendid(int friendid) {
return userManageMapper.findByFriendid(friendid);
}
/**
* 删除好友关系,逻辑是成对删除
* @param relation
* @return
*/
@Override
@Transactional(propagation = Propagation.REQUIRED,readOnly = false)
public int deleteFriend(Relation relation) {
Relation search=userManageMapper.getRelation(relation);
if(search==null){
throw new OperationException(Result.FRIENDS_NOT_EXIST);
}
Relation newRelation = new Relation();
newRelation.setUserid(relation.getFriendid());
newRelation.setFriendid(relation.getUserid());
int res1 = userManageMapper.deleteFriend(relation);
int res2 = userManageMapper.deleteFriend(newRelation);
return res1*res2;
}
/**
* 用于查询是否存在该用户
* @param login
* @return
*/
@Override
public User search(Login login) {
return userManageMapper.searchForLogin(login);
}
}
1.数据库中记录如下:
此时缓存的记录如下:
然后调用http://localhost:8081/user/get/1016,得到如下结果。
此时查看缓存记录:
则发现缓存出现。
2.调用http://localhost:8081/user/delete/1016 删除数据库记录
此时数据库记录
缓存记录如下,说明删除操作会清空对应的缓存。
————————————————
版权声明:本文为CSDN博主「carson0408」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/carson0408/article/details/104664552/