高并发问题
缓存穿透
当用户访问的数据既不在缓存也不在数据库中时,就会导致每个用户查询都会“穿透”缓存,到达数据库。这种情况被称为缓存穿透。当高度并发的访问请求到达时,缓存穿透不仅增加了响应时间,而且还会引发DBMS的高并发查询,这种高并发查询很可能会导致DBMS崩溃。
缓存穿透产生的原因及解决思路
- 在数据库中没有相应的查询结果
- 对非法请求进行限制
- 查询结果为空时,不对查询结果进行缓存
- 对结果为空的查询给出默认值
缓存击穿
对于某一个缓存,在高并发情况下若其访问量特别巨大,当该缓存的有效时限到达时,可能会出现大量的访问都要重建该缓存,即这些访问请求发现缓存中没有该数据,则立即到DBMS中进行查询,那么就可能会引发对DBMS的高并发查询,从而间接导致DBMS的崩溃
通常采用双重检测锁来预防
// 获取key对应的value
turnover = ops.get();
if(turnover == null){
// 对当前资源加锁
synchronized(this){
// 获取key对应的value
turnover = ops.get();
if(turnover == null){
// 从数据库中查询出数据
// 将数据存入到redis缓存中
}
}
}
该算法在高并发场景下,从第一个获取该资源的请求再到将该数据从数据库中查询出来存入到redis缓存,在这段时间内也可能会有获取同一资源的请求,此时由于该资源在redis中并不存在,故这些请求也会通过第一层if判断,但由于第一个请求对该资源的加锁,使得这些请求会被阻塞在该处.当第一个请求成功将数据库中的数据查询并存入到redis缓存中后,该锁被解开.第二个请求同样也会对该资源进行加锁,然后获取该资源,发现并不为空后会直接解锁,防止了对该数据的重复查询写入操作.在这段时间内的请求都会重复这一操作.而当第一个请求将数据查询并写入到redis中之后的请求都会直接被第一层if返回
缓存雪崩
对于缓存中的数据,很多都是有过期时间的.若大量缓存的过期时间在同一很短的时间段内几乎同时到达,那么在高并发访问场景下就可能会引发对DBMS的高并发查询,而着将可能直接导致DBMS的崩溃
对于缓存雪崩没有很直接的解决方案,最好的解决方案就是预防,规划好缓存过期的时间.如果是分布式部署,则要将热点数据均匀分布在不同节点中,将访问负载均衡开
数据库缓存双写不一致
-
修改数据库更新缓存
- 对于具有缓存warmup功能的系统,DBMS中常用数据的变更,都会引起缓存中相关数据的更新。在高并发写请求场景下,若多个请求要对DBMS中同一个数据进行修改,修改后还需要更新缓存中相关数据,那么就有可能会出现缓存与数据库中数据不一致的情况
-
修改数据库删除缓存
- 对于没有缓存warmup功能的系统,为了保持缓存与数据库数据的一致性,一般都是在对数据库执行了写操作后,就会删除相应缓存。
- 在高并发读写请求场景下,若这些请求对DBMS中同一个数据的操作既包含写也包含读,且修改后还要删除缓存中相关数据,那么就有可能会出现缓存与数据库中数据不一致的情况
-
延迟双删
- 解决修改数据库删除缓存时出现的问题
- 延迟双删方案:在写操作完成后会立即执行移除缓存的删除操作,然后再停上一段时间后再进行一次删除。两次删除的间隔时长要大于一次缓存写操作的时长。
- 这里的第二次删除,其目的是作用于删除该写请求执行时间段内,其它并行写请求所导致的结果
-
队列
- 出现双写不一致的问题,主要原因是,对请求的处理出现了并行。只要将请求写入到一个统一的队列中,处理完一个请求后才能处理下一个请求,即使得系统对用户写请求的处理串行化即可解决。
-
分布式锁
- 使用队列会使得系统性能降低。
- 对数据库中这个共享数据的访问使用分布式锁来进行协调