微服务的数据聚合Join_cn_hhaip的专栏-CSDN博客

标签: | 发表时间:2021-08-20 16:50 | 作者:
出处:https://blog.csdn.net

单库join

传统SQL数据库,通常正规化(normalization)的方式来建模数据。正规化的好处是 数据冗余少,不足之处是数据聚合Join会比较麻烦,可能实际Join的时候,需要将几张相关表,通过主键和外键关系才能Join起来。我们知道,Join是一种开销比较大的SQL运算,当数据量少的时候,这种开销通常OK。但是随着企业规模逐渐变大,数据库中的数据量也会越变越大,相应地,Join的开销也会越来越大。于是,Join变慢的问题就会越来越突出,通常表现为用户的查询慢,严重时,复杂的Join可能会导致数据库繁忙不响应甚至宕机。之前我在上家公司工作的时候,就曾经经历过几次复杂Join造成DB宕机的事故。可以说,单库Join性能慢的问题,是目前很多网站的普遍痛点问题。所以,去数据库Join,是很多企业当前正在做的数据库优化工作之一。

优点:

  • 数据冗余少
  • 无需跨微服务查询,实现方便,不用考虑分布式问题

缺点:

  • join多张表的时候一样比较麻烦(SQL运算大)
  • join非常影响性能,随着数据量越来越大,传将越慢,对CPU和内存的占用越来越大,甚至影响其他业务正常进行或DB宕机
  • join往往是数据库瓶颈之一

分布式微服务聚合join

分布式聚合join服务层的另两种名字 1、Aggregator聚合服务 2、BFF服务,BFF是Backend for Frontend的简称

在分布式的微服务时代,数据聚合Join的问题并没有消失,它变成了另外一种形式( 微服务之间的聚合join)。
例子:

上图,有两个基础领域服务customer-service和order-service。根据微服务有界上下文和职责单一的原则,customer-service只负责客户数据,order-service只负责订单数据。但是前端业务需要一个order-history-API,这个API支持查询用户的历史订单,它既要提供用户详细信息,也要提供用户的历史订单信息。为此,我们需要引入这样一个order-history-API服务,它同时去调用order-service和customer-service,获得数据后再在本地进行聚合Join,然后再对外提供聚合好的客户+订单历史数据。
总体上,上图的order-history-API做的事情,就是所谓的分布式聚合Join。这个API服务还有两个专门的称谓,一个叫 Aggregator聚合服务,另外一个叫 BFF服务(BFF是Backend for Frontend的简称),它的 主要工作也就是聚合Join(外观模式)

大规模互联网系统中,分布式聚合Join非常常见。基本上你上任何一个大厂的网站,比方说天猫,京东,或者美团,携程等,它们的网站页面上的数据,大部分都是通过后台的分布式聚合服务聚合出来的。所以, 聚合服务层(或者称BFF层),是现代互联网和微服务架构中普遍存在的一个架构层次。

分布式聚合的优点:
1、具有实时性和强一致性的好处
2、聚合服务层使相同业务更加内聚,不同业务更低耦合

分布式聚合的缺点:

1、一个是 N+1问题。有的时候,为了获得A和B服务的聚合数据,可能A只需要调用一次,但是B却需要调用N次才能获取完整数据。这个就是软件开发领域臭名昭著的N + 1问题,它通常是性能杀手。

2、第二个问题是数据量的问题。聚合服务需要把A和B的部分数据都加载到本地内存,然后才能进行聚合运算。当访问量大的时候,聚合服务会占用大量内存开销,严重时可能会造成内存被撑爆。

3、第三个问题就是随着后台基础领域服务的数据量越来越大,总体聚合服务的性能也会随着越变越慢。需要特别说明的是,如果不做缓存的话,这种分布式聚合,对于每个请求都是会重复执行和运算的,也就是会有大量的频繁和重复的聚合运算,会白白消耗大量CPU/内存等资源。

CQRS

Denormalize + Materialize the View

反正规化的物化视图

企业实践表明当互联网公司的体量规模发展到一定的阶段,为了解决分布式聚合Join慢的问题(或者是为了解决传统SQL数据库Join慢的问题),它们通常会采用另外一种称为 数据分发 + 预聚合的新方式。
(坤同以前通过定时任务将领料汇总提前生成数据)

