如何保证缓存一致性?

缓存可以提高数据查询的效率,降低数据库的压力,但使用缓存的过程中,需要注意缓存一致性问题。

先更新缓存,再更新数据库

假设2个并发线程A和B,均为写线程,其执行顺序如下:

  • 线程A更新缓存值为v1;
  • 线程B更新缓存值为v2;
  • 线程B更新数据库值为v2;
  • 线程A更新数据库值为v1。

此时,数据库中存储的为值v1,而缓存中的值为v2,数据不一致。

先删除缓存,再更新数据库

假设2个并发线程,1个读线程A,1个写线程B,其执行顺序如下:

  • 线程B删除缓存;
  • 线程A读取缓存,发现无缓存;
  • 线程A继续读数据库,取出数据v1到缓存;
  • 线程B写入数据v2到数据库。

后续的请求每次从缓存中读取到的均是老数据v1,而数据库中的为新数据v2,数据不一致。

先更新数据库,再更新缓存

假设2个并发线程A和B,均为写线程,其执行顺序如下:

  • 线程A更新数据库值为v1;
  • 线程B更新数据库值为v2。
  • 线程B更新缓存值为v2;
  • 线程A更新缓存值为v1;

此时,数据库中存储的为值v2,而缓存中的值为v1,数据不一致。

先更新数据库,再删除缓存(Cache Aside)

假设2个并发线程,1个读线程A,1个写线程B,其执行顺序如下:

  • 线程B发起写操作,还未操作数据库;
  • 缓存刚好失效;
  • 线程A读取缓存,发现无缓存;
  • 线程A继续读数据库,取出数据v1;
  • 线程B写入数据v2到数据库;
  • 线程B删除缓存;
  • 线程A将数据v1更新到缓存。

此时,数据库中存储的为值v2,而缓存中的值为v1,数据不一致。

但是,上述情况发生的前提是线程A写数据到数据库的时间比线程A写数据到内存的时间要快,但缓存的写速度远远高于数据库,发生概率极低。

当然,为了避免上述小概率事件的发生,可以再给缓存添加1个过期时间,双重保证。

在左耳朵耗子的博文《缓存更新的套路》中,还提到了另外2种缓存更新模式:

Read/Write Through

Read/Write Through的思路是把更新数据库的操作由缓存自己代理,更新只与缓存交互。

Read-Through直接从缓存中读数据,若缓存中存在则直接返回,否则缓存自己去数据库查询数据更新到缓存。

在这里插入图片描述

Write-Through相当于是先更新缓存,再更新数据库,区别在于:

  • 2步操作均在同一个事务中完成;
  • 第2步的更新数据库由缓存负责,与应用方无关。

在这里插入图片描述

优点是保障了一致性,且应用方编码简单,缺点就是引入了事务,增加了写入延迟,一般应用在银行系统等。

Write-Behind

该种模式也是先更新缓存,再更新数据库,且更新数据库的操作也是由缓存自己负责。

是不是和Write-Through很像?它俩的区别主要是:

Write-Behind的更新数据库步骤是异步的,即第2步更新数据库会在第1步之后的一段时间之后(或者被其他方式触发)再执行。

同时,其异步写入数据库的操作为了提升性能,往往采取批量提交的方式。

基本原理和Linux系统的PageCache类似。

Write-Behind优点是性能强劲(内存操作、异步批量落盘),缺点就是缓存和数据库的一致性在所有方案中最差。

综上,在一般业务场景下,缓存一致性的最佳实践为:

  • 为缓存设置过期时间;
  • 先更新数据库,再删除缓存。

当然,没有最佳的方案,只有适合的方案,用户可以根据业务需求灵活选择缓存一致性的实现方法。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值