微服务架构中的数据完整性-如何确保没有分布式事务和紧密连接

大家好 如您所知,我过去经常和谈论有关存储,Vertica,大数据存储和其他分析性内容。 现在,所有其他数据库都属于我的职责范围,不仅是分析型数据库,还包括OLTP(PostgreSQL)和NOSQL(MongoDB,Redis,Tarantool)。


这种情况使我可以将具有多个数据库的组织视为具有一个分布式异构(异构)数据库的组织。 一个单一的分布式异构数据库,由一堆PostgreSQL,Redis和Mong ...以及一个或两个Vertica数据库组成。


这个单一的分布式基础的工作产生了很多有趣的任务。 首先,从业务角度来看,重要的是,在这样的基础上移动数据,一切正常。 我没有专门使用术语“完整性,一致性”,因为 该术语很复杂,在考虑DBMS(A C ID和C AP定理)的不同细节上,它具有不同的含义。


如果公司试图切换到微服务架构,则具有分布式基础的情况将更加恶化。 在此之下,我将讨论如何在微服务架构中确保数据完整性而又无需分布式事务和紧密连接。 (最后,我解释了为什么我选择本文的插图)。



克里斯·理查森(克里斯·理查森)(微服务体系结构最著名的传播者之一)认为,这种体系结构有两种使用数据库的方法:共享数据库和每服务数据库。



共享数据库是良好的第一步,对于没有雄心勃勃的增长计划的小公司来说,这是一个很好的解决方案。 此外,从微服务架构的角度来看,这种模式本身就是一种反模式,例如 共享同一基础的两个服务不能独立测试和扩展。 即 而是,这些服务是一种趋于成为一体的服务。


每个服务的数据库模式假定每个服务都有自己的数据库。 一个服务只能通过API(广义上)访问另一个服务的数据,而无需直接连接到其数据库。


每个服务的数据库模式允许相应服务的团队根据需要选择数据库。 有人能够使用MongoDB,有人相信PostgreSQL,有人需要Redis(此服务可接受关闭时发生数据丢失的风险),并且有人通常将数据存储在磁盘上的CSV文件中(以及为什么实际上,不?)。



使用这种“动物园”数据库将恢复数据顺序到全新的复杂性水平的任务。


ACID和微服务架构


让我们看一下通过基于DBMS的经典ACID要求集整理事物的任务:我们将扩展缩写的每个字母的本质,并说明在微服务体系结构中使用该字母的困难。


(A)CID-原子性。 原子性-全部或全部。


根据原子性要求,必须完成所有步骤(可能重复),如果重要步骤失败,则取消已完成的步骤。


上图演示了购买VIP服务的测试过程:在记账中预留了钱(1),为用户激活了奖励服务(2),将用户类型更改为专业(3),在记账中记入了预留的钱(4)。 所有四个步骤必须完成或未完成。



在这种情况下,您不能挂在进程的中间,因此,异步是可取的,在极端情况下,应与内置超时保持同步。


A(C)ID-一致性。 一致性-每个步骤都不应与边界条件相矛盾。


例如,从服务1中的客户A向服务2中的客户B汇款的条件的经典示例:这种汇款的结果不应减少(在转移过程中不应该损失钱款)或更多(将相同的钱汇给两个用户是不可接受的)同时)。 为了符合此要求,您需要在某处对条件进行编码并检查条件的数据(理想情况下,无需进行其他调用)。



ACI(D)-耐久性。 耐久性要求意味着操作的影响不会消失。


在Polyglot持久性条件下,服务可以在数据库上运行,该数据库可以定期“丢失”记录在其中的数据。 如果在本地数据库中启用了异步复制,即使从诸如PostgreSQL之类的固态数据库中也可以获得类似的技巧。 该图显示了如何通过刻录主服务器来销毁记录在主服务器中但未通过异步复制到达从服务器的更改。 为了确保耐用性要求,必须能够正确诊断并弥补此类损失。



你问我在哪里?