例子:
上图,我们这里也有两个基础领域服务,一个是商品服务 item-service,另外一个是订单反馈服务 order-feedback-service
前端业务需要一个商品反馈服务item-feedback-service,它的数据是由item-service和order-feedback-service聚合的结果。为了实现这个order-feedbak-service,我们可以用前面的聚合(或者说BFF服务)来实现,但是那种做法可能每次查询的开销较大,性能无法满足要求。为了解决性能问题,我们可以改用之前讲解的数据分发技术,比方说 事务性发件箱技术,或者 CDC变更数据捕获技术,也就是基于 数据分发+预聚合(提前组装数据)的思路来实现这个服务。当item-service或者order-feedback-service有数据变更的时候,我们把它们的变更,通过数据分发技术,分发到item-feedback-service这个聚合+查询服务。item-feedback-service可以根据本地已有的数据,加上发送过来的变更数据,实时/或者近实时的聚合计算出商品反馈数据,并存入本地数据库缓存起来,这个就是数据分发+预聚合的思路。

坤同例子:
KA-VMC接收kafka的机器消息,然后通过http调用(可改成缓存)机器和商品信息提前组装好数据,最后保存到本服务业务库。然后通过数据分发技术,将数据分发不到不同的业务服务。

**注意:**这个方式和前面的聚合层BFF方式是有本质区别的。前面的方式是每次请求都要触发重复计算的,而这里的方式是一次性预先聚合好,并且缓存起来,后面的查询都是查询的缓存数据,所以这是一个提前预聚合的思路, 有一定的延迟

这个方式其实就是 反正规化(denormalize)的方式,它把原来正规化的需要聚合Join的数据,通过反正规化方式预先聚合并缓存,这样可以大大加快后续的查询。另外,学过数据库的同学应该知道,数据库当中有 物化视图(Materialized View)这样一个概念,它本质上也是一种预聚合的思路。物化视图把底层的若干张表,以反正规化的方式,实时地聚合起来,提供方便查询的视图View。并且,当底层数据表发生变更的时候,物化视图也可以实时同步这些变更(相当于实时聚合Join)。现在你应该明白,我们这里所讲的数据分发+预聚合方式,其实它的思想和物化视图是相同的,只不过我们这里讲的是 分布式的物化视图

注意实时预聚合能够大大提升查询的性能,但是技术门槛也比较高。当数据变更发生的时候,或者说当变更数据流过来的时候,你就需要对数据流进行实时运算。这个计算越实时,查询的实时性就越好,当然,所需要的技术门槛也越高。之前我们提到过的Kafka Stream,它就是支持实时流式聚合的一个开源产品。

CQRS模式

基于数据分发与聚合还有一个名称是CQRS(Command/Query Responsibility Segregation) ,它是将数据的写入和查询职责分离的一种模式

上面讲的数据分发+预聚合的方式,在互联网领域还有一个更时髦的名称,叫 CQRS,英文全称是Command/Query Responsibility Segregation,翻译成中文是 命令/查询职责分离模式

这个模式的总体形态,如上图所示。CQRS的左边是Command命令端,这一端通常只负责写入。CQRS的右边是Query查询端,这一端通常只负责读取。底层一般是数据分发技术,比如事务性发件箱、CDC还有MQ,它们将命令端的变更数据,实时或者近实时地同步到查询端。

写入端的数据存储,通常采用传统SQL数据库(MySQL Oracle DB2)。而 查询端则可以根据需要选择最适合的存储机制,比如说如果通过KV键查询的话,可以采用Redis或者Cassandra;通过关键字查询的话,可以采用ElasticSearch。当然,还可以引入离线批处理Hadoop,甚至是实时计算平台Spark/Flink/KafkaStream等。不管查询端采用何种存储技术,它们的目标都是提升查询的规模化和性能。

总体上,从命令端到查询端,数据的流动变化过程,就是一个反正规化,适合各种快速查询需求的过程。在三层应用时代,为了提高查询性能,我们通常采用 数据库的读写分离技术。到了微服务时代,这个技术的思路仍然适用,只不过它向上提升到服务层,演变成CQRS模式了。所以也可以说, CQRS是服务层的读写分离技术(读服务和写服务)。

