场景描述
在博客系统中,用户可以给博客点赞或者评论,这些操作需要更新数据库中的数据,同时要保证缓存中的博客信息与数据库保持一致。为了提高性能,博客数据会存放在Redis缓存中。但当有大量用户同事点赞或是评论时,缓存和数据库中的数据可能出现不一致。
何谓延迟双删?
延迟双删的核心思想:更新数据库数据的同时,处理Redis缓存中的数据,以避免缓存和数据库中的数据不一致。
双删
在更新数据库之前删除缓存中的博客数据
更新数据库之后,为了防止第一次删除缓存和数据库更新之间,产生新的缓存(一个新的并发请求产生在删除缓存之后,更新数据库之前再次写入了旧缓存,导致不一致问题),所以在更新数据库后的一,延迟一段时间再次删除缓存。
实现步骤
- 当用户点赞或评论某篇博客时,首先将Redis缓存中对应的博客数据删除
public void deleteCacheBeforeUpdate(Long blogId) {
// 删除Redis中缓存的博客数据
redisTemplate.delete("blog:" + blogId);
}
- 更新数据库
public void updateBlogInDatabase(Long blogId) {
// 假设有一个点赞服务或评论服务,执行数据库更新
blogService.updateLikeCount(blogId); // 点赞操作
// 或者
blogService.updateCommentCount(blogId); // 评论操作
}
- 延迟删除缓存(通过RabbitMQ异步处理)
发送延迟删除缓存的消息到RabbitMQ:更新数据库
后,将延迟删除的任务放入RabbitMQ消息队列,并设置一个适当的延迟时间
public void sendDelayedCacheDeletion(Long blogId) {
// 发送博客ID到RabbitMQ消息队列,延迟一段时间后删除缓存
rabbitTemplate.convertAndSend("cache-delete-queue", blogId);
}
RabbitMQ监听消息,进行第二次缓存删除操作。
@RabbitListener(queues = "cache-delete-queue")
public void handleCacheDelete(Long blogId) {
try {
// 设置延迟时间,比如500毫秒 可以采用延迟队列
Thread.sleep(500); // 模拟延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
// 删除Redis缓存中的博客数据
redisTemplate.delete("blog:" + blogId);
}
执行逻辑:
// 1. 删除缓存,确保读取的下一次是最新数据
deleteCacheBeforeUpdate(blogId);
// 2. 更新数据库中的博客点赞数或评论数
updateBlogInDatabase(blogId);
// 3. 发送延迟删除缓存的消息到RabbitMQ
sendDelayedCacheDeletion(blogId);
以上方案可以在一定程度上保证缓存一致性,但不是绝对的,在某些极端情况下还是会存在缓存不一致的情况。可以结合分布式锁来确保缓存的一致性。在更新数据库和处理缓存删除的过程中,通过锁机制保证只有一个线程可以操作缓存,从而避免多个线程并发地写入缓存。
最后,延迟双删策略虽然能有效解决大部分并发下的缓存一致性问题,但在极端高并发场景中仍可能会出现短暂的不一致。通过引入分布式锁,可以进一步增强缓存和数据库之间的一致性,确保在并发操作时只有一个线程能够执行缓存和数据库的操作。此外,如果对一致性要求更高,还可以在数据库更新后直接更新缓存而不是删除缓存,进一步提高数据的一致性。或者直接更新缓存,cache优于DB的情景。
标签:缓存,删除,双删,数据库,Redis,更新,MQ,blogId,延迟 From: https://blog.csdn.net/weixin_52630811/article/details/143062546