缓存与数据库一致性

1、先更新缓存,后更新数据库

第二步失败,则在缓存失效之前读取的都是旧数据。

2、先更新数据库,后更新缓存

第二步失败,缓存失效后读取到的都是错误的数据。

缓存利用率角度看:

  • 每次数据变更都更新缓存,但缓存可能长时间不被访问,浪费资源。

  • 很多情况下缓存中的值与数据库不是一一对应的,可能是数据库的资源经过一系列计算得出的值。

3、先删除缓存,再更新数据库(实际有一定使用量)

第二步失败问题,读取到的都是旧数据。

并发问题,线程A执行操作时,线程B在删除缓存后更新数据库前查询数据,查到旧数据——>缓存延迟双删:删除缓存,更新数据库,删除缓存。延迟时间在分布式和高并发场景下很难评估,很多时候,我们都是凭借经验大致估算这个延迟时间,例如延迟 1-5s,只能尽可能地降低不一致的概率。

4、先更新数据库,再删除缓存(比较流行)

第二步失败,缓存失效之前读取的都是旧数据。

并发问题,缓存中不存在时A读取数据库得到旧值,B更新数据库并删除缓存,A将旧值写入缓存。(概率很低)

应对第二步删除缓存失败方法

1、异步重试:把操作缓存这一步,直接放到消息队列中,由消费者来操作缓存。(消息队列保证可靠性)

2、订阅数据库变更日志,再操作缓存:用 canal(阿里)监听binlog日志去进行同步数据。(可以做数据同步)

引入消息队列等其他组件,增加系统复杂度。

其他思考:

引入缓存的目的就是为了性能,性能和一致性就像天平的两端,无法做到都满足要求。当操作数据库和缓存完成之前,只要有其它请求可以进来,都有可能查到「中间状态」的数据。如果非要追求强一致,那必须要求所有更新操作完成之前期间,不能有「任何请求」进来。虽然我们可以通过加「分布锁」的方式来实现,但我们要付出的代价,很可能会超过引入缓存带来的性能提升。

个人觉得引入缓存之后,有些场景如果为了短时间的不一致性问题,选择让系统设计变得更加复杂的话,完全没必要。

你可能感兴趣的:(Java基础知识,数据库,缓存,java)