而且无处。 在几个独立的异步服务环境中进行隔离是一项技术要求。 现代研究表明,可以在没有隔离的情况下实施真实的业务流程。 隔离通过最小化并发来简化思考(对于程序员而言,开发并行计算更加困难),但是微服务架构本质上是大规模并行的,因此在这种环境中进行隔离是多余的。


有许多方法可以达到上述要求。 所谓的两阶段提交(2PC)提供了最广泛使用的分布式事务算法。 不幸的是,实现两阶段提交需要重写所有涉及的服务。 最严重的是:该算法不是很有效。 最近的研究表明,该算法在两台服务器的分布式基础上显示出一定的性能,但是随着服务器数量的增加,生产率并没有线性增长……或者,它根本没有增长。



微服务架构的主要优势之一是能够通过简单地添加越来越多的服务器来线性地提高性能。 事实证明,即使服务器数量增加,如果我们使用两阶段提交来确保分布式完整性,那么此过程将成为瓶颈,成为生产力增长的限制因素。


如何在没有两阶段提交的情况下确保分布式完整性(ACiD要求),并且能够线性地扩展性能?


现代研究(例如, 《分布式并发控制评估》,VLDB,2017年 )认为,所谓的“乐观方法”可以提供帮助。 两阶段提交与广义“乐观方法”之间的区别可以通过旧的苏联商店(带柜台)和现代超市(如欧尚)之间的区别来说明。 在设有柜台的商店中,每个顾客都被视为可疑商品,并受到最大程度的控制。 因此,界线和冲突。 在超级市场中,默认情况下,买方被认为是诚实的,他们给他机会接近货架并装满手推车。 当然,有监视工具可以捕获骗子(摄像机,安全性),但是大多数购买者都不必对付它们。


因此,只需放置更多的收银台即可扩展,扩展超市。 它与微服务体系结构相似:如果通过“乐观的方法”确保分布式完整性,则仅在出现问题的进程中附加检查。 正常流程无需额外检查。


这很重要。 “乐观方法”包括几种算法。 我想告诉您有关saga的故事-克里斯·理查森(Chris Richardson)推荐的用于维持分布式完整性的算法。


Sagas-算法元素


sag算法有两个选择。 因此,首先,我想通用地描述算法的必需元素,以便该描述适用于两种选择。


要素1.服务之间可靠的事件传递持久通道,保证“至少一次传递”。 即 如果过程的步骤2已成功完成,则关于此的通知(事件)应至少到达步骤3,重复的交付是可以接受的,但不应丢失任何内容。 “永久”表示通道必须存储一段时间(每周2-3天)的通知,以便由于数据库丢失而丢失了最新更改的服务(请参见持久性示例,在图示中,这是步骤2)。通过重播频道中的事件来进行这些更改。



元素2。通过使用唯一的等幂密钥来调用服务的等幂。 想象一下,我(用户)启动了购买VIP套餐的过程(请参见原子性示例)。 在过程开始时,我会得到一个唯一键,即幂等键,例如42。接下来,应在显示的幂等键下调用每个步骤(1→2→3→4)。 在上面的段落中,提到了将同一消息重复到达服务(在步骤中)的可能性。 服务(步骤)应该自动能够忽略已处理事件的重复到达,并通过幂等键检查重复。 也就是说,如果所有服务(处理步骤)都是幂等的,那么为了满足原子性和持久性的要求,将其重定向到与来自通道的事件相对应的步骤就足够了。 跳过事件的步骤将执行它们,而已经完成事件的步骤将由于幂等而忽略它们。



元素3.通过幂等键取消服务调用(步骤)。


为了确保原子性(请参见示例),例如,如果具有等幂键42的过程在步骤3中停止/掉落,则有必要取消对键42的步骤1和2的成功执行。为此,每个强制性过程步骤都必须具有“补偿”步骤,一种API方法,可取消执行指定幂等键的所需步骤(42)。 作为sag算法实现的一部分,补偿性呼叫的实现是服务优化中的困难但必要的元素。



上面列出的三个元素与“ sag”的两个实施版本都相关:编排和编排。


精心策划的Sagas


