强一致性和弱一致性
分区引发的分布式一致性问题
当数据出现分区(分区不一定等于分片,分片是指数据划分多个域,分区多指复制,分区包含分片的概念)
根据CAP原理(CAP原理来源于 FLP不可能原理),数据出现分区P,那么一致性C和有效性A就得二选一。
我们如果强调一致性,即CP,那么就会出现,数据会一段时间不可读,也就意味着系统长时间不可用。
如果我们强调系统可用性,即AP,那么就会出现,数据会有一段时间读取旧数据,也就意味着系统对外提供的数据和实时数据不一致。
FLP 不可能原理:在网络可靠,存在节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性算法。
分区所面对的场景
分区所面对的场景有很多
其中最常见的就是DB缓存。
我们把数据存储在DB,然后用redis等缓存节点,cache数据,提高查询性能。
也就是说,我们得把部分mysql数据cache到redis中。
我们的web系统会收到两种请求:读请求和写请求。
读请求
读取数据,发现有数据就直接返回,发现没有数据就从DB读取,并缓存到redis中。
非常简单的套路。
写请求
写请求我们有几个问题需要确认:
- 操作顺序: DB和redis先操作谁?
- 是否删除缓存:要不要删除缓存?
然后我们可以得出4种策略:
1. 先更新DB,后更新cache
2. 先更新DB,后删除cache
3. 先更新cache,后更新DB
4. 先删除cache,后更新DB
思考在访问DB或者redis失败的情况下,这个几种场景会发送什么问题,以及解决方案。
这就是一道复杂的场景分析题了,按照几种失败场景合理分析即可,下面我做个演示。
1. 先更新DB,后更新cache
先更新DB,后更新cache失败,那么按照读请求会一直命中cache的旧数据,除非cache再次被更新或者删除。
更新失败的话,最好不要反复重试更新,因为更新很多时候是不等幂的,比如你在重试的时候新的更新事件来了,那么重试的数据很容易覆盖了最新数据。
2. 先更新DB,后删除cache
先更新DB,后删除DB,如果删除失败,也会出现旧数据的问题,但是他比更新数据更加友好,而且节约宝贵的缓存空间。还有一个好处就是删除是等幂的,也就说你不小心删除了最新数据也不怕,因为读请求会以DB存储的为准,重新Load。
3. 先更新cache,后更新DB
这个和先更新DB比起来,有一个大问题,就是DB可能没有写入成功,但是你却写入缓存,而我们缓存的数据要求要先在DB存在,显然DB没有写入成功看起来很糟糕,而且失败重试不等幂。
4. 先删除cache,后更新DB
这个看起来还过得去,先删除cache,失败,也就不会去更新DB了。如果更新DB失败,那么该次请求要么放弃(放弃的话还好),要么重试,如果重试的话,延迟会比较久,之前删除的缓存很可能又被旧数据填充了。
总结
基本上,就是往并发脏读,失败重试等幂性这个方向去考虑问题即可。
面对的问题
缓存穿透,即查不存在db中的key,导致访问db。可以用bloom过滤拦截不在db的key。
缓存雪崩,即多个key同一时间过期,加随机数,避免同样的过期时间。
缓存击穿,同一个key过期时被DB被并发,可以采用互斥锁,确保一个进程进行load db。
其他手段
-
给缓存的key设置随机的expire时间,让缓存自动过期。
-
用队列或者binlog日志的方式主动删除key。
-
bloom过滤安全防护。
-
用线程池控制缓存key的写并发数。
-
考虑失败重试是否等幂
引用: https://www.cnblogs.com/semi-sub/p/13735800.html