今天咱们重点讨论一下如何解决一致性问题,一致性在分布式架构面试中会经常问道,在传统单体架构中,数据状态的处理都在同一个服务和数据库中,而具有**ACID特性**的数据库支持强一致性,就是说数据库本身是不会出现不一致的状态的,比如我们常用的关系型数据库MySQL就是通过多版本控制协议(MVCC)的实现来保证了强一致性。
但是随着互联网的发展,用户增多&服务也越来越多越来越复杂,数据量和请求的并发量都上来了。为了满足这一变化,越来越多的系统从单体架构投入到服务化或者微服务架构。然而,微服务是把双刃剑,虽然能通过对服务的有效拆分来实现敏捷开发和自动化部署,并提高系统的水兵伸缩能力;但是微服务模式下,服务之间通过网络来通信(网络并不是可靠的),那么当网络异常时,就很容易引起各个系统之间的不一致。
本节课主要四个知识点
- 如何处理缓存与数据库不一致
- 数据库主从不一致性怎么办
- 分布式系统session一致性问题
- 分布式事务解决方案
面试题
如何处理缓存与数据库不一致?
你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?
由于操作缓存与操作数据库不是原子的,非常有可能出现执行失败,你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何处理缓存与数据库不一致?
本知识点主要讨论这么几个问题:
(1)啥时候数据库和缓存中的数据会不一致
(2)如何保证数据库与缓存的一致性
什么情况下可能出现缓存和数据库中数据不一致呢?
- 数据库有数据,缓存没有数据;
- 数据库有数据,缓存也有数据,数据不相等;
- 数据库没有数据,缓存有数据。
看一个数据库有数据,缓存也有数据,数据不相等的场景:
假设先写数据库,再淘汰缓存:第一步写数据库操作成功,第二步淘汰缓存失败,则会出现数据库中是新数据,缓存中是旧数据,数据不一致。
在分布式环境下,数据的读写都是并发的,上游有多个应用,通过一个服务的多个部署(为了保证可用性,一定是部署多份的),对同一个数据进行读写,在数据库层面并发的读写并不能保证完成顺序,也就是说后发出的读请求很可能先完成(读出脏数据):
(a)发生了写请求A,A的第一步淘汰了缓存cache(如上图中的1)
(b)A的第二步写数据库,发出修改请求(如上图中的2)
(c)发生了读请求B,B的第一步读取cache,发现cache中是空的(如上图中的步骤3)
(d)B的第二步读取数据库,发出读取请求,此时A的第二步写数据还没完成,读出了一个脏数据放入缓存cache(如上图中的步骤4)
即在数据库层面,后发出的请求4比先发出的请求2先完成了,读出了脏数据,脏数据又入了缓存,缓存与数据库中的数据不一致出现了
如何保证数据库与缓存的一致性
- 对删除缓存进行重试,数据的一致性要求越高,我越是重试得快。
- 定期全量更新,简单地说,就是我定期把缓存全部清掉,然后再全量加载。
- 对同一数据的读取/写入请求串行,保证同一数据的请求消息,都会路由到同一个服务上。一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去。串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
- 给所有的缓存一个失效期。
第四种方案可以说是一个大杀器,任何不一致,都可以靠失效期解决,失效期越短,数据一致性越高。但是失效期越短,查数据库就会越频繁。因此失效期应该根据业务来定。
面试题
数据库主从不一致性怎么办?
面试官心理分析
你只要用数据库主从,就可能会涉及到由于写入主库的数据量大,导致数据库主库同步到从库延迟问题,如果主从延迟,就一定会有数据一致性的问题,由于主从延时导致读取到旧数据,那么你如何解决一致性问题?
本知识点主要讨论这么几个问题:
(1