用于编排Sagas的更简单,更明显的算法更易于理解和实现。 在一篇出色的文章中, kevteev描述了Avito中编排Sagas机制的算法和实现过程。 他们的算法假定存在控制服务,即在服务的业务流程的框架内“协调”服务调用。 相同的监视服务可能拥有自己的数据库(例如PostgreSQL),该数据库充当可靠的持久事件传递通道(元素1)。


编舞Sagas


编舞的传奇比较棘手。 在这里,实现以下要求的数据总线应充当可靠的持久通道:即发即忘发布,发布-订阅事件传递,至少传递一次。 即 每个过程的每个步骤都应从总线接收命令以进行操作,并向其发送有关成功完成以及下一步开始的消息,以便他也可以从总线读取该消息并继续该过程。 此外,对于每个消息,可以有多个订户。


编舞传奇还应该提供控制服务,既可以提供Sagas服务,又可以提供“轻量级”服务。 服务应了解系统中注册的业务流程,每个流程中包含的步骤的组成。 他还应该收听总线,监视每个过程的执行(每个幂等性密钥),并且只有在出现问题时才对特定步骤进行“重复”,或者对所采取的步骤进行“取消”,“补偿”。


细微差别


萨加斯人最不同于传统交易的最细微差别之一是背离了每个步骤的线性,顺序和义务。 传奇不一定是线性的事件链,它可以是有向图:新的用户注册事件可以并行产生几个步骤(发送SMS,注册登录名,生成密码,发送电子邮件),其中某些步骤是可选的。 粗略地看,在带有可选步骤的“分支”传奇中,很难确定传奇(过程)的完成,但是实际上,一切都很简单:当所有必需步骤以任意顺序完成时,传奇(过程)就完成了。



第二个细微差别是编排Sagas的典型特征,但对于编排Sagas也是可能的,它是选择一种方法来注册业务流程,即Sagas服务中的Sagas类型。 原子性示例描述了四个连续的必需步骤的过程。


谁注册了此过程,指出了所有步骤,放置了依赖项和强制性步骤? 一个明显但过时的答案是,流程注册应在sag服务中集中进行。 但是这个答案与微服务架构不是很一致。 在微服务架构中,注册自下而上的流程更有希望,生产率更高且速度更快。 即 不要在sag服务中写下流程的所有细微差别,而要允许单个服务自己“适应”现有流程,表明它们的绑定/可选性质和强制性的前身。


即 在sag服务中注册用户的过程最初可以包括三个步骤,然后,在系统开发过程中,将再包含七个步骤,并写出一个步骤,其中将有九个步骤。 这样的“无政府主义”和“分散式”方案很难测试,难以实施严格,协调的流程,但对于敏捷团队而言,要进行持续的多方向产品演进,要方便得多。


其实在这里 经过认真的介绍,我认为值得完成,否则这篇文章太大了。




这是此材料演示文稿的链接 ,我在Highload Siberia 2018上就此主题做了报告。
UPD-会议视频:





结语


最后,我想尝试用一种更具比喻性的语言来解释以上所有内容。
毕竟,从一开始就是一个传奇? 这个情节,这个来自中世纪的冒险...或者来自《权力的游戏》。 发生了一个事件(一场战斗,一场婚礼,某人死亡),有关这一消息的消息通过信使,信鸽,商人而在世界范围内传播。 当新闻传到感兴趣的人时(一周,一个月,一年),他们会做出反应:他们派遣军队,宣战,处决某人,并且新消息飞扬。


没有监管机构来监控行动顺序。 从取消操作的意义上说,没有事务,没有回滚,就好像从未执行过一样。 所有这些都是成年人的行为,它永远发生。 它可以得到补偿,但这恰恰是行动(谋杀)和补偿(为头部,病毒付费),而不是废除死亡。


事件需要很长时间,它们来自不同的来源,动作并行发生,而不是严格顺序发生。 很多时候,新参与者突然出现在情节中,他们决定参加(龙来了;))……而一些老参与者突然死了。


这样的事情。 似乎是一团糟,但一切正常,世界的内部协调并未中断,情节正在发展并保持一致……尽管有时是不可预测的。

Source: https://habr.com/ru/post/zh-CN426101/


All Articles