Chris Richardson微服务翻译:微服务之事件驱动的数据管理

Chris Richardson 微服务系列翻译全7篇链接:

原文链接:Event-Driven Data Management for Microservices


微服务与分布式数据管理问题

单体应用一般只有一个关系型数据库,这样的好处是可以实现 ACID 保证:

  • 原子性(Atomicity):原子粒度的更改
  • 一致性(Consistency)数据库的状态始终保持一致
  • 隔离性(Isolation):并发的事务表现的像是串行执行,事务之间不会互相影响
  • 持久性(Durable):一旦事务提交就不会撤销

因此,应用可以简单的开始事务,更改(增删改)多行数据,然后提交事务。

使用关系型数据库另一好处是支持 SQL。SQL 是一种丰富的、声明式的标准查询语言,用户能简易的关联查询多个表中的数据,然后RDBMS 查询调度器会执行最优的查询方式,用户不必关系底层的细节。所有的数据在一个数据库中也方便查询。

然而微服务架构中数据访问变的复杂,因为每个微服务都拥有独立的数据库,仅能通过 API 来访问。数据封装保证了微服务的松耦合,各个服务可以独立其他服务演进。而如果多个服务访问同样的数据,架构更新会更耗费时间,也需要更多的服务协调。

不同服务可能使用不同类型的数据库,现代应用存储和处理各种各样的数据,关系数据库并不总是最好的选择。一些场景下,一种特殊的 NoSQL 能提供更方便的数据模型、更好的性能和可扩展性。例如:使用 ElasticSearch 这样的搜索引擎来进行文本的存储和查询;使用 Neo4j 这样的图谱数据库来存储社交图谱数据。因此,微服务应用会混合使用 SQL 和 NoSQL 数据库,即多态型数据持久方案。这也带来了一些挑战:

1)如何跨多个服务实现事务,维护数据的一致性。我们以 B2B 商店为例:客户服务维护用户信用额度等相关的信息,订单服务管理订单并确保新订单没有超过用户的信用额度。单体应用中,订单服务可以使用 ACID 事务来核对用户信用额度并新建订单。而在微服务架构中, order 和 customer 表是各个服务私有的:

订单服务无法直接访问 customer 表,只能通过客户服务的 API。订单服务可能使用分布式事务,也被称为两阶段提交(2PC),然而 2PC 在现代应用通常不是很好的选择。CAP 定理需要用户在可用性和 ACID 的一致性中二选一,通常可用性是更好的选择。此外,很多NoSQL 并不支持 2PC,维护服务和数据库中数据的一致性是很重要的,因此我们可以选择另一种方案。

2)另一个挑战是如何检索多个服务中的数据,例如应用需要显示一位客户和他最近的订单,如果订单服务提供了用户订单的查询 API,那么可以在应用端获取该数据,应用端通过客户服务检索客户,再通过订单服务检索该客户的订单。假设订单服务只支持通过主键来查询订单,此时就没有合适的方法来检索所需数据了。

事件驱动的架构

对于许多应用,解决方案就是事件驱动的架构:服务在业务发生时(例如更新一条记录信息)会发布一个事件,其他微服务订阅该事件,当某一微服务接收到事件就会更新自己的业务记录,然后其他更多的事件会被发布。下图展示了如何使用事件驱动的方式在创建订单时检查可用信用,微服务间通过 MQ 来交换事件:

1)订单服务创建状态为 NEW 的订单,然后发布『订单创建』的事件

2)客户服务获取『订单创建』事件,为此订单保留信用,发布『信用保留』事件

3)订单服务获取『信用保留』事件,将订单状态修改为 OPEN

更为复杂的场景会涉及更多的步骤,比如核对用户信用时预留库存。

基于(a)每个服务原子性的更新数据库并发布事件;(b)MQ 确保事件至少交付一次,那么就可以实现跨服务的业务逻辑了。这并非 ACID 事务,他提供更弱一点的最终一致性。这种事务模型可称为 BASE model

也可以使用事件维护关联多个微服务的物化视图。维护此视图的服务订阅相关事件并更新视图,例如:用户订单视图通过订阅订单事件和用户事件来进行更新:

当用户订单服务收到用户服务或订单服务的事件时,会更新视图。可以使用类似 MongoDB 的文档数据库为每个用户存储一份用户订单的文档。

事件驱动架构的优点:

  1. 他使得事务能跨多个服务并提供最终一致性;
  2. 使得应用可以维护物化视图。

不足之处:

  1. 编程模型比 ACID 事务更加复杂,为了从应用级别的错误中恢复,需要完成补偿事务,例如:信用检查不成功则必须取消订单;
  2. 临时事务会导致不一致的数据。另外应用从物化视图中读取的数据未能及时更新,也会产生不一致的问题;
  3. 必须检测并忽略重复事件

实现原子化

