Skip to content

分布式架构的关键设计

企业数字化转型不仅要关注商业模式、业务边界、领域建模以及前中台的融合设计,同时由于中台大多采用分布式微服务架构,我们还需要关注分布式架构下的关键技术实现细节。诸如分布式架构下的数据库选择?数据如何同步和复制?高频热点数据的处理方式等问题。

选择什么样的分布式数据库

分布式架构下的数据应用场景远比集中式架构复杂,会产生很多数据相关的问题。谈到数据,首先就是要选择合适的分布式数据库。分布式数据库大多采用数据多副本的方式,让数据具有了高性能、多活和容灾能力。

目前有三类主要的分布式数据库解决方案,这些方案的差异主要在于数据多副本的处理方式和采用的数据库中间件的类型。

1)原生分布式数据库方案。它支持数据多副本、高可用。多采用Paxos协议,一次写入多数据副本,多数副本写入成功即算成功。

2)集中式数据库+数据库中间件方案。它是集中式数据库与数据库中间件相结合的方案,通过数据库中间件实现数据路由和全局数据管理。数据库中间件和数据库分别独立部署,采用数据库自身的同步机制实现主副本数据的一致性。

开源的集中式数据库主要有MySQL和PostgreSQL。基于这两种数据库衍生出了很多解决方案,比如开源数据库中间件MyCat+MySQL方案等。

3)集中式数据库+分库类库方案。它是一种轻量级的数据库中间件方案,分库类库实际上是一个基础JAR包,与应用软件部署在一起,实现数据路由和数据归集。它适合比较简单的读写交易场景,在强一致性和聚合分析查询方面相对较弱。典型分库基础组件有ShardingSphere。

综上,这三种方案实施成本不一样,业务支持能力差异也比较大。

原生分布式数据库大多由互联网大厂开发,具有超强的数据处理能力,大多需要云计算底座,实施成本和技术能力要求比较高。集中式数据库+数据库中间件方案,实施成本和技术能力要求适中,可基本满足中大型企业业务要求。分库类库的方案可处理简单的业务场景,成本和技能要求相对较低。

在选择数据库的时候,需要考虑自身技术能力、业务量、成本以及业务场景需要,以选择合适的数据库方案。

[]

如何设计数据库分库主键

选择了分布式数据库,第二步就要考虑数据分库,这时分库主键的设计就很关键了。

与客户接触的关键业务,建议以客户ID作为分库主键。这样可以确保同一个客户的数据分布在同一个数据单元内,避免出现跨数据单元的频繁数据访问。跨数据中心的频繁服务调用或跨数据单元的查询,会对系统性能造成致命的影响。

将客户的所有数据放在同一个数据单元,也更容易为客户提供一致性服务。而对企业来说,要具备"以客户为中心"的业务能力,要先在数据上做到"以客户为中心"。

当然,你也可以根据业务需要选用其他的业务属性作为分库主键,比如机构代码、用户ID等代码。

## 数据库的数据同步和复制

在分布式微服务架构中,数据会根据数据扩展能力要求被进一步垂直或水平分割。为了实现数据的整合,数据库之间的批量数据同步与复制是必不可少的。

数据同步与复制主要用于数据库之间的数据同步,实现业务数据迁移、数据备份、不同渠道核心业务数据向数据平台或数据中台的数据复制,以及不同主题数据的整合等。

传统的数据同步方式有ETL工具和定时提数程序,但在数据时效性方面存在短板。

分布式架构一般采用数据库日志捕获技术(CDC),根据数据库增量日志提取数据库增量数据,实现准实时的数据复制和传输。这种设计方式可以实现业务处理逻辑及数据复制和同步处理逻辑的独立与解耦,使用起来会更加简单、便捷。

现在主流的PostgreSQL和MySQL等数据库外围,有很多数据库日志捕获技术组件。CDC技术也可以应用于领域事件驱动设计中,作为领域事件增量数据的捕获工具。

## 跨库关联查询如何处理

跨库关联查询是分布式数据库的一个短板,会影响查询性能。

在领域建模时,很多原来在一个数据库的实体会被分散到不同的微服务中,但很多时候因为业务需求,它们之间需要关联查询。关联查询的业务场景一般包括两类。

第一类是基于某一维度或某一主题域的数据查询,比如基于客户全业务视图的数据查询,由于客户的业务数据会随着不同业务条线的操作而被分散在不同业务领域中,这类数据查询会跨多个微服务。

