本文跟大家说说,实战项目中,缓存跟数据库数据一致性,我们是如何保证的。因为最近给我的星球项目引入本地缓存Caffeine,因此说说保证本地缓存和数据库数据一致性,是如何处理?
1. 基于Cache-Aside缓存模式的读请求
日常开发中,我们是如何使用缓存的呢?如果是读请求,一般都是这样吧:
- 读的时候,先读缓存,缓存命中的话,直接返回数据
- 缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应。
假设我们查询数据信息,如下:
@Cacheable("book")
public BookVO queryBookById(Integer bookId) {
BookPO bookPO = bookRepository.queryBookById(bookId);
BookVO bookVO = new BookVO();
BeanUtils.copyProperties(bookPO, bookVO);
log.info("查询书本ID:{}", bookId);
return bookVO;
}
这个加了@Cacheable("book")注解,执行流程如下:
- 查询缓存:当调用被 @Cacheable 注解的方法时,Spring Cache 会首先检查指定的缓存(在这个例子中是名为 "book" 的缓存)中是否存在与给定参数(这里是 bookId)相关的缓存项。
- 缓存未命中:如果缓存中没有找到对应的项(即缓存未命中),那么方法将被执行,以从数据库获取数据。
- 执行方法:在你的例子中,queryBookById 方法会被执行,从 bookRepository 中查询 BookPO 对象,然后将其转换为 BookVO 对象。
- 设置缓存:方法执行完成后,其返回值(BookVO 对象)会被存储到缓存中,与调用方法时使用的参数(bookId)相关联。
- 返回结果:最终,方法返回的值(从缓存中获取或直接计算得到的)会被返回给调用者。
2. 如何保证数据一致性
2.1 更新数据时,让缓存失效
既然我们读的时候,是先读缓存的。为了保证缓存和数据库的数据一致,当更新数据库数据的时候,则需要让缓存失效。
//乐观锁只更新数量
int result = updateBookByVersion(bookPO, request);
if (result > 0) {
//让缓存失效
Objects.requireNonNull(cacheManager.getCache("book")).evict(bookPO.getId());
}
2.2 重试删除
很多伙伴可能会有疑问,就是执行让缓存失效时,如果执行失败了,怎么办呢?缓存和数据库的数据是不是就不一致了?
就是执行这一步,失败
Objects.requireNonNull(cacheManager.getCache("book")).evict(bookPO.getId());
其实一般很少很少失败的,如果你比较谨慎的话,可以重试几次哈。
2.3 延迟双删
还有些伙伴比较有这方面考虑,就是你让缓存失效的时候,查询接口那边,极端情况,查到书,把旧的数据放进去缓存呢?
比如这个流程:
- 线程B发起读请求,去读缓存,发现缓存刚好到期失效了。
- 接着线程B去查数据库的数据,查到了旧的数据。
- 这时候,线程A过来了,它把数据库的数据更新了
- 然后线程A它去让缓存失效
- 这时,线程B才把旧的数据库,更新到缓存
酱紫就有问题啦,缓存和数据库的数据不一致了。缓存保存的是老数据,数据库保存的是新数据。
这可以考虑延迟双删啦:
在更新数据库之前,先删除缓存中的旧数据。然后,在更新数据库之后,再次删除缓存.在两次删除缓存之间设置一个合理的延迟时间。
代码实现如下:
// 第一次删除缓存
Objects.requireNonNull(cacheManager.getCache("book")).evict(bookPO.getId());
//乐观锁只更新数量
int result = updateBookByVersion(bookPO, request);
if (result > 0) {
// 等待延迟(这里使用简单的Thread.sleep作为示例,实际中应使用更合适的机制)
try {
Thread.sleep(DELAY_DELETE_TIME); // 延迟时间应该根据实际情况设定
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
// 可以选择抛出异常或记录日志等
}
Objects.requireNonNull(cacheManager.getCache("book")).evict(bookPO.getId());
}
有关于这个延迟双删的时间,设置为多久,大家可以看我这篇文章哈:字节一面:延迟双删,要延迟多久呢?
2.4 最终一致性,给缓存设置一定的过期时间
当然,有些极端请款,或者一些bug产生,导致缓存和数据库数据不一致。因为缓存不要设置永久过期时间。
为了保证数据最终一致性,我们时要给缓存设置一定的过期时间的。
Caffeine
来源:
https://mp.weixin.qq.com/s/UVHMeFDO4NYTnSwHZc9f1A