公司门户网站设计,互联网舆情监测,东坑仿做网站,dw网页制作教案03——缓存双写一致性
一、缓存双写一致性
如果redis中有数据#xff0c;需要和数据库中的值相同如果redis中无数据#xff0c;数据库中的值要是最新值#xff0c;且准备回写redis
缓存按照操作来分#xff0c;可以分为两种#xff1a; 只读缓存 读写缓存 同步直写操作…03——缓存双写一致性
一、缓存双写一致性
如果redis中有数据需要和数据库中的值相同如果redis中无数据数据库中的值要是最新值且准备回写redis
缓存按照操作来分可以分为两种 只读缓存 读写缓存 同步直写操作及时生效 写数据库后也同步写redis缓存缓存和数据库中的数据一致 对于读写缓存来说要想保证缓存和数据库中的数据一致就要采用同步直写策略 异步缓写策略 正常业务中mysql数据变动了但是可以在业务上允许出现一定时间后才作用与redis仓库、物流 异常情况出现了不得不将失败的动作重新修补有可能需要借助kafka或者其他MQ等消息中间件实现重试重写
双检加锁策略当多个线程同时去查询数据库的某一条数据时可以在第一个查询数据的请求上使用一个互斥锁。其他线程获取锁失败就会阻塞。第一个线程查询完毕并将数据回写redis后其他线程直接从redis中获取数据。以此来减轻数据库的压力。
二、数据库和缓存一致性的几种更新策略
目标数据最终一致性
给缓存设置过期时间定期清理缓存并回写是保证最终一致性的解决方案。
我们可以对存入缓存的数据设置过期时间所有的写操作以数据库为准对缓存操作只是尽最大努力即可。也就是说如果数据库写成功缓存更新失败那么只要到达过期时间则后面的读请求自然会从数据库中读取新值然后回填缓存达到一致性切记要以mysql的数据库写入库为准。
上述方案和后续落地案例是调研后的主流成熟的做法但是考虑到各个公司业务系统的差距不是100%绝对正确不保证绝对适配全部情况请同学们自行酌情选择打法合适自己的最好。
可以停机的情况 挂牌报错凌晨升级温馨提示服务降级 单线程这样重量级的数据操作最好不要多线程 四种更新策略 先更新数据库再更新缓存 案例一 更新mysql的某商品的库存当前商品的库存是100更新为99个。 1、先更新mysql修改为99成功然后更新redis。 2、此时假设异常出现更新redis失败了这导致mysql里面的库存是99而redis里面的还是100。 上述发生会让数据库里面和缓存redis里面数据不一致读到redis脏数据 案例二 先更新缓存再更新数据库 业务上一般把mysql作为底单数据库保证最后解释 案例一 先删除缓存再更新数据库 案例 两个并发操作一个是更新操作另一个是查询操作 A删除缓存后B查询操作没有命中缓存B先把老数据读出来后放到缓存中然后A更新操作更新了数据库。 于是在缓存中的数据还是老的数据导致缓存中的数据是脏的而且还一直这样脏下去了。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSCPx6nt-1692427619711)(https://you-blog.oss-accelerate.aliyuncs.com/2023/202303062316864.png)] 解决方案 延时双删策略 加上sleep的这段时间就是为了让线程B能够先从数据库读取数据再把缺失的数据写入缓存然后线程A再进行删除。所以线程A sleep的时间就需要大于线程B读取数据再写入缓存的时间。这样一来其它线程读取数据时会发现缓存缺失所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后延迟一段时间再次进行删除所以我们也把它叫做“延迟双删”。 延迟双删问题 这个删除该休眠多久呢 线程A sleep的时间需要大于线程B读取数据再写入缓存的时间 确认时间的方法 第一种方法 在业务程序运行的时候统计下线程读数据和写缓存的操作时间自行评估自己的项目的读数据业务逻辑的耗时以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。这么做的目的就是确保读请求结束写请求可以删除读请求造成的缓存脏数据。 第二种方法 新启动一个后台监控程序比如后面要讲解的VatchDog监控程序会加时 这种同步淘汰策略吞吐量降低怎么办 使用异步线程避免阻塞 看门狗WatchDog分析 先更新数据库再删除缓存 异常问题 业务指导思想 微软云 https://learn.microsoft.com/en-us/azure/architecture/patterns/cache-aside 阿里canal 解决方案 流程如下图所示 更新数据库数据 数据库会将操作信息写入binlog日志当中 订阅程序提取出所需要的数据以及key 另起一段非业务代码获得该信息 尝试删除缓存操作发现删除失败 将这些信息发送至消息队列 重新从消息队列中获得该数据重试操作。 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中例如使用Kafka/RabbitMQ等 当程序没有能够成功地删除缓存值或者是更新数据库值时可以从消息队列中重新读取这些值然后再次进行删除或更新。 如果能够成功地删除或更新我们就要把这些值从消息队列中去除以免重复操作此时我们也可以保证数据库和缓存的数据一致了否则还需要再次进行重试 如果重试超过的一定次数后还是没有成功我们就需要向业务层发送报错信息了通知运维人员。 类似经典的分布式事务问题 只能保证最终一致性
三、总结 如何选择方案利弊如何 在大多数业务场景下优先使用先更新数据库再删除缓存的方案先更库→后删缓存。 理由如下 先删除缓存值再更新数据库有可能导致请求因缓存缺失而访问数据库给数据库带来压力导致打满nysql。. 如果业务应用中读取数据库和写缓存的时间不好估算那么延迟双删中的等待时间就不好设置。 如果使用先更新数据库再删除缓存的方案: 如果业务层要求必须读取一致性的数据那么我们就需要在更新数据库时先在Rdis缓存客户端暂停并发读请求等数据库更新完、缓存值删除后再读取数据从而保证数据一致性这是理论可以达到的效果但实际不推荐因为真实生产环境中分布式下很难做到实时一致性一般都是最终一致性。 总结