第二类是表与表之间的关联查询,比如机构表与业务表的联表查询。但机构表和业务表却分散在不同的微服务。

如何解决这两类业务场景下的查询呢?

对于第一类场景,由于数据分散在不同微服务里,我们无法跨多个微服务来统计这些数据。你可以建立面向不同业务主题的分布式数据库,它的数据来源于不同业务领域的微服务。我们可以采用数据库日志捕获技术和领域事件驱动机制,从各业务端微服务将数据准实时汇集到主题数据库。

在数据汇集时,需要提前做好数据关联处理(如将多表数据合并为一个宽表)或者建立数据模型。然后建立面向主题数据查询的微服务,这样你就可以相对容易地一次获取客户所有维度的业务数据了。你还可以根据主题或场景设计分布式数据库的分库主键,以提高大数据量条件下的数据库查询效率。

对于第二类场景,即不在同一个数据库的表之间的关联查询场景,你可以采用小表广播的设计模式。

比如,如果需要基于机构代码进行关联查询,我们可以在需要进行关联查询的业务库中增加一张冗余的机构代码表,这张表的数据只用于关联查询。机构代码表的数据在主数据微服务中进行生命周期管理,当主数据微服务中的机构代码数据发生变化时,你可以通过消息发布和订阅的领域事件驱动模式,异步刷新所有订阅了机构代码表数据的冗余表中的数据。通过异步消息广播和数据冗余的方式,既可以解决表与表的关联查询,还可以提高数据的查询效率。

## 如何处理高频热点数据

对于高频热点数据,比如商品、机构等代码类数据,它们同时面向多个应用,需要有很高的并发响应能力。这样会给数据库带来巨大的访问压力,影响系统的性能。

常见的做法是将这些高频热点数据从数据库加载到如Redis等缓存中,通过缓存提供高频数据访问服务。这样既可以降低数据库的压力,还可以提高数据的访问性能。

另外,对需要模糊查询的高频数据,你也可以选用ElasticSearch等搜索引擎。

缓存就像调味料一样,投入小、见效快,用户体验提升快。

## 前后序业务数据的处理

在微服务设计时你会经常发现,某些数据需要关联前序微服务的数据。比如:在保险业务中,投保微服务生成投保单后,保单会关联前序业务的投保单数据等。在电商业务中,货物运输单会关联前序业务的订单数据。由于这些需要关联的数据分散在业务流程的前序微服务中,你无法通过不同微服务的数据库来给它们建立数据关联。

那如何解决这种前后序的实体关联呢?

一般来说,前后序的数据都跟领域事件有关。你可以通过领域事件处理机制,按需将前序数据通过领域事件实体,传输并冗余到当前的微服务数据库中。

你可以将前序数据设计为实体或者值对象,供当前实体引用。在设计时你需要关注以下内容:如果前序数据在当前微服务只可整体修改,并且不会对它做查询和统计分析,那么你可以将它设计为值对象。当前序数据是多条,并且需要做查询和统计分析,你可以将它设计为实体。

这样,当货物运输的前端应用需要查看订单数据时,你可以在货物运输微服务中一次同时获取前序订单的清单数据和货物运输单数据,并将所有数据一次反馈给前端应用,这样就降低了跨微服务之间的调用。

如果前序数据被设计为实体,你还可以将存储在本地业务库的前序数据作为查询条件,在本地微服务完成多维度的综合数据查询。只有在必要时才从前序微服务获取前序业务实体的明细数据。通过数据冗余设计,即使前序微服务出现故障不能提供服务,也不会影响当前微服务与前序数据相关的业务和服务。

这样,既能保证数据的完整性,还能降低微服务的依赖,减少跨微服务的频繁调用,提升系统性能。

## 数据中台与企业级数据集成

分布式微服务架构虽然提升了应用弹性和高可用能力,但原来集中的数据会随着微服务拆分而形成很多数据孤岛,增加数据集成和企业级数据使用的难度。

你可以通过数据中台来实现数据融合,解决分布式架构下的数据应用和集成问题。可以分三步来建设数据中台。

第一,按照统一数据标准,完成不同微服务和渠道业务数据的汇集和存储,解决数据孤岛和初级数据共享的问题。

第二,建立主题数据模型,按照不同主题和场景对数据进行加工处理,建立面向不同主题的数据视图,比如客户统一视图、代理人视图和渠道视图等。

第三,建立业务需求驱动的数据体系,支持业务和商业模式创新。

数据中台不只适用于分析场景,也适用于交易型场景。你可以将其建立在数据仓库和数据平台上,将数据平台化之后提供给前台业务使用,为交易场景提供支持。

