欢迎光临散文网 会员登陆 & 注册

面试官,如何才能保证缓存与数据库的数据一致性

2023-03-28 22:46 作者:程序员徐师兄  | 我要投稿

0. 缓存更新策略

内存淘汰超时剔除主动更新说明利用redis的内存淘汰机制,当内存不足时自动淘汰部分数据,下次查询时更新缓存给缓存数据添加过期时间,到期后删除缓存,下次查询再更新缓存编写业务逻辑,自己控制在修改数据库时更新缓存一致性差一般好维护成本无低高

0.1 缓存主动更新策略

  • 方案一:由缓存的调用者在更新数据库的时候同时更新缓存

  • 方案二:将缓存和数据库整合为一个服务,由该服务来维护一致性。对外提供接口,调用者调用该服务提供的接口,无需关心缓存一致性问题

  • 方案三:调用者只操作缓存,由其他线程异步将缓存数据持久化到数据库,保证最终一致性。

1. 数据同步策略

1.1 设置有效期

  • 给缓存设置有效期

  • 优点:简单方便

  • 缺点:时效性差,缓存过期之前可能数据不一致

  • 场景:适合更新频率低,时效性要求低的业务

1.2 同步双写

  • 在修改数据库的同时,直接修改缓存

  • 优点:时效性强,缓存与数据库强一致

  • 缺点:有代码侵入,耦合度高

  • 场景:对一致性、时效性要求较高的缓存数据

1.3 异步通知

  • 修改数据库时发送事件通知,相关服务监听到通知后再修改缓存数据

  • 优点:低耦合,可以同时通知多个缓存服务

  • 缺点:时效性一般,可能存在中间不一致状态

  • 场景:时效性要求一般,有多个服务需要同步

2. 保证缓存与数据库一致性的四个方案

  • 先更新数据库,再更新缓存(有bug)

    • 并发更新数据库场景下,会将脏数据刷新到缓存,不推荐

  • 先更新缓存,再更新数据库(有bug)

    • 缓存更新成功后,数据库更新失败,则会造成数据不一致性,而且也有并发问题,不推荐

  • 先删除缓存,在更新数据库(有bug)

    • 改进方法:先删缓存,再更新数据库,再删缓存

  • 先更新数据库,再删除缓存(有bug)

    • 更新数据库成功,但是删除缓存失败。

    • 推荐这种,更新完数据库后删除缓存的速度是非常快的,所以在这个间隔内插入其他事务概率会比较低。

总结:不推荐更新数据库的时候更新缓存。因为会有并发问题。推荐使用更新数据库时删除缓存,虽然也有问题。

3. 先删除缓存,后更新数据库

3.1 可能出现的问题

  1. 请求A进行写操作,删除缓存

  2. 请求B查询发现缓存不存在

  3. 请求B去数据库查询得到旧值

  4. 请求B将旧值写入缓存

  5. 请求A将新值写入数据库

如果没有给缓存设置过期时间,则缓存数据永远都是脏数据

3.1 解决方式:延时双删

  • 先淘汰缓存

  • 再写数据库

  • 休眠一秒后再次淘汰缓存

对于读写分离的数据库,主从同步之间也会有时间差,若此时来了两个请求,请求A(更新操作)和请求B(查询操作),也会出现一些问题

  1. 请求A更新操作,删除缓存

  2. 请求A再主库进行更新操作,主库与从库进行数据同步操作

  3. 请求B查询操作,发现redis中没有数据

  4. 请求B去从库获取旧值数据

  5. 请求B更新缓存

  6. 主从同步完成

主从同步时间差

解决方法:如果对redis进行填充的查询数据库操作,那么强制将其指向主库进行查询

从主库中拿数据

4. 先更新数据库,后删除缓存(推荐)

4.1可能出现的问题

  • 更新数据库成功了,但是在删除缓存的阶段没有成功,则之后读取的缓存都是错误的

4.2解决方式一:异步实现之利用消息队列

  1. 请求A向服务端发送修改商品请求

  2. 相应的模块根据请求会对数据库对应内容进行更新,更新成功后会向MQ发送消息

    1. 该消息通知缓存处理模块删除对应的缓存

  3. 缓存模块监听到有新的消息,会执行缓存删除逻辑

    1. 利用消息队列的 手动提交机制 可以保证删除逻辑顺利完成

4.3 解决方式二:基于Canal的通知

  • 商品服务完成数据库修改操作后,业务直接结束。没有任何代码侵入

  • Canal监听mysql变化,当发现变化后,立即通知缓存服务

  • 缓存服务接收到canal通知,删除缓存。


面试官,如何才能保证缓存与数据库的数据一致性的评论 (共 条)

分享到微博请遵守国家法律