当前位置: 首页 > 后端技术 > Java

面试官:如何保证数据库和缓存数据的一致性?面试题...

时间:2023-04-02 10:25:54 Java

作者:牛牛马特链接:https://juejin.cn/post/684490...后台缓存在软件开发中是一个非常有用的概念,在项目中数据库缓存是不可避免的。遇到的场景。缓存一致性的保证在采访中被反复问到。以下是针对不同需求选择合适的一致性解决方案的总结。缓存存储在什么速度上是有区别的。缓存是一种将低速存储的结果暂时保存在高速存储中的技术。如图所示,金字塔上方的存储可以作为下方存储的缓存。我们这次的讨论主要是针对数据库缓存的场景,将以redis作为mysql的缓存作为一个案例。推荐一个开源免费的SpringBoot最全教程:https://github.com/javastacks/spring-boot-best-practice为什么需要缓存存储比如mysql通常支持完整的ACID特性,因为可靠性、持久化和其他因素,性能普遍不高,高并发查询会给mysql带来压力,导致数据库系统不稳定。它也容易出现延误。根据局部性原则,80%的请求会落在20%的热点数据上。在读多写少的场景下,增加一层缓存对于提高系统吞吐量和健壮性是很有帮助的。问题中存储的数据可能会随着时间的推移发生变化,缓存中的数据会不一致。具体可以容忍的不一致时间需要具体业务具体分析,但通常的业务需要最终一致。Redis作为mysql的缓存在通常的开发模式下,mysql作为存储,redis作为缓存来加速和保护mysql。但是,当mysql数据更新时,redis是如何保持同步的。强一致性同步成本太高。如果追求强一致性,那就不用缓存了,直接用mysql就可以了。通常考虑的是最终一致性。方案一:通过key的过期时间,mysql更新时,redis不会更新。这种方法实现起来简单,但是不一致的时间会很长。如果读请求非常频繁,过期时间比较长,会产生大量的长期脏数据。优点:开发成本低,易于实施;管理成本低,出现问题的机会少。不足完全取决于过期时间。时间太短,缓存会频繁失效,时间太长,会出现很长的更新延迟(不一致)。第二种方案是在第一种方案的基础上进行扩展,利用key的过期时间来覆盖底线。更新mysql的时候,同时更新redis。与第一种方案相比,更新延迟更小。不足如果mysql更新成功,但redis更新失败,退化为第一种方案;在高并发场景下,业务服务器需要同时连接mysql和redis。这样一来,连接资源成倍增加,容易造成连接过多的问题。方案三针对方案二同步写入redis进行了优化,增加了一个消息队列,将redis的更新操作交给kafka。可靠性由消息队列保证,然后构建一个消费者服务异步更新redis。优点消息队列可以使用句柄,很多消息队列客户端还支持本地缓存发送,有效解决了方案二中连接过多的问题;使用消息队列实现逻辑解耦;消息队列本身是可靠的,通过手动提交等方式,至少可以消费到redis一次。不足还是不能解决时序问题。如果多个业务服务器分别处理对同一行数据的两次请求,例如a=1;a=5;,如果mysql中第一个先执行,而进入kafka的顺序是第二个先执行,那么数据就会不一致。引入了消息队列,同时需要增加服务消费消息,成本高。方案4通过订阅binlog更新redis,使用我们搭建的consumer服务作为mysql的slave,订阅binlog,解析出更新内容,再更新到redis。优点是在mysql压力不大的时候延迟低;它与业务完全脱钩;它解决了时间问题。缺点是需要单独搭建同步服务,引入binlog同步机制,成本较高。总结方案选择首先确认产品的时延要求。如果要求特别高,数据可能会变化,就不要使用缓存。一般来说,解决方案1就足够了。笔者咨询过4、5个团队,基本都是采用方案1,因为可以使用缓存方案,所以通常是读多写少的场景。同时,业务对延迟有一定的容忍度。.方案一没有开发成本,其实更实用。如果你想增加更新的即时性,选择选项2,但不需要做重试保证之类的。Option3和Option4针对时延要求高的业务,一种是push模式,一种是pull模式,Option4可靠性更强。既然大家都愿意花时间在处理消息的逻辑上,不如一步到位。使用选项4。结论通常,选项1就足够了。如果延迟要求高,直接选择选项4。如果是面试场景,从简单到复杂,面试官一步步提问,我们一点点推演,宾主尽兴。近期热点文章推荐:1.1000+Java面试题及答案(2022最新版)2.厉害了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!