**注意:**合理应用CQRS技术,可以大大提升查询的性能,同时提升企业数据规模化的能力。但是对于CQRS/CDC这类技术,它们的技术门槛高,一般小公司可能玩不起,只有到一定体量的公司才会考虑,例:Netflix的落地案例。

CQRS和最终一致性

采用CQRS模式以后,客户从命令端写入数据,然后变更数据分发到查询端,查询端再聚合生成查询视图,这中间难免会有网络和聚合计算延迟,所以这个模式并不保证写入和查询数据的强一致性,而是演变成最终一致性。

最终一致性会带来UI更新的问题。举个例子,如PPT所示,用户通过UI到Order订单服务创建一个新订单,这个订单落到订单服务的数据库中,然后订单服务在返回用户响应的同时,后台再异步发消息到Order Query订单查询服务,然后订单查询服务收到消息,就去做聚合更新订单视图的工作,这个工作可能需要耗费一定的时间。如果新视图在被更新之前,用户又通过UI来查询新订单数据,那么他可能会查不到数据。也就是说,CQRS的最终一致特性,会引入一定的时间差,而且这个时间差还是不确定的。

**总结:**由于聚合的延迟性,CQRS没有强一致,可有最终一致性

**注意:**考虑到网络的不稳定和不可靠,数据分发组件可能会因为网络等因素而重发数据( At least Once至少一次),所以,查询端一般需要对数据进行去重或者做 幂等处理(如:kafka消息就可能重复,幂等不可避免)。

CQRS和UI(前端)更新策略

为了解决最终一致性带来的时间差问题,业界通常有三种实践的UI更新策略,请看上图:

第一种策略是 乐观更新。UI在发出请求后,马上更新UI,页面反应已经更新的数据状态。

例:比方说你点赞了某社交网站上的视频或图片,页面马上会显示一颗红心。然后页面后台再通过ajax等方式查询更新结果,如果确认更新成功,那就不需要做什么;如果确认更新失败,只需将页面状态回滚即可。这种方式仅适用于一些简单的场景。

第二种策略是采用 拉模式。UI向命令端发出请求时,请求中带上版本号,然后通过ajax等方式不断轮询查询端,并检查更新后的视图的版本号是否和请求的版本号一致,直到版本号匹配为止,也就是等到视图明确更新成功或失败为止。

第三种策略是采用 发布订阅模式。UI向命令端发出请求,同时通过websocket等方式订阅在查询端,查询端更新好视图,通过发消息通知UI更新页面展示。

架构2005 VS 2016

图是从2005到2016,互联网网站架构发生的变化

重点提一下2016年的网站架构的几个显著特点:

第一个当然是中间引入了微服务。微服务可以用不同语言栈开发,而且可以拥有独立的数据存储。

第二个是 微服务前端引入了BFF聚合服务层,实现实时和强一致性的聚合Join

第三个特点是后台引入了 CQRS/CDC/大数据/AI等技术。这些技术引入的主要目标,说白了,无非就是对数据库中的数据(包括变更数据),进行聚合或者再加工计算,生成能够进一步产生业务价值的读视图,再通过微服务或者BFF服务等方式暴露给用户。如果把下半部分逆时针旋转90度,就是一个典型的CQRS模式图。

从总体架构上看,2016年的网站架构和2005年相比,最大的区别是2016年的网站架构是一个更大规模的读写分离架构。

本文所讲的内容,包括 微服务架构数据分发技术CDC,还有 BFF聚合服务等等是现代网站架构的一个基础。

参考:https://www.bilibili.com/read/cv7547597

相关 [微服务 数据 聚合] 推荐:

微服务的数据聚合Join_cn_hhaip的专栏-CSDN博客

- -
CQRS和UI(前端)更新策略. 架构2005 VS 2016. 传统SQL数据库,通常正规化(normalization)的方式来建模数据. 数据冗余少,不足之处是数据聚合Join会比较麻烦,可能实际Join的时候,需要将几张相关表,通过主键和外键关系才能Join起来. 我们知道,Join是一种开销比较大的SQL运算,当数据量少的时候,这种开销通常OK.

互联网数据聚合

- - 四火的唠叨
文章系本人原创,转载请保持完整性并注明出自 《四火的唠叨》. 我们经常需要从互联网上获取数据,在很多情况下,你需要的是特定信息,或者说是符合某些条件的信息,比如:. 这条需求隐含着两个有普遍意义的步骤:. 从互联网上聚合符合特定条件的信息;. 当满足阈值条件时,以某种方式通知用户. 事实上有太多做互联网数据聚合的网站了,比如 酷讯机票,聚合了各大航空公司的机票信息:.