当然,数据中台的建设投入高、见效慢、收益高,你需要整体权衡。

BFF与企业级业务编排和协同

企业级业务流程往往是由多个微服务协作完成的,每个单一职责的微服务就像积木块,它们只完成自己特定的功能。但是企业级的业务功能往往是由多个中台微服务的功能组成的,那如何组织这些微服务,完成企业级业务编排和协同呢?

你可以在微服务和前端应用之间,增加一层BFF(Backend for Frontends)微服务。BFF的主要职责是处理微服务之间的服务组合和编排。

前面提到了,微服务内的应用服务也可以处理服务的组合和编排。那么你可能会问,这两者之间有什么差异呢?

BFF是位于中台微服务之上,它的主要职责是负责微服务之间的服务协调和编排。而应用服务主要处理微服务内的服务组合和编排,它可以组合和编排领域服务。在小型项目里,应用服务也可以编排其他微服务的应用服务,我们就没必要增加一层BFF的逻辑了。

在设计时我们应尽可能地将可复用的服务能力往下层沉淀,在实现能力复用的同时,还可以避免跨中心的服务调用,带来不必要的开销。

BFF像齿轮一样,适配着前端应用与微服务之间的步调。通过BFF微服务中的facade接口服务,向上适配不同的前端应用。通过协调不同微服务,向下实现企业级业务能力的组合、编排和协同。

BFF微服务可根据需求和流程变化,与前端应用版本协同发布,避免中台微服务为适配不同前端需求的变化,而频繁地修改和发布版本,从而保证中台微服务版本和核心领域逻辑的稳定。

如果你的BFF做得足够强大,它可以成为一个集成了不同中台微服务能力、面向多渠道应用的业务能力聚合平台。

## 分布式事务还是事件驱动机制

分布式架构下,原来单体的内部调用会变成跨微服务的分布式调用。如果一笔交易同时新增和修改了多个微服务的数据,就容易产生数据一致性的问题。数据一致性有强一致性和最终一致性两种实现方案,它们的实现方式不同,代价也不同。

对实时性要求高的强一致性业务场景,你可以采用分布式事务,但分布式事务有性能代价,在设计时我们需平衡考虑业务拆分、数据一致性、性能和实现的复杂度,尽量避免分布式事务的产生。

领域事件驱动的异步方式是分布式架构常用的设计方法,它可以解决非实时性场景下的数据最终一致性问题。基于消息中间件的领域事件发布和订阅,可以很好地解耦微服务。通过削峰填谷,可以减轻数据库实时访问压力,提高业务吞吐量和业务处理能力。

你还可以通过领域事件驱动机制实现读写分离,提高数据库访问性能。对数据实时性要求不高的最终一致性的场景,一般优先采用异步化的领域事件驱动设计方式。

多中心多活设计

分布式架构的高可用主要通过多中心多活设计来实现。多中心多活是一个非常复杂的话题,涉及的技术很多,我主要列出以下几个关键的设计点。

1)选择合适的分布式数据库。数据库应该支持多数据中心部署,满足数据多副本以及数据底层复制和同步的技术要求,满足数据复制和恢复的时效性要求。

2)单元化架构设计。将若干个应用组成的业务单元集合作为部署的基本单位,实现同城和异地多活部署,以及跨中心的弹性扩容。各单元集合内业务功能自包含,所有业务流程都可在本单元集合内完成。任意单元的数据在多个数据中心有副本,不会因故障而造成数据丢失。任何单元故障不影响其他同类单元的正常运行。单元化设计时,我们要尽量避免跨数据中心和跨单元的调用。单元化架构可以很好地支持全链路压测和灰度发布,支持数据中心和业务的快速扩容。

3)访问路由。访问路由包括接入层、应用层和数据层的路由,需要确保前端请求能够按照正确的路由到达数据中心和业务单元,准确地写入或获取业务数据所在数据库。

4)全局配置数据管理。统一管理各数据中心全局配置数据,所有数据中心全局配置数据可实现实时同步。保证所有数据中心配置数据的一致性,在出现灾难时,可以一键切换。

小结

企业级分布式架构的实施是一个非常复杂的系统工程,涉及非常多的技术体系和方法。我选取了10个关键的分布式设计关键领域,每个领域其实都非常复杂,需要大量的投入和研究。具体实施和落地时,企业要结合自身情况来选择合适的技术组件和实施方案。

Released under the MIT License.