事件驱动架构还存在一个问题:以原子粒度更新 DB 与发布事件。例如:订单服务在订单表中 insert 一行记录,然后发布『订单创建』的事件,这两个操作需要是原子性的,否则,更新 DB 后,发布事件前服务崩溃了,系统将存在不一致。确保原子性的方法是使用 分布式事务 调用 DB 和 MQ。然而由于 CAP 理论,我们是想避免这么做。

使用本地事务发布事件

应用发布事件并保证原子性的方法之一就是:多步骤本地事务方法。技巧是 DB 中有一张 EVENT 表(模拟消息队列),存储业务数据的状态。首先启动一个本地数据库事务,更新业务数据记录并往 EVENT 表中插入一条数据,最后提交事务。一个单独的线程会轮询 EVENT 表,将查询结果往 MQ 中发送事件消息,然后使用本地事务标注事件状态为已发布。如下图所示:

订单服务首先往 ORDER 表中 insert 一行记录,然后在 EVENT 表插入类型为 Order Created 的事件(状态为 NEW )。事件发布线程或进程轮询 EVENT 表中未发布的事件,发布事件然后更新 EVENT 表事件状态为已发布。

这种方法的优点:

  1. 保证每次更新都有对应的事件发布,不使用两阶段提交(2PC);
  2. 应用发布了业务级别的事件,消除推断事件类型的麻烦。

不足:

  1. 易出错,因为要求开发者必须记得更新后去发布事件;
  2. 当使用 NoSQL 时,因为 NoSQL 的事务和查询能力有限,实现起来较麻烦。

挖掘数据库事务日志

另一种不需要两阶段提交就能实现原子性的方法是:分析数据库事务日志或提交日志来发布事件。应用更新 DB时,DB的事务日志会记录这些变更,事务日志挖掘线程或进程读取这些日志,并将事件发布到 MQ,如下图所示:

范例之一就是开源的 LinkedIn Databus 项目,Databus 挖掘 Oracle等数据库的事务日志并发布相应的事件,LinkedIn 使用 Databus 来保持各种派生数据存储和记录系统的一致。

另一范例就是 streams mechanism in AWS DynamoDB,AWS DynamoDB 流包括 DynamoDB 表在过去 24 小时内的时序变化,包括新建、更新和删除操作。应用能读取这些变更,将其作为事件发布。

事务日志挖掘的优点:

  1. 能保证无需使用两阶段提交就能对每个更新发布事件;
  2. 简化应用,将事件发布与主业务逻辑分离。

不足:

  1. 每个 DB 或 同一 DB 的不同版本的事务日志格式不同;
  2. 很难从低级别事务日志的更新记录中反推高级别的业务事件。

使用事件源

事件源通过采用一种截然不同的、以事件为中心的方法来保存业务实体,而不需要 2PC 来实现原子性。这种方法存储一系列状态变动的事件,而不是实体的当前状态。应用通过重放事件来构建实体的当前状态,每当业务实体的状态改变,就往事件列表中添加新的事件。由于保存事件是唯一操作,本质上就是原子性的。

以订单为例:传统方案中,每个订单为 ORDER 表中的一行记录。使用事件源时,订单服务存储导致订单状态变化的事件,包括创建、批准、配送、取消。每个事件由充足的信息来重新构建订单:

事件被存储 DB 中,可使用 API 添加或查找实体的事件。事件存储类似上文提及的 MQ,提供 API 让其他服务订阅事件,将事件传达至感兴趣的订阅者。事件存储是事件驱动的微服务架构的支柱。

事件源的优点:

  1. 解决了事件驱动的微服务架构的关键问题,能够可靠的发布事件;
  2. 解决了数据一致性问题,由于存储事件而不是领域对象,也避免了面向对象到关系数据库的不匹配问题;
  3. 为实体提供了100%可靠的审计日志,使得获取任何时间点的实体状态成为可能;
  4. 业务逻辑与事件交互的业务实体是松耦合的,这使得单体服务迁移到微服务更容易。

不足:

  1. 采用了陌生的编程风格,学习曲线陡峭;
  2. 事件数据库只支持通过主键查询业务实体,必须使用 CQRS(Command Query Responsibility Segregation)来完成查询,因此应用程序必须采用最终一致性。

总结

微服务架构中,每个微服务都有自己的数据存储,不同的微服务可能使用不同的 SQL 和 NoSQL 数据库。这些数据库架构有很多优势,也带来了分布式数据管理的挑战。第一个挑战就是如何实现跨服务的业务事务,并保证一致性;第二个挑战就是如何从多个服务中查询数据。
对于许多应用,解决方案就是使用事件驱动的架构。事件驱动的架构带来的挑战是如何原子化地更新状态和发布事件。有几种方案可以考虑,包括把数据库用作消息队列、事务日志挖掘和事件源。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容