数据一致性保障

缓存和数据库的同步可以通过以下几种方式:

  1. 先更新缓存,再更新数据库
  2. 先更新数据库存,再更新缓存
  3. 先删除缓存,再更新数据库,后续等查询把数据库的数据回种到缓存中
  4. 先更新数据库,再删除缓存,后续等查询把数据库的数据回种到缓存中
  5. 缓存双删策略。更新数据库之前,删除一次缓存;更新完数据库后,再进行一次延迟删除
  6. 使用 Binlog 异步更新缓存,监听数据库的 Binlog 变化,通过异步方式更新 Redis 缓存

以上就是实现数据库与缓存一致性的六种方式,这里前面三种都不太推荐使用,后面三种的话其主要根据实际场景:

  • 如果是要考虑实时一致性的话,先写 MySQL,再删除 Redis 应该是较为优的方案,虽然短期内数据可能不一致,不过其能尽量保证数据的一致性。
  • 如果考虑最终一致性的话,推荐的是使用 binlog + 消息队列的方式,这个方案其有重试和顺序消费,能够最大限度地保证缓存与数据库的最终一致性。

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

image.png

由于网络原因,请求顺序无法保证,可能出现先更新缓存的请求,后更新数据库,而后更新缓存的请求反而先更新了数据库,这样就出现了缓存数据为 20,数据库数据为 10,即数据不一致的情况。

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

image.png

这个问题上面其实一样,都是因为并发和网络问题导致的数据库与缓存不一致。

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

image.png

盘一下流程:

  1. 请求 A 先对缓存中的数据进行删除操作。
  2. 请求 B 这个时候来执行查询,发现缓存中数据为空,就去数据库进行查询并回写缓存。
  3. 这个时候请求 A 删除缓存中的数据之后,进行数据库数据的更新。
  4. 但此时请求 B 已经把从数据库查询到的原始数据回写缓存了,这个时候就出现了上图的情况,数据库中查询的值是 20,而缓存中的数据是 10 。
    读操作获取到的数据是过时的数据,虽然写操作已经完成了,但是因为缓存被删除了,读操作就必须从数据库中读取到旧值,并不是最新的数据。

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

image.png

先把数据库的信息修改了,然后再删除对应的缓存,然后在修改数据库期间,可以允许一定时间的缓存不一致,保证缓存的最终一致性。
不过这种模型也有一定的问题,如下图所示:


image.png

其主要原因在于有一个写操作,此时刚好缓存失效,又在同一时刻刚好有一个并发读请求过来,且回写缓存的请求晚于缓存删除,导致数据库与缓存的不一致。
从上面的表述可以知道,这个发生的概率比较低,一般而言业务上都会使用这个方案。

缓存双删(先删除缓存,再更新数据库,过段时间再删除缓存)

image.png

这个方案为了避免旧数据被回种,等待一段时间后再延迟删除缓存。
也可以使用消息队列、定时任务或者延迟任务等方式去实现延迟删除:


image.png

先写数据库,再通过Binlog异步更新缓存

image.png

先修改数据库,然后通过 Canal 监听数据库的 binlog 日志,记录数据库的修改信息,然后通过消息队列异步修改缓存的数据。
这里需要注意需要保证顺序消费,保证缓存中数据按顺序更新,然后再加上重试机制,避免因为网络问题导致更新失败。

强一致性保障

可以使用分布式读写锁实现强一致性。读写锁中读和读操作不互斥,读写互斥,写写互斥。
写操作流程:

  • 获取写锁。
  • 更新数据库。
  • 删除缓存。
  • 释放写锁。

读操作流程:

  • 获取读锁。
  • 查询缓存:如果命中缓存,释放读锁,返回结果。如果缓存未命中,读取数据库,并将数据更新到缓存。
  • 释放读锁。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容