作业1-12306的取舍

来源:2-22 本章作业

慕姐6517219

2022-12-16

作文1问题是:12306网站去买票的会遇到一个现象,页面上一直看到还有票,但是去下单购票的时候,又提示没有票了

我的思考:我知道这是因为一致性和可用性之间取舍的问题,如果保证强一致性,就会使得数据在同步和更新的时候所有其他用户都不能购票(即使有余票也不行),所以为了这一点12306设计这样的方式,虽然牺牲了一致性,但是却保证了高可用。但是这样设计会有什么问题吗?可能会造成用户体验不佳,但还会有什么问题呢?谢谢

写回答

1回答

大能老师

2022-12-16

这就是CAP理论的一种典型场景:在网络分区的情况下,一致性和可用性只能二选其一。


鉴于一致性与可用性存在冲突,以及实现一致性的代价过高这两个原因,在设计分布式系统时,放弃对严格一致性的约束,让系统去适应相对宽松一致性(弱一致性),从而在一致性、可用性和性能上取得相对可接受的平衡,是更加理性的选择。


所谓“弱一致性”,是在隔离性和性能等方面适当放宽要求后的一系列降级版一致性。相对的,我们之前讨论的一致性,也被称为“强一致”。最终一致是普遍采用的一种宽松一致。


也就是说通常在架构设计中我们会兼顾用户体验,放弃强一致。特别是用户并发量特别大的情况下,强一致是不现实的。举个例子:10000 个用户同时查询下单,部分用户处于支付中(或者排队抢票中),如果每次查询都采用真正校验实际库存,其实是不现实的,其实 12306 的余票计算可不是简单的库存的概念,远远复杂得多(可参考文章《12306互联网售票系统余票数据一致性保障技术方案研究.pdf》https://max.book118.com/html/2022/0420/6240015145004135.shtm)。所以强一致是不可能的,而且你还是在查询阶段。很多电商系统一般的设计原则也都是“读不做一致性校验,写的时候才进行校验”。


** 牺牲一致性需要守住两个底线:防止脑裂和要保证单调读写 **


底线一:防止脑裂


> 例如,传统MySQL主从结构中,如果主库宕机,或者网络分区导致无法访问主库,也不应该去更新从库中的数据,否则在故障结束后,系统面对主库和从库二份不一样的数据,是无法自动恢复的。这种情况被称为“脑裂(Split-brain)”,出现脑裂后,理论上系统的一致性不可恢复。


那么,如何防止脑裂呢?这个可以参考我们课程Paxos等一致性协议,保证更新操作的一致性。简单地说,就是每次更新操作必须在超过半数的副本上达成一致才算更新成功,如果在系统故障时,更新请求不能达成多数派一致,也必须让本次更新失败。


底线二:单调读写


最终一致系统在故障时,为了保证系统持续可用,应允许客户端从任意一个尚可访问的节点上读取状态数据。尽管这个时候,客户端读到的可能并非最新状态。对于绝大多数系统来说,短时间内读到一个并非最新状态都是可接受的。


> 例子:例子,以小明给小华转账来说明。在一个只有主从二副本的最终一致性系统中,转账成功后主副本的状态已更新,小明转给小华的钱已到账,小华的账户余额是100元。但由于同步延迟,从副本中转账还未到账,小华的账户余额还是0元。

>

> 假设小华第一次查询账户的请求被分配到主副本上,App显示余额100元。小华再次查询,这次查询请求被分配到了从副本上,App显示余额0元!刚到账的钱没了!


避免这个问题,就需要保证在客户端视角的一致性。所谓单调读写,要求对每一个客户端来说,每次读到的状态不能比上次一读写到的状态更旧。简单的说就是**“不能时序错乱”**。实现单调读写有两种常用的方法。


- **第一种方法是通过保持会话(Sticky Session)的方式**,让同一个客户端的请求总是由与之建立会话的那个特定的服务端节点(副本)处理。客户端只与服务端一个节点交互,自然就不会出现“时序错乱”的问题。


- **另一种方法是,通过记录和比较状态的版本号来实现单调读写。**


系统需要为状态数据维护一个版本号系统,状态版本号是状态的一部分,并且要确保每次状态更新,对应版本号都单调递增。(类似版本号的机制)


小结


在分布式系统中,平衡可用性和一致性是一个难题。推荐设计者在设计系统一致性时能够兼容最终一致,这样可以极大提升系统在面临故障时保持高可用的难度,在一致性和可用性上取得相对较好的平衡。但系统最终一致也不等于不一致,需要防止系统出现脑裂,并通过单调读写保证客户端视角的一致性。

1
1
洪小才
大佬强呀,66666
2023-01-12
共1条回复

Java分布式架构设计与开发实战

项目贯穿式讲解,真正将理论与实战相结合

325 学习 · 74 问题

查看课程