如何保证双写一致性

Cache Aside Pattern

1、读的时候,先读缓存,缓存没有的话,就读数据库,取出数据后放入缓存,同时返回响应。

2、更新的时候,先删除缓存,然后更新数据库

3、缓存设置ttl

问题一:为什么第二步不是更新缓存,而是删除缓存

 数据库更新后,缓存数据的更新操作不是必须的。有可能频繁的更新了缓存,但在此期间并没有读操作,所以使用lazy的思想,可以把缓存更新放在读操作流程里。

问题二:第二步为什么先删除缓存,见下图


主要是考虑操作失败的情况:

1、如果采用先更新库再删除缓存的方式,如果删除缓存失败,则缓存里是脏数据

2、如果采用先删除缓存再更新库的方式,无论是哪种操作失败,都不会有脏数据

选择先删除缓存,然后更新数据库的方式,读取和更新并发执行仍有可能不一致  见下图


上图的步骤执行完,缓存里是旧数据。虽然几率很小,不过仍然有可能发生。

简单版解决方法:

   更新操作优化为:先删除缓存,再更新数据库,1秒后,再删除缓存。这样就把有可能造成的脏缓存删除掉

    优点:实现简单

    缺点:1秒内有可能有脏缓存,虽然几率很小

问题:延时删除操作失败,仍然是脏缓存数据。

解决方案?

    将删除失败的key放入消息队列,业务从消息队列消费,重试删除key的操作,直到删除成功。

复杂版解决方案:

    把读操作和更新操作串行化操作。

    一、程序实现

        根据操作数据的唯一表示,把对数据的操作路由到某一个程序的内部队列里。

        1、读操作时,如果缓存没有读到数据,则将“读取数据库,更新缓存”的操作放到内部队列里

        2、更新操作,直接放到内部队列里

        优点:不需要借助第三方消息队列

        缺点:负载均衡处需要把数据的读操作和更新操作做路由处理

二、消息队列实现

    将“程序实现”步骤中的内部队列替换成消息队列(rabbitmq、kafka等)

    优点:保证一致性,不需要路由

    缺点:流程比较长,处理时间较长

参考:

https://blog.csdn.net/suifeng629/article/details/93903185

https://www.cnblogs.com/rjzheng/p/9041659.html

你可能感兴趣的:(如何保证双写一致性)