“尽信书不如无书”
每一次阅读,能够从中看到自己的不足,同时,能提出不一样的观点就更好了。不一样的观点被提出,不仅希望自己对文章内容理解从表面和认同转向深入和探索,也希望自己融入更多元的视角,而不是“死读书,读死书”。因此,围绕传统分布式系统的 CAP 理论,谈谈自己对数据一致性、容错、容灾的一些看法。
▐ 原文 CAP 理论概要
通过CAP理论,我们知道无法同时满足一致性、可用性和分区容错性这三个特性,那要舍弃哪个呢?
CA without P:如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但其实分区不是你想不想的问题,而是始终会存在,因此CA的系统更多的是允许分区后各子系统依然保持CA。CP without A:如果不要求A(可用),相当于每个请求都需要在Server之间强一致,而P(分区)会导致同步时间无限延长,如此CP也是可以保证的。很多传统的数据库分布式事务都属于这种模式。AP wihtout C:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。现在众多的NoSQL都属于此类。
对于多数大型互联网应用的场景,主机众多、部署分散,而且现在的集群规模越来越大,所以节点故障、网络故障是常态,而且要保证服务可用性达到N个9,即保证P和A,舍弃C(退而求其次保证最终一致性)。虽然某些地方会影响客户体验,但没达到造成用户流程的严重程度。
对于涉及到钱财这样不能有一丝让步的场景,C必须保证。网络发生故障宁可停止服务,这是保证CA,舍弃P。貌似这几年国内银行业发生了不下10起事故,但影响面不大,报道也不多,广大群众知道的少。还有一种是保证CP,舍弃A。例如网络故障事只读不写。
孰优孰略,没有定论,只能根据场景定夺,适合的才是最好的。
▐ ACID 扩展阅读
一个事务本质上有四个特点ACID:
Atomicity原子性
Consistency一致性
Isolation隔离性
Durability耐久性
ACID一致性是有关数据库规则,如果数据表结构定义一个字段值是唯一的,那么一致性系统将解决所有操作中导致这个字段值非唯一性的情况,如果带有一个外键的一行记录被删除,那么其外键相关记录也应该被删除,这就是ACID一致性的意思。
CAP理论的一致性是保证同样一个数据在所有不同服务器上的拷贝都是相同的,这是一种逻辑保证,而不是物理,因为光速限制,在不同服务器上这种复制是需要时间的,集群通过阻止客户端查看不同节点上还未同步的数据维持逻辑视图。
当跨分布式系统提供ACID时,这两个概念会混淆在一起,Google’s Spanner system能够提供分布式系统的ACID,其包含ACID+CAP设计,也就是两阶段提交 2PC+ 多副本同步机制(如 Paxos)
▐ BASE 扩展阅读
由于我是个法学专业半路出家的程序员,理论不是我擅长,有不对和不专业的地方敬请大家指正。我主要是从自己设计、研发分布式系统的经验出发,谈谈自己对 CAP 概念不一样的感知和思考。
▐ 关于 CAP 理论对象的定位是否应该升级?
▐ 云原生对容错、容灾带来的变化是什么?
由于整个软件工程的基础设施在云计算的加持下产生了巨大变化,因此,对于容错和容灾也不能直接套用过往的经验和方法,否则,要么会过度设计、要么会设计不足甚至错误。下面,简单分享一下我个人对云原生对容错、容灾带来变化的一些思考。
云原生本质是什么?
有的观点说:云原生是一种基于云计算的弹性和分布式优势构建和运行程序的编程思想和设计、实现方法。有的观点认为:云原生就是基于云计算提供的能力构建应用程序并部署在云端。有的观点是:基于微服务、容器化、DevOps、持续交付为核心要素的软件开发模式。
这些观点都没有错,不同的目标、不同的视角和不同的应用场景下,都可以归纳云原生的定义。因为,软件工程乃至行业都在飞速发展和变化,其解决的问题随着时间推移愈加复杂。最初,使用 Pascal、Fortran、Basic 编程的时候,由于当时的程序大多是 CLI 输入和输出能力都比较匮乏,程序的功能也比较单一。在接触了 C/C++、VB.Net、C# 后,才对软件工程有了较为深入的理解。后来,在数据库技术盛行的时代,Oracle、MySQL 替代了 FoxBase 等电子表格,提供了关系型数据库能力、视图、事务等概念也极大降低了软件工程的复杂度。数据库技术之后就是服务技术的时代,互联网普及让软件从单机走向联网,网络编程技术加持下面向数据库编程转入面向服务编程。
面向服务编程带来了 CAP、ACID、BASE 想要解决的诸多问题,比如数据一致性问题。这个时代的服务一致性问题由于服务较简单,例如:状态、鉴权、数据请求等,都可以用数据一致性来解决,但是,服务是对数据加工处理后基于程序逻辑提供的,因此本质上还是有所不同,ORM 或 DataAdapter 除外。随着服务的增多,服务的托管因硬件更迭被摩尔定律推动的速度极快,自己购买并托管服务器被容器化技术打败,这才是云计算出现的根本原因:成本。不更新服务器会限制服务用户的能力,更新服务器又跟不上硬件迭代发展的速度,而容器技术把硬件标准化且隐去了,卖计算机到卖计算(当然还有存储),这一字之差使云计算的成本大幅降低。
由于老板觉得用云计算好,而程序员积累的技术生态和技术栈还停留在托管服务器时代,这中间的巨大的矛盾和需求推动了容器编排、微服务、DevOps 等技术的发展。有了这些云技术基础设施和云技术生态的双重加持,云原生应用设计、开发成本逐渐降低,老板心中的云计算成为了技术人员心中的云原生。所以,云原生本质上是把软件工程的技术栈和技术生态云化,并以此为基础的设计和构建软件工程之方法。
软件工程技术栈云化对容错、容灾带来的改变是什么?
如果软件工程技术栈云化,基于这些云化的基础设施进行软件工程,最大的容错、容灾变化就在“可控性”上。表面上看可控性变差了,因为我把更多容错、容灾能力交给了云计算基础设施和云原生技术栈,因而无法直接管控。但是,事实上这些云计算基础设施和云原生技术栈,每天都在经历各种软件开发和应用场景的考验和挑战,这将使这些基础设施和能力更加成熟和稳健。就像使用开源框架,表面上看复杂的框架中任何的问题和 BUG 都会带来不可控性和风险,但流行的开原框架在各种 Issue 和 MergeRequest 以及各种应用场景的历练下,各种牛人 CodeReview,比自己开发的框架更稳健和可控。
既然云原生更加稳健和可控,我就不用关心容错、容灾了吗?非也。任何稳健和可控都是有代价的,比如对数据库的可用性做主从设计、备份冗余设计等,都是用额外成本来供给健壮性。云原生则用 SLA(服务水平协议)来描述自身的健壮性,更高的 SLA 可能需要支付更高的费用。但是,如果你深究 SLA 则会发现,不同类型的云原生能力或服务 SLA 是不同的,比如:消息队列的 SLA 和图片识别服务的 SLA 是不同的,人脸识别服务的 SLA 应用在登陆和安检等场景与应用在生成游戏 Avatar 的要求也是不同的,这里容错和容灾的关系就很微妙了,很难清晰的定义这些场景里 SLA 的意义,以及这些意义在容错和容灾分别带来的挑战和要求是什么?因此,需要针对具体的应用对 SLA 理解,然后针对性设计容灾能力。
此外,如果简单认为云原生可以不关注容灾,只根据使用场景和 SLA 来进行容错设计,那么最终会形成什么情况?拿交易支付为例,如果我的服务都在杭州的某个集群,而这个集群因为骨干网问题失联了,云原生的云计算供应商会帮助我把请求都路由到江苏宿迁的另一个集群,如果我使用了云原生的数据存储,那么数据也可以在另一个集群被一致性访问,如果我使用了云基础设施自己处理数据同步和一致性,将对这种情况束手无策,无法完成容灾或灾难恢复,我将丢失交易支付信息造成用户无法支付。因此,当我将整个应用都基于云原生技术栈进行设计和构建,则只需要关注容错部分,容灾部分通过 SLA 保证就足够了。如果云原生技术栈无法完成软件工程的设计和构建,则需要把基于云原生和非云原生的混合架构中容错和容灾的部分识别出来,分别进行特殊的设计和构建、部署,从而对非云原生的部分做好容灾。
▐ 我做容错和容灾的基本方法是什么?
一语以蔽之:我做容错和容灾的基本方法就是识别不确定性后改造它们以提供确定性。而这里所指的不确定性大体有两类,一类是错误,另一类是灾难。错误和灾难指什么?作为法学专业毕业的,举个法律上的例子:错误对应过失和灾难对应不可抗力,为什么这么说呢?在法律上对过失犯罪的定义有两种:过于自信的过失和疏忽大意的过失。过于自信的过失只能够预见结果的发生但轻信可以避免,疏忽大意的过失则是指应该预见结果的发生但没有预见到。
对于过于自信的过失比较好理解,就是在写代码的时候,虽然知道可能发生一些错误,但因自己凭经验判断这种异常情况和边界值不会发生,从而在设计和构建应用的时候,没有引入保护性编程、单元测试等预防手段,也没有进行错误捕获和自定义错误处理等保护措施。疏忽大意的过失则比较有趣,什么叫做应该预见结果的发生呢?首先应该是软件工程中的基本方法、理念、思想,例如:高内聚低耦合、单一职责等。如果以高标准来看,则是那些顶级程序员能够想见的都应该被预见,这显然有点难和理想化,按下不表,后面机器学习部份再说。
对于灾难在法律上指不可抗力,在软件工程里就是那些人力无法干预和改变的情况,比如:断网、断电、硬件故障等。这些不可抗力在法律上可以免除责任,但是,在软件工程里“不可抗力”很可能造成商誉损失、用户体验损失、收入损失等,因此,软件工程里需要付出适当的成本进一步降低这些问题发生的概率。而对于“付出多少?降低多少?”这种问题,要根据实际的投入产出比来计算和取舍。然而,云原生的供应商可以借助规模效应,在不同应用间平衡这些成本,因此具有巨大的成本优势。所以,云计算公司收入的来源之一是:容灾成本随用户规模扩大而下降,利用容灾价值创造巨大利润。
因此,容错和容灾的不确定性识别,首先可以从:过失——软件工程、不可抗力——云原生基础设施两个方面考量。先说不可抗力——云原生基础设施容灾能力部分,这部分的不确定性主要来源于:行业标准。假设对服务的容灾能力有特殊需求,如果出了问题比如云服务商关键基础设施挂了,如果该云服务商遵从行业标准,而不是只想着用云原生基础设施把我 Lock-in,我可以轻松迁移到另一个遵从行业标准的云服务商,继续对用户提供服务。反之,如果我选择的云服务商不遵从行业标准,就会给我带来巨大不确定性,我可能因为自己的业务不能在极端情况下迁移,而选择那些遵从行业标准的云服务商。
对于混合云则有所不同,某些应用使用的云基础设施可能更基础、不那么云原生。例如在自己处理数据存储时,不确定性在容灾层面需要考虑数据存储的冗余、日志和数据恢复能力等等。假设出现不可抗力的情况时,能够在另一个容器、集群上借助这些能力进行灾难恢复。这时的不确定性,不仅来源于对云服务商的选择,还来源于选择的云基础设施的弹性缩扩容能力、容器启动能力、日志和数据传输能力、冗余数据存储成本等。把灾难恢复能力的确定性设计和构建好,就是对灾难发生和其损害的不确定性的抗争,其间利弊权衡,既包含设计和构建软件工程的复杂度,还包含其耗费的成本。
容错则更加复杂,因为错误本就是个主观概念:代码出错、程序出错、组件出错、模块出错、子系统出错、系统出错等等,甚至一些情况本来没有出错,但在运行时用户使用是错误的。例如:正确的显示闹钟的时间调整,但给过去设置闹钟对用户来说是没有意义的,如果程序在用户设置闹钟时提供了对过去时间的选择,从程序本身来说是没有错的,但对于产品设计或交互设计来说却不正确,这就把错误的外延伸到产品和交互设计领域。因此,容错概念范畴内对错误的定义应该收敛至:程序无法继续执行、服务无法正常响应等等,需要从具体的业务场景中,根据实际功能要求来进行探查。
对于容错中错误的定义,除了上文向外扩展的看,还可以向内收敛的看。向内收敛的看,可以给错误下一个较为清晰的定义:系统、程序或用户抛出的异常,而容错则是:对系统、程序或用户抛出异常的容忍度。怎么理解呢?比如:系统异常是指程序的系统级函数调用发生错误,如下代码:
if ((pid = fork()) < 0){
// strerror 返回一个文本串,描述了和某个error值相关联的错误。
fprintf(stderr, "fork error: %s\n", strerror(errno));
exit(0)
}
fork()
系统函数创建进程的时候,可能因为系统描述符满了,无法完成调用而产生异常。此时,系统会返回异常值来标识异常的类型等信息,这就是系统异常的一种。示例中用
pie<0
判断调用异常进行捕获,还有很多语言提供的异常捕获能力。捕获异常后进行的处理有很多类型,示例中的异常就可以用重试来提升容忍度,从而达到一定的容错效果。
▐ 对容灾和容错的一点儿展望
如前所述:只要在某个领域处理的异常足够多,在某个领域里容错设计和构建的能力就足够强。假设,这里把程序员用算法模型替代会怎样?之所以在某个领域处理的异常足够多,是因为在这个领域写了或看了(CodeReview)足够多的代码,或者处理过足够多线上、线下的故障和问题,那么,就能够牢牢记住各种系统、程序和用户异常的情况和特点,未来在写代码的时候能够迅速对这些情况进行识别、反应、设计和构建对应的容错能力。因此,如果把这些过程对应的信息蒐集起来形成标注数据:xx异常对应xx类别和xx容错处理,这就像 VSCode 等 IDE 提供的代码模板一样,教会算法模型识别、反应、设计和构建对应的容错能力。
如果初期算法模型不是很精确,可以把这些能力放在 IDE 里,作为提高编码效率的手段,让程序员来决定选择哪种方式进行容错处理。程序员的选择可以帮助我不断校准模型算法,让模型算法识别、反应、设计和构建容错能力的召回率和准确率不断攀升。最终,有信心的把这些能力部署到线上,在线上的程序出现异常而没有被捕获、处理时,则由算法模型自动识别、反应、设计和构建容错能力,用容错能力帮助线上的程序进行容错处理,这或许就是:自愈合。
当然,这仅是自愈合的一种,有些简单的方法例如:自动执行预案、自动回滚等等,很多方法都可以基于规则得到良好执行,只有当这些规则发生冲突、无法判断等各种复杂情况下,我才需要考虑用算法模型自愈合。希望未来在这个方面能够有更进一步的研究和实践再分享出来,感兴趣的也可以联系我一起交流探讨。
我们是阿里巴巴-大淘宝技术-导购和营销产品(原频道与D2C智能团队),也是阿里经济体前端委员会智能化方向的核心团队,隶属于大淘宝技术(大淘宝技术,一支致力于成为全球最懂商业的技术创新团队,旗下包含淘宝技术、天猫技术等团队和业务,是一支是具有商业和技术双重基因的螺旋体)。
本文分享自微信公众号 - 大淘宝技术(AlibabaMTT)。
如有侵权,请联系 [email protected] 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。