微服务下的数据架构

- - IT瘾-dev
微服务是一个软件架构模式,对微服务的讨论大多集中在容器或其他技术是否能很好的实施微服务,而本文将从以下几个角度来和大家分享在微服务架构下进行数据设计需要关注的地方,旨在帮助大家在构建微服务架构时,提供一个从数据方面的视角:. 按照 Martin Fowler 的定义,微服务是一个软件架构模式,通过开发一系列的小型服务的方式来实现一个应用.

分析聚合数据的SDK

- - Solidot
Aveline Swan上周发现聚合数据(juhe.cn)的 SDK会偷偷上传用户通讯录至服务器,虽然聚合数据随后更新了SDK关闭了上传用户通讯录,但Swan指出在产品端更新SDK是个漫长的过程,旧版的SDK仍然在收集用户通讯录,而且合数据服务器上用于接收上传的通讯录的接口并没有被删掉,仍然能正常处理数据.

NoSQL聚合数据模型 - 大CC

- - 博客园_首页
聚合数据模型的特点就是把经常访问的数据放在一起(聚合在一块);. 这样带来的好处很明显,对于某个查询请求,能够在与数据库一次交互中将所有数据都取出来;. 当然,以这种方式存储不可避免的会有重复,重复是为了更少的交互;. 聚合结构对某些交互有利,却阻碍另一些交互;. 比如:以学生学号聚合学生信息(含学生姓名、班级、年龄、等信息,甚至英语学科成绩),通过学号查询时,能够在一次交互中查询出该学生的所有信息,但如果想通过学生姓名来查询,就很困难;.

实时数据聚合怎么破

- -
实时数据分析一直是个热门话题,需要实时数据分析的场景也越来越多,如金融支付中的风控,基础运维中的监控告警,实时大盘之外,AI模型也需要消费更为实时的聚合结果来达到很好的预测效果. 实时数据分析如果讲的更加具体些,基本上会牵涉到数据聚合分析. 数据聚合分析在实时场景下,面临的新问题是什么,要解决的很好,大致有哪些方面的思路和框架可供使用,本文尝试做一下分析和厘清.

微服务架构--服务框架,metrics 和调用链数据

- - 行业应用 - ITeye博客
微服务化以后,为了让业务开发人员专注于业. 务逻辑实现,避免冗余和重复劳动,规范研发. 提升效率,必然要将一些公共关注点推到框架. 服务框架 ( 图 9) 主要封装公共关注点. 服务注册、发现、负载均衡和健康检查,. 假定采用进程内 LB 方案,那么服务自注. 册一般统一做在服务器端框架中,健康检.

微服务下的数据一致性思考

- -
之前讲到了数据库层和缓存层的改造思路,而对于业务层的改造,采用了集中式服务转微服务的架构方案. 既然是微服务,就意味着面临大量的服务间的内部调用及服务依赖,这就意味着,如果一次请求的调用涉及到两个或多个微服务之间的调用,恰好有下游的微服务调用失败,我们就必须要考虑到回滚及服务间保证数据一致性的问题.

微服务开发中的数据架构设计

- - ITeye资讯频道
GitChat 作者:陈伟荣. 原文: 微服务开发中的数据架构设计. 关注微信公众号:「GitChat 技术杂谈」 一本正经的讲技术. 微服务是当前非常流行的技术框架,通过服务的小型化、原子化以及分布式架构的弹性伸缩和高可用性,可以实现业务之间的松耦合、业务的灵活调整组合以及系统的高可用性. 为业务创新和业务持续提供了一个良好的基础平台.

微服务化的数据库设计与读写分离

- - 行业应用 - ITeye博客
数据库永远是应用最关键的一环,同时越到高并发阶段,数据库往往成为瓶颈,如果数据库表和索引不在一开始就进行良好的设计,则后期数据库横向扩展,分库分表都会遇到困难. 对于互联网公司来讲,一般都会使用My SQL数据库. 我们首先来看Mysql数据的总体架构如下:. 这是一张非常经典的Mysql的系统架构图,通过这个图可以看出Mysql各个部分的功能.