微服务 数据库(微服务开发中的数据架构应该怎样设计)
很多朋友对于微服务 数据库和微服务开发中的数据架构应该怎样设计不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!
微服务开发中的数据架构应该怎样设计
前言
微服务是当前非常流行的技术框架,通过服务的小型化、原子化以及分布式架构的弹性伸缩和高可用性,可以实现业务之间的松耦合、业务的灵活调整组合以及系统的高可用性。为业务创新和业务持续提供了一个良好的基础平台。本文分享在这种技术架构下的数据架构的设计思想以及设计要点,本文包括下面若干内容。
微服务技术框架中的多层数据架构设计
数据架构设计中的要点
要点1:数据易用性
要点2:主、副数据及数据解耦
要点3:分库分表
要点4:多源数据适配
要点5:多源数据缓存
要点6:数据集市
为了容易理解,本文用一个简化的销售模型来阐述,如下图。图1显示了客户、卖家、商品、定价、订单的关系(这里省略支付、物流等其他元素)。
图1销售模型
在这个销售模型中,卖家提供商品、制定价格,客户选择产品购买、形成销售订单。根据微服务的理念设计,可以划分为客户服务、卖家服务、商品服务、定价服务、订单服务,以及公共服务(比如认证、权限、通知等),如图2所示。
图2微服务功能
微服务架构中的多层数据架构设计
分布式架构一般把系统分为 Saas(Software-as-a-Service)、Paas(Platform-as-a-Service)、Iaas(Infrastructure as a Service)三层。其中 Saas层负责对外部提供业务服务,Paas层提供基础应用平台,Iaas层提供基础设施。微服务垂直嵌入这三层服务之中,相互独立。因此数据架构设计时需要考虑三层服务对数据的关注点,又要考虑微服务的独立性。
数据架构的分层设计
图3微服务技术框架
如图3所示,Iaas层提供程序运行的物理基础环境(这边涉及很多硬件·网络内容,在本文中省略)。Pass层细分为三层,基础服务层,主要负责数据存储处理;事务框架层,主要负责微服务的注册·调度管理、分布式事务处理;应用服务层、主要实现各个微服务的 API,供其它微服务直接调用以及 Saas层的服务调用。Saas服务就是公开对外提供的业务服务。
数据架构自下向上相应的分为 Raw Data层、Logic Data(inner)层和 Logic Data(outer)层(Iaas中主要以基础硬件环境为主,在本文中省略)。Raw Data层是基于数据库、文件或者其他形式数据内容。Logic Data(inner)层是微服务 API使用的逻辑数据,比如客户数据、订单数据等等。Logic Data(outer)层是对外服务提供数据,比如客户订单数据。因此,我们的数据架构的分层结果如图4所示。
图4数据分层架构
除此之外,很多情报会以画面或报表的形式展现出来。因此在 Logic Data(outer)之上,可以构建 Information Block(常用的信息块)、通过 View type(显示模式)的设定后,最终 View展现出来。
如图4所示,越靠近对外服务层,客户对设计者的影响度越大,越需要从使用性、易用性、适用性等考虑。反之,越远离对外服务层,设计上更关心数据的存储。
数据三层架构的好处是实现数据从系统实现到业务实现的逐层过渡,实现业务数据和系统数据间的松耦合。同时实现业务的灵活扩展和系统的灵活扩展。
数据架构设计中的要点
上面讲述了数据架构的分层设计,下面讲述数据架构设计中的要点。
要点1:数据易用性
数据无论用什么方式实现,其最终目的都是为业务(或者是客户)使用的。因此,在对外提供服务的时候,数据的易用性非常关键。
图5数据易用性
如图5所示,客户信息在 Logic Data(inner)层中为了数据的柔软性和非冗余,把人员信息拆成若干子表来存储。比如,人员地址表可以无限多的存储客户地址信息。这样的好处在于每次人员地址更新时,不用直接更新人员地址,而是生成一个新的地址数据,原有的地址信息作为历史数据得到保存,易于数据快速恢复和历史信息追踪。但在 Logic Data(outer)层提供外部数据的时候,首先考虑的是一次性能提供足够用的信息(毕竟查询的操作大大高于修改的操作),减少业务场景中不需要的信息。比如对一般客户只提供三个常用地址的时候,数据设计中地址1、地址2和地址3放在一张表中。
要点2:主、副数据及数据解耦
每个微服务 API的数据完全独立是不太现实的,比如订单中需要有商品、客户(包括收货者)、卖家以及价格等数据。如果这些数据都在订单服务 API中管理,那么客户情报的变更、价格调整等信息都要同步给订单 API中数据,数据的耦合度就会变得非常高。在数据设计的时候,需要考虑降低数据间的相互依赖性。因此,首先需要确定每个微服务 API的主数据和副数据。主数据指微服务 API的核心数据,这种数据的增删改主要集中在某个微服务 API中,比如订单服务 API中的订单数据。副数据指参照或者映射其他微服务 API的数据,比如订单服务 API中的商品数据、价格数据等。其次,为了降低数据之间的耦合度,用数据关联表来表征数据间的关系。如果想去掉数据间的关联关系,直接去掉关联表即可,对数据本身的没有任何影响。具体如图6所示。
图6主、副数据及数据解耦
要点3:分库分表
随着业务数据量不断增加,单一数据库或单一数据表中会积累大量的数据,比如订单数据,随着时间推移和客户数量的增加,产生的订单数据也会越来越多。当数据累积到一定程度后,数据操作的性能会大幅下降,也就是我们常说的数据库“带不动了”。所以,在数据架构设计阶段就应该考虑数据的分库分表。
如图7所示,分库,即我们把订单数据分为当前数据应用库、历史数据库、历史归档数据库。当前数据应用库用来支持新订单的生成以及执行中订单的增删改查。历史数据库(这里举例分为最近3个月和最近1年)当客户想看过往订单的时候才使用。历史归档数据(按年间归档)原则上不直接对客户公开,用于备查、统计分析。对于当前数据应用库,可以继续再分库,按客户号范围来分库。这样每个数据库的大小都能得到有效控制。分表,即把一条信息分别存储在两张或多张表中。比如把订单信息按基本信息和详细信息分表,就可以适用于订单的基本信息查询和订单详细信息查询。总之,分库分表的核心就是控制单一数据库的负荷(数据量和数据信息量),通过多表多库来应对业务数据量的增长。
图7分表分库
要点4:多源数据适配
传统的关系型数据库之外,有多种多样的数据源,比如图像、声音、视频等多媒体数据文件或数据流,CSV、TXT、Doc、Excle、PDF、XML等各种异构数。这些数据都需要做相应的处理,转换成可管理的数据信息。因此在数据架构设计的时候,需要给不同性质的数据源配置相对应的读写适配器,同时也需要有统一调度的地方,如图8所示。
图8多源数据适配
要点5:多源数据缓存
数据处理的性能除了处理逻辑的复杂度以外,还有很大一部分是目标数据的操作时长(含对硬件磁盘设备的读写以及网络的传输)。网络速度特别是光纤的使用后已经大幅度提高,但机器磁盘的读写效率并没有显著提高,因此减少磁盘读写是提高效率的一个重要途径。数据缓存就是把常用的数据(不会经常更改的数据)、最近使用数据放到内存中。这样就可以大幅降低系统对硬件磁盘设备的操作开销,提高整个数据系统的性能,如图9所示。
图9数据缓存
要点6:数据集市
数据集市是一个很大的话题。当现有的数据不能简单地通过几个表数据关联以及简单加工后就可以供业务使用的时候,就需要考虑构建数据集市。数据集市以数据运用的观点来分析加工数据,通过多源数据的导入、清洗、加工、视图做成等一系列的数据操作后,为业务提供可用的、稳定的数据源。例如,对销售分析中、什么样的客户喜欢什么样的商品、价格对销售金额的影响、销售金额跟地区日期的关联关系等多维度分析,就要用数据集市的概念,如图10所示。
图10数据集市
数据承载着信息,好的数据架构设计会使业务系统变得更加流畅、更加容易理解和维护。本文只是总结一些在实际工程中的体会,供大家分享。如果有不足之处、也请大家补充、赐教。
(此文转载至GitChat技术杂谈)
「微服务架构」跨多个微服务的数据架构模式
在微服务中,一个逻辑上原子操作可以经常跨越多个微服务。即使是单片系统也可能使用多个数据库或消息传递解决方案。使用多个独立的数据存储解决方案,如果其中一个分布式流程参与者出现故障,我们就会面临数据不一致的风险-例如在未下订单的情况下向客户收费或未通知客户订单成功。在本文中,我想分享一些我为使微服务之间的数据最终保持一致而学到的技术。
为什么实现这一目标如此具有挑战性?只要我们有多个存储数据的地方(不在单个数据库中),就不能自动解决一致性问题,工程师在设计系统时需要注意一致性。目前,在我看来,业界还没有一个广为人知的解决方案,可以在多个不同的数据源中自动更新数据-我们可能不应该等待很快就能获得一个。
以自动且无障碍的方式解决该问题的一种尝试是实现两阶段提交(2PC)模式的XA协议。但在现代高规模应用中(特别是在云环境中),2PC似乎表现不佳。为了消除2PC的缺点,我们必须交易ACID for BASE并根据要求以不同方式覆盖一致性问题。
在多个微服务中处理一致性问题的最着名的方法是Saga模式。您可以将Sagas视为多个事务的应用程序级分布式协调。根据用例和要求,您可以优化自己的Saga实施。相反,XA协议试图涵盖所有场景。 Saga模式也不是新的。它在过去已知并用于ESB和SOA体系结构中。最后,它成功地转变为微服务世界。跨越多个服务的每个原子业务操作可能包含技术级别的多个事务。 Saga Pattern的关键思想是能够回滚其中一个单独的交易。众所周知,开箱即用的已经提交的单个事务无法进行回滚。但这是通过引入补偿操作来实现的-通过引入“取消”操作。
除了取消之外,您还应该考虑使您的服务具有幂等性,以便在出现故障时重试或重新启动某些操作。应监控故障,并应积极主动地应对故障。
如果在进程的中间负责调用补偿操作的系统崩溃或重新启动,该怎么办?在这种情况下,用户可能会收到错误消息,并且应该触发补偿逻辑,或者-当处理异步用户请求时,应该恢复执行逻辑。
要查找崩溃的事务并恢复操作或应用补偿,我们需要协调来自多个服务的数据。对账
是在金融领域工作的工程师所熟悉的技术。你有没有想过银行如何确保你的资金转移不会丢失,或者两个不同的银行之间如何汇款?快速回答是对账。
回到微服务,使用相同的原则,我们可以在一些动作触发器上协调来自多个服务的数据。当检测到故障时,可以按计划或由监控系统触发操作。最简单的方法是运行逐记录比较。可以通过比较聚合值来优化该过程。在这种情况下,其中一个系统将成为每条记录的真实来源。
想象一下多步骤交易。如何在对帐期间确定哪些事务可能已失败以及哪些步骤失败?一种解决方案是检查每个事务的状态。在某些情况下,此功能不可用(想象一下发送电子邮件或生成其他类型消息的无状态邮件服务)。在其他一些情况下,您可能希望立即了解事务状态,尤其是在具有许多步骤的复杂方案中。例如,预订航班,酒店和转机的多步订单。
在这些情况下,事件日志可以提供帮助。记录是一种简单但功能强大的技术。许多分布式系统依赖于日志。“预写日志记录”是数据库在内部实现事务行为或维护副本之间一致性的方式。相同的技术可以应用于微服务设计。在进行实际数据更改之前,服务会写入有关其进行更改的意图的日志条目。实际上,事件日志可以是协调服务所拥有的数据库中的表或集合。
事件日志不仅可用于恢复事务处理,还可用于为系统用户,客户或支持团队提供可见性。但是,在简单方案中,服务日志可能是冗余的,状态端点或状态字段就足够了。
到目前为止,您可能认为sagas只是编配(orchestration)方案的一部分。但是sagas也可以用于编排(choreography),每个微服务只知道过程的一部分。 Sagas包括处理分布式事务的正流和负流的知识。在编排(choreography)中,每个分布式事务参与者都具有这种知识。
到目前为止描述的一致性解决方案并不容易。他们确实很复杂。但有一种更简单的方法:一次修改一个数据源。我们可以将这两个步骤分开,而不是改变服务的状态并在一个过程中发出事件。
在主要业务操作中,我们修改自己的服务状态,而单独的进程可靠地捕获更改并生成事件。这种技术称为变更数据捕获(CDC)。实现此方法的一些技术是Kafka Connect或Debezium。
但是,有时候不需要特定的框架。一些数据库提供了一种友好的方式来拖尾其操作日志,例如MongoDB Oplog。如果数据库中没有此类功能,则可以通过时间戳轮询更改,或使用上次处理的不可变记录ID查询更改。避免不一致的关键是使数据更改通知成为一个单独的过程。在这种情况下,数据库记录是单一的事实来源。只有在首先发生变化时才会捕获更改。
更改数据捕获的最大缺点是业务逻辑的分离。更改捕获过程很可能与更改逻辑本身分开存在于您的代码库中-这很不方便。最知名的变更数据捕获应用程序是与域无关的变更复制,例如与数据仓库共享数据。对于域事件,最好采用不同的机制,例如明确发送事件。
让我们来看看颠倒的单一事实来源。如果不是先写入数据库,而是先触发一个事件,然后与自己和其他服务共享。在这种情况下,事件成为事实的唯一来源。这将是一种事件源的形式,其中我们自己的服务状态有效地成为读取模型,并且每个事件都是写入模型。
“事件优先”方法面临的挑战也是CQRS本身的挑战。想象一下,在下订单之前,我们想要检查商品的可用性。如果两个实例同时收到同一项目的订单怎么办?两者都将同时检查读取模型中的库存并发出订单事件。如果没有某种覆盖方案,我们可能会遇到麻烦。
处理这些情况的常用方法是乐观并发:将读取模型版本放入事件中,如果读取模型已在消费者端更新,则在消费者端忽略它。另一种解决方案是使用悲观并发控制,例如在检查项目可用性时为项目创建锁定。
“事件优先”方法的另一个挑战是任何事件驱动架构的挑战-事件的顺序。多个并发消费者以错误的顺序处理事件可能会给我们带来另一种一致性问题,例如处理尚未创建的客户的订单。
诸如Kafka或AWS Kinesis之类的数据流解决方案可以保证将按顺序处理与单个实体相关的事件(例如,仅在创建用户之后为客户创建订单)。例如,在Kafka中,您可以按用户ID对主题进行分区,以便与单个用户相关的所有事件将由分配给该分区的单个使用者处理,从而允许按顺序处理它们。相反,在Message Brokers中,消息队列具有一个订单,但是多个并发消费者在给定顺序中进行消息处理(如果不是不可能的话)。在这种情况下,您可能会遇到并发问题。
实际上,在需要线性化的情况下或在具有许多数据约束的情况(例如唯一性检查)中,难以实现“事件优先”方法。但它在其他情况下确实很有用。但是,由于其异步性质,仍然需要解决并发和竞争条件的挑战。
有许多方法可以将系统拆分为多个服务。我们努力将单独的微服务与单独的域匹配。但域名有多细化?有时很难将域与子域或聚合根区分开来。没有简单的规则来定义您的微服务拆分。
虽然匹配帐户余额至关重要,但有许多用例,其中一致性不那么重要。想象一下,为分析或统计目的收集数据。即使我们从系统中随机丢失了10%的数据,也很可能不会影响分析的业务价值。
数据的原子更新需要两个不同系统之间达成共识,如果单个值为0或1则达成协议。当涉及到微服务时,它归结为两个参与者之间的一致性问题,并且所有实际解决方案都遵循一条经验法则:
在给定时刻,对于每个数据记录,您需要找到系统信任的数据源
事实的来源可能是事件,数据库或其中一项服务。实现微服务系统的一致性是开发人员的责任。我的方法如下:
为什么在微服务架构下,服务网关和数据库不能部署在虚拟机上
最近开发了一基于springcloud的微服务架构的门户项目,因为客户对系统性能有要求,所以楼主对系统的一些api接口进行了大量压力测试。在压测过程中,发现接口的性能瓶颈之一是服务网关和数据库部署在虚机上,所以本文将分享内容分为两部分
性能压测思路是从软硬件负载 f5,nginx,到容器化平台k8s、docker、zuul网关,再到数据存储es、mysql、mongodb、redis,进行全面测试。
性能压测汇总
部分接口压测结果
其中值得关注的是,用一台zuul网关节点和一个业务节点压测空接口,发现一个有意思现象:
空接口压测不走zuul,一个业务节点tps能达到 32000,走zuul网关,一个业务节点空接口tps只有11000,性能损耗64%。
当时就感觉zuul网关在我心中高大的形象碎了一地,但是没办法,性能不达标必须要优化。所以楼主查了很多资料,也问过一些docker和k8s的容器化平台大牛,总结出两点经验:
所以楼主向公司申请物理机,继续性能压测,当然这不是重点,重点是接下来要讲的:为什么服务网关和数据库不能部署到虚拟机上。
虚拟机的特点
io开销
我们知道,不管虚机上部署了多少个应用,一旦涉及到数据的存储,如果采用虚机部署数据库,会带来不必要的网络io开销。因为虚拟机在调度大量物理的cpu和内存、特别是磁盘IO时,必须经过虚拟机和物理机两层网络io读写开销操作,是非常耗系统性能的。
一般情况下,使用虚拟机部署应用,其性能衰减约20%左右,这不是优化代码能解决的。
共享物理机资源
因为虚拟机在cpu资源、网络等方面共享物理机资源,虚拟机之间会存在竞争物理机资源,造成程序不稳定情况。
docker容器部署
更要命的是,如果数据库和zuul网关部署到容器(实质也是虚拟机)里,那么网络io读写变成docker(虚拟机)到虚机,再到物理机三层访问,无形之中又增加了io读写性能开销。尤其是对于请求吞吐量要求很高的服务网关zuul,是不能容忍的。
所以虚机对于IO密集型以及对延迟要求很高的业务场景不合适。
另外,早期的时候,作为一名架构师需要尽早的规划好服务网关和数据库的物理部署方式以及软硬件性能要求。
好了,文章到此结束,希望可以帮助到大家。