文章目录

  • 单库join
  • 分布式微服务聚合join
  • CQRS
    • Denormalize + Materialize the View
    • CQRS模式
      • CQRS和最终一致性
      • CQRS和UI(前端)更新策略
  • 架构2005 VS 2016

单库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相关推荐

  1. 如何解决微服务的数据聚合Join问题?

    单库Join问题 有后端开发经验的同学应该了解,对于传统SQL数据库,我们通常以正规化(normalization)的方式来建模数据.正规化的好处是数据冗余少,不足之处是数据聚合Join会比较麻烦.实 ...

  2. 微服务中数据聚合的三种方式

    在微服务暴热的情形下,似乎不弄点微服务,已经是跟不上IT的大潮了. 因此,公司结合本身情况,以及将来的可拓展性,在我的主导下,在新的项目中采用了微服务架构 然而,实施过程中遇到一个挠头的问题,就是数据 ...

  3. 如何做服务编排/数据聚合?(使用Goku API Gateway实现)

    什么是服务编排/数据聚合? 服务编排/数据聚合 指的是可以通过一个请求来依次调用多个微服务,并对每个服务的返回结果做数据处理,最终整合成一个大的结果返回给前端. 例如一个服务是"查询用户预定 ...

  4. 【推荐】2020,2021网易数字+大会(云原生微服务+大数据数据库+网易AI实践集合+其他) - (共187份)

    [推荐]2020,2021网易数字+大会(云原生&微服务+大数据&数据库+网易AI实践集合+其他) - (共187份) 下载地址:https://download.csdn.net/d ...

  5. 微服务下数据表之间的耦合关系

    微服务下数据表之间的耦合关系 1.问题产生 今天同事给我发了一个链接,问我这种做可视化建表的过程觉得怎么样?(如下图) 同事的意思主要是,在微服务架构下,新建单一数据表,然后表内数据需要引用其他数据表 ...

  6. 微服务之数据同步Porter

    Porter是一款数据同步中间件,主要用于解决同构/异构数据库之间的表级别数据同步问题. 背景 在微服务架构模式下深刻的影响了应用和数据库之间的关系,不像传统多个服务共享一个数据库,微服务架构下每个服 ...

  7. 微服务之数据同步Porter 1

    Porter是一款数据同步中间件,主要用于解决同构/异构数据库之间的表级别数据同步问题. 背景 在微服务架构模式下深刻的影响了应用和数据库之间的关系,不像传统多个服务共享一个数据库,微服务架构下每个服 ...

  8. .Net Core with 微服务 - Seq 日志聚合

    上一次我们介绍并演示了如果使用 Consul 做为我们微服务的注册中心,来实现服务的注册与发现.那么本次我们讲会演示如何做日志聚合.日志聚合比较常用的有 ELK 等,但是这次我想要介绍的是一款比较小众 ...

  9. Fegin拦截器解决各微服务之间数据下沉

    上篇说了当前端访问微服务网关,借助ZuulFilter过滤器来过滤所有请求,获取request,判断cookie是否有身份短令牌,request的header中是否有Jwt令牌,redis中是否有Jw ...

  10. 【go-zero】go-zero开发环境 如何聚合所有api? caddy反向代理服务分发 微服务设计api聚合方法 best practice

    帮助go-zero开发者聚合api 相关视频 一.go-zero 微服务整体架构 1.微服务的基本架构 2.go-zero 微服务的 api authrpc.api 文件 routes.go 文件 二 ...

最新文章

  1. 阿里软件测试工程师手把手教学——自动化测试报告太丑,怎么办?
  2. malloc 和 calloc的区别?
  3. 论文解读 | 利用脑功能连接实现疲劳驾驶检测
  4. 奢侈品网购真正大牌占比未过半:被指只剩噱头
  5. 转载--#define 用法
  6. txt文件保存为Java_java实现写入并保存txt文件
  7. mmlspark-102 : 简单的ML Pipelines
  8. HTML5项目实战之旅行社网站——PC端固定布局
  9. vue 函数(二):callback回调函数
  10. 终于进了阿里,记录一下我作为一名测试员磕磕碰碰的三个月找工作经历...
  11. 计算机声卡原理,什么是电脑声卡 电脑声卡的工作原理
  12. HTML与CSS--------p标签
  13. Office2019 VOL版本 自定义安装组件
  14. 基于WaterDetect的水域面积提取,以长江中上游水面为例
  15. 个人计算机是国产芯片,全球最纯国产PC诞生!所有芯片/系统都是国产
  16. base64加密--excel--pdf--img 上传
  17. Codeforces 545 C Woodcutters(贪心/DP)
  18. JS图片360度全景预览插件
  19. Ansible-playbook进行检测时unarchive模块跳过的问题
  20. 如何帮助中国8000万中小企业上云?阿里云交出了这样的答卷

热门文章

  1. 2021年12月国产数据库大事记-墨天轮
  2. 时钟系统(NTP子母钟系统)如何为高铁系统保驾护航
  3. 关于大创项目的初期思考2020.11.14
  4. echarts字体大小自适应
  5. DirectX11学习笔记01
  6. ubuntu本地安装中文
  7. 创新工场和海豚浏览器宣讲会启示
  8. 流媒体:浅谈传统媒体—流媒体—加P2P的流媒体的演变之路
  9. 7步打造持续盈利的会员体系
  10. FeedBurner 被阻尼,Feed 托管转到 FeedSky