一,Redis作缓存服务器
本篇博客是接着上一篇博客未分享完的技术点。
redis作为缓存服务器是众多企业中的选择之一,虽然该技术很成熟但也是存在一定的问题。就是缓存带来的缓存穿透,缓存击穿,缓存失效问题,继而引用分布式锁。
1.1,缓存穿透
在如今的项目中大多采用垂直的MVC架构,由service层去调用DAO层,然后DAO层再去查询数据库。而redis作为缓存服务器就是在service层去调用DAO层去查询时先去缓存服务器查询,如果存在则直接返回该数据,否则再去查询数据库。由此可知,这么做大量减少了对磁盘I/O的操作,减轻了数据库的压力。
现在我们假设一种情况,在数据库中存在有id为1到1000的数据。现在如果有人手动去模拟一个id为1001的请求,那么该数据在缓存服务器中是不存在的,因而便会去查询数据库。那么问题来了,如果是一个大量无效的请求去查询数据库。则势必会对数据库造成难以承受的压力,这种情况就是所谓的缓存穿透。
那如何解决呢?
1,将查询到的null值直接保存到缓存服务器中,但是这种做法并不推荐,因为如果是大量不同的请求id同样会去查询数据库。
2,接口的限流,降级与熔断
在项目中对于重要的接口一定要做限流,对于以上恶意攻击的请求除了要限流,还要做好降级准备,并且进行熔断,这种做法可以有效控制大量无效请求。
3,布隆过滤器
Bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小,该做法是多数企业所选择的。
1.2,缓存击穿
在高并发下,对某些热点的值进行查询,但是这个时候缓存正好过期了,缓存没有命中,导致大量请求直接落到数据库上,此时这种大量的请求可能会是数据库崩盘。
解决方案:
1,将热点key设置成永不过期。
2,使用互斥锁。
以上两种情况均是属于缓存失效,但里面还有小小的细节。那就是存在多个缓存同时失效的问题,尤其在高并发时间段。为避免这种多个缓存失效的问题,我们在设置超时时间的时候,可以使用固定时间+随机时间。以最大限度避免当缓存失效时大量请求去查询数据库。
1.3,分布式锁
通常情况下分布式锁有三种实现方式,1. 数据库乐观锁;2. 基于ZooKeeper的分布式锁;3. 基于Redis的分布式锁;这里只记录基于redis的分布式锁。
作为分布式锁的要求:
互斥性: 保证在分布式应用集群中,同一把锁在同一时间只能被一台机器上的一个线程执行。
避免死锁:有一个客户端在持有锁的过程中崩溃而没有解锁,也能保证其他客户端能够加锁。
先参看如下代码:
public List goodsManager() {
System.out.println("调用过了业务层的goodsManager方法");
return goodsDao.queryAllPage();
// 1,先去查询缓存服务器
List goodsList = (List) redisTemplate.opsForValue().get("goods");
if(goodsList == null){
// 2,申请分布式锁
RedisConnection conn = redisTemplate.getConnectionFactory().getConnection();
if(conn.setNX("lock".getBytes(), "1".getBytes())){