在线型业务/大数据场景/AI时代的技术栈

在线型业务的技术栈

  •  首先当然是硬件,在线业务型的场景下,计算资源基本是CPU为主;存储资源则各种类型都有,主要是IO吞吐、时延、安全性等各种考虑;网络资源公网、内网类型,时延、吞吐考虑等。
  • 在硬件之上(软件定义计算/存储/网络资源),基本都会使用类似K8S,或商用VMWare之类,来更方便的使用硬件资源,
    • 这层软件的核心一方面是抽象硬件资源方便上层根据需求来使用硬件资源,以及同时根据需求来匹配找到合适的资源,例如最简单的排他规则等等;
    • 另一方面则是充分的提升硬件资源的使用率,例如虚拟化技术、容器化技术的引入等
  • 再往上,通常就是根据业务系统的架构、建设需求做的各种中间件的选型,这些中间件最重要的是为了降低业务系统的门槛,把一些通用的技术问题解决掉,例如
    • 数据库解决OLTP类型的数据读写
    • Dubbo之类的服务框架解决跨系统间的同步通信
    • RocketMQ之类的消息中间件解决跨系统间的异步通信等。
  • 这些中间件之上,通常就是业务系统本身了,这个就完全要根据业务需求来做相应的架构设计,并且通常都会需要跟随业务的发展来不断的迭代架构(从单体 --> 分布式 --> 异地多活,推荐 郭东白《架构思维:从程序员到CTO》

K8S(Kubernetes)

K8s 主要用于管理和编排容器化的应用程序,这里的编排如何理解?

在Kubernetes(简称K8s)的上下文中,“编排”指的是自动化 容器化应用程序的部署、管理、扩展和联网的过程。具体来说,它包括但不限于以下功能:

  • 自动部署和更新容器化应用
  • 管理容器的生命周期,包括冗余容器的替换和故障转移
  • 控制容器的可用性和伸缩性,根据负载自动增加或减少容器实例的数量
  • 定义网络规则来控制服务之间的通信
  • 存储卷挂载等。

关于你的Java微服务应用“mabe”,以及其资源基线和POD分配情况,这里有一些概念需要澄清和建议:

  1. 资源基线:你提到的应用资源基线为8C-16G-60GB,意味着每个POD被配置为需要8个CPU核心、16GB内存以及可能的60GB存储空间。这种资源配置相对较高,确保了应用有足够的资源运行。

  2. POD数量:你总共分配了10个POD。这意味着你的应用将并行运行10个实例,每个实例都有相同的资源配置。这样的设置非常适合于需要高可用性和/或水平扩展能力的应用场景,例如处理大量并发请求的服务。

  3. 优化建议:

    • 资源优化:确保这些资源限制是基于实际需求设定的,而不是过度分配。可以考虑使用Kubernetes的Horizontal Pod Autoscaler (HPA) 根据实际负载动态调整POD的数量,这样可以在保证性能的同时优化成本。
    • 健康检查:配置合适的liveness和readiness探针,以帮助Kubernetes了解何时重启一个POD或者何时将其加入到服务中,提高应用的稳定性和可靠性。
    • 服务发现与负载均衡:利用Kubernetes的服务机制来实现内部的服务发现和外部的负载均衡,简化微服务架构中的网络配置。

通过这种方式,Kubernetes可以帮助你更有效地管理和扩展你的Java微服务应用,同时确保其高可用性和资源的有效利用。

 

在 Kubernetes 中,POD 并不是容器,而是 Kubernetes 的最小运行和管理单元。POD 和容器的关系类似于“容器组”“容器”的关系。以下是详细解释:


1. POD 的定义(类似于“容器组”,如一个pod=一个应用容器 + 一个边车容器(运维、监控等)

POD 是 Kubernetes 中的最小调度单元,它封装了一个或多个 共享资源 的容器。具体来说:

  • POD 是一个逻辑单元:它代表一组紧密相关的容器,这些容器共享相同的资源(如网络、存储、IP 地址等)。
  • POD 的核心功能:为容器提供运行环境,并协调它们的生命周期、资源共享和通信。

2. 容器的定义

容器(Container)是 Docker 等容器化技术的核心概念:

  • 容器是运行时的实体:它是镜像(Image)的实例,包含应用程序及其依赖。
  • 容器的隔离性:每个容器运行在独立的命名空间中,具有独立的文件系统、进程空间等。

3. POD 与容器的区别

特性POD容器
抽象层级 更高层的抽象(Kubernetes 的核心概念)。 更底层的实现(Docker 等容器技术的实体)。
资源共享 POD 内的容器 共享网络、存储卷(Volume)、IP 等资源(CPU和内存是独立的,通过cgroup隔离 容器本身是独立的,除非被封装在同一个 POD 中,否则不共享资源。
生命周期管理 Kubernetes 直接管理 POD 的生命周期。 Kubernetes 不直接管理容器,而是通过 POD 管理容器。
IP 地址 每个 POD 有唯一的 IP 地址。 容器没有独立的 IP 地址(除非显式配置)。
通信方式 POD 内的容器通过 localhost 通信。 跨 POD 的容器需要通过服务(Service)或 IP 地址通信。

资源类型POD 内是否共享说明
网络(IP、端口) ✅ 共享 所有容器共享同一个 IP 和端口空间,通过 localhost 通信。
存储卷(Volume) ✅ 共享 容器通过挂载相同的 Volume 实现数据共享。
CPU 和内存 ❌ 独立 每个容器独立配置资源请求和限制,Kubernetes 通过 cgroups 进行隔离和限制。

4. 为什么需要 POD?

POD 的设计解决了以下问题:

  1. 资源共享
    • 同一个 POD 中的容器共享网络命名空间(IP 和端口),因此可以通过 localhost 直接通信
    • 共享存储卷(Volume)方便数据传递或持久化
  2. 简化管理:
    • Kubernetes 以 POD 为单位进行调度、扩展、健康检查等操作,无需直接管理单个容器。
  3. 生命周期协调:
    • 通过 Pause 容器(基础设施容器)管理 POD 的状态,确保 POD 内容器的一致性。

5. 你的场景分析

你提到:

  • 每个 POD 的资源基线8C-16G-60GB,并部署了 10 个 POD。
  • 问题:POD 是否就是容器?

答案:不是。你的每个 POD 实际上是一个容器组(可能只包含一个容器),但 Kubernetes 以 POD 为单位进行管理。例如:

  • 如果你的 Java 微服务每个 POD 只运行一个容器(典型场景),那么每个 POD 就是一个容器的封装。
  • 如果你的 POD 内运行多个容器(例如主容器 + 辅助容器),则这些容器共享同一个 POD 的资源。

6. 举个例子

假设你有一个 Java 微服务应用,部署在 Kubernetes 中:

 1 apiVersion: v1
 2 kind: Pod
 3 metadata:
 4   name: mabe-pod
 5 spec:
 6   containers:
 7     - name: java-app
 8       image: your-java-image:latest
 9       resources:
10         requests:
11           memory: "15Gi" # 显式配置了资源请求和限制(7.5C-15G),Kubernetes 会确保该容器的资源需求被满足12           cpu: "7.5"
13         limits:
14           memory: "15Gi"
15           cpu: "7.5"
16     - name: sidecar-container  # 
17       image: sidecar-image:latest  # 未显示配置资源请求和限制,可能导致(资源争用、OOM等风险

边车容器sidecar作用

  • 日志收集:使用Sidecar容器收集和转发应用程序的日志。
  • 监控:通过Sidecar容器进行应用的性能监控和健康检查。
  • 安全性:使用Sidecar容器实现服务之间的安全通信和访问控制

在一个POD中若某个容器未显示配置资源请求和限制可能导致,Kubernetes 允许容器不配置资源请求和限制,但这种行为可能导致以下问题:

  • 资源争用:sidecar-container 可能会占用大量 CPU 或内存,影响 java-app 的性能。
  • 调度风险:如果 java-app 的资源请求(如 8C-16G)占用了节点的大部分资源,而 sidecar-container 未配置资源请求,Kubernetes 调度器可能无法正确评估节点的可用资源,导致调度决策不合理。
  • OOM Killer 风险:如果 sidecar-container 没有内存限制,它可能因内存不足被节点的 OOM Killer 终止

显示配置资源请求和限制:

 1 containers:
 2   - name: java-app
 3     resources:
 4       requests:
 5         memory: "16Gi"
 6         cpu: "8"
 7       limits:
 8         memory: "16Gi"
 9         cpu: "8"
10   - name: sidecar-container
11     image: sidecar-image:latest
12     resources:
13       requests:  # 资源请求(Requests):确保 sidecar-container 的最小资源需求被满足
14         memory: "512Mi" 
15         cpu: "100m"
16       limits:   # 资源限制(Limits):防止 sidecar-container 占用过多资源,影响 java-app
17         memory: "1Gi"
18         cpu: "500m"
  • POD:mttt-pod 是 Kubernetes 的最小管理单元。
  • 容器:java-app 和 sidecar-container 是两个容器,它们共享同一个 POD 的网络和存储。

7. 总结

  • POD ≠ 容器:POD 是 Kubernetes 的最小运行单元,容器是 POD 中的运行实体。
  • POD 的作用:封装容器、协调资源、简化管理。
  • 你的场景:10 个 POD 实际上是 10 个容器组(每个可能包含一个容器),Kubernetes 通过 POD 管理这些容器的生命周期和资源分配。

如果需要进一步优化资源或理解 POD 的调度策略,可以结合 Horizontal Pod Autoscaler(HPA) 或 资源限制(Resource Limits) 进行调整。

大数据场景的技术栈

  • 硬件
  • 软件定义计算/存储/网络资源(Yarn)Yarn和K8S是两种不同的集群管理和调度系统,它们在管理资源和调度任务方面有着不同的设计目标和应用场景。,的区别如下:
    • YARN 的设计重点是对大数据处理作业(如批处理、交互式处理和流处理)的优化。它有一个中心的资源管理器(ResourceManager)管理着分布在集群的所有计算资源,以及多个节点管理器(NodeManager),运行在集群的每个节点上。YARN 不具备容器原生支持,它通常管理的是进程级的应用程序
    • Kubernetes 是一个开源的容器编排系统,用于自动部署、扩展和管理容器化应用程序。
      它具有强大的容器支持和原生云环境的集成,非常适合微服务架构和持续集成/持续部署(CI/CD)。
      Kubernetes 中没有“集群”一说,而是通过集群来管理运行各种容器化应用程序的资源,没有绑定特定的数据处理框架或技术栈。
      它包含一个主机(Master)组件,负责全局的调度和管理,以及多个工作节点(Worker Node),运行容器化应用程序。
      Kubernetes 通过 Pods 来管理容器,Pod 是 Kubernetes 应用部署的最小单元,可以包含一个或多个容器。

    • 应用生态系统:YARN 主要针对的是大数据生态系统(特别是 Hadoop 相关的框架),而 Kubernetes 设计用于广泛的容器化应用程序和服务。

    • 资源和任务管理:YARN 管理的是 Hadoop 集群资源,强调在这些资源上运行不同的数据处理作业;Kubernetes 管理的是容器级别的资源,它允许容器在分布式环境中灵活运行。

    • 架构和抽象级别:YARN 是以进程为单位进行资源管理,侧重于大数据任务,而 Kubernetes 是以容器为单位,侧重于应用服务的部署和管理。

    • 使用场景:YARN 常常用在大数据分析场景(Hadoop生态),比如运行 Spark、Hive 作业;Kubernetes 则广泛用于微服务架构的应用管理和云原生应用。

  • 计算引擎
    • 离线型(批处理,有边界数据集):Hadoop、Spark
      • Hadoop 的 MapReduce是一种编程模型,用于在 HDFS 上对数据进行批处理计算;更适合于容忍较长时间的批量数据处理任务。
      • Hadoop 的 MapReduce 技术在设计上面向磁盘,适用于延迟不敏感的用例
      • 补充Hadoop生态:
        • HDFS(分布式文件系统,用于存储大量数据)
        • MapReduce(编程模型和处理框架,用于在分布式环境中批量处理数据),
        • YARN(资源管理和任务调度)
        • Hive(SQL 类查询)数据仓库基础架构,提供了一个SQL-like interface(HiveQL)进行数据汇总、查询和分析
          • Hive 是一个构建在 Hadoop 上的数据仓库框架,用于提供数据摘要、查询以及分析。Hive 允许用户用类似 SQL 的语言进行数据查询,这种语言被称为 HiveQL(Hive Query Language)。通过将 HiveQL 语句转换成 MapReduce、Tez 或 Spark 作业,Hive 使得 SQL 开发人员可以不必了解 Java 或底层的数据处理技术就能查询大规模数据集。
          • OLAP数据库和Hive区别:OLAP数据库(如Doris/StarRocks/阿里的adb、holegres)和Hive都被用于数据分析和决策支持(OLAP场景),但它们在设计、功能和最佳使用场景方面存在差别
            • OLAP数据库:被设计来执行快速的数据分析和复杂的查询操作(如优化了聚合操作例如求和、计数、平均值等),它通常用于多维数据分析(如数据立方体分析) (提供实时到近实时的查询性能,如对存储的数据执行快速、复杂的分析查询,Doris比Hbase 可能更适合
            • Hive虽然可以用来进行数据仓库任务,Hive更多是用于处理大数据集的批量数据分析和数据挖掘任务。与OLAP数据库相比它的查询性能可能不够快,特别是在交互式查询的上下文中(但是在大规模批量数据处理有优势)。
        • HBase(NoSQL 数据库)
          • Bigtable不是一个产品,而是Google公司在一篇论文中描述的一个分布式存储系统概念,用于管理结构化数据的可扩展性;HBase被设计成为Bigtable的开源等价物
          • Hbase开源的非关系型分布式数据库,它构建在Hadoop之上,特别是利用了HDFS作为其底层存储
          • 它的数据模型列族导向的,这意味着它特别适合于访问数据的特定列,对于稀疏数据表格也非常高效。
          • 通常用作在线事务性数据库(OLTP场景/系统)和作为实时查询的、大型数据集的存储和处理平台。(如 处理高速、随机的读写访问
        • OpenTSDB(Open time series data base) 是一个开源的、可扩展的时间序列数据库,它基于Hadoop的HBase构建。时间序列数据库主要用于存储和查询随时间变化的数据点,这类数据通常包括服务器监控指标(如CPU使用率、内存使用情况)、传感器数据等
        • Zookeeper(集中服务,为分布式应用提供一致性和协调)
        • Kafka(分布式流处理平台,常用于建立实时数据管道和流式应用程序等)
        • Apache Cassandra是一个分布式NoSQL数据库管理系统(用于存储结构化数据并提供查询功能),旨在处理跨多个商品服务器的大量数据,提供高可用性和无单点故障。
        • Apache Flume是一种高效、可靠的服务,用于收集、聚合和移动大量的日志数据(专注于日志数据的收集与传输)。它具有简单的配置方式,能够灵活地从各种来源采集日志,并将其传输至指定的目标。

        • Spark(作为MapReduce的替代,提供高速的数据处理和带有机器学习库的API):

          Spark 的初衷是解决MapReduce在迭代计算和交互式查询等方面的局限性。MapReduce每次执行任务都需要读写HDFS(Hadoop分布式文件系统),这导致了较高的I/O开耗时。而Spark通过引入内存计算模型,能够在内存中缓存中间结果,大大提高了数据处理速度,尤其是在需要多次操作同一份数据集的情况下。

          此外,Spark不仅仅局限于批处理作业,它还支持流处理(Spark Streaming 是 Spark 中的一个组件,专门用于实时数据流处理)、图计算、机器学习等多种工作负载,形成了一个统一的数据处理平台。这种多功能性使得用户可以在同一个平台上完成不同类型的数据分析任务,而无需切换不同的工具或框架,从而简化了开发流程并提高了效率。

        • Storm(实时大数据处理框架,跟flink是竞品)
    • 实时型(流计算,无边界数据集):Flink、Storm
      • 设计为内存计算,具备更低的延迟和更高的吞吐量,适用于实时数据流处理。
      • 以数据流为中心,提供了灵活的窗口操作和时间管理功能,它能够处理有界(批处理)和无界(流处理)的数据集,实现了真正的流批一体化。
  • 数据库/存储(Hbase、OLAP数据库 (如Doris/StarRocks/阿里的adb、holegres))
    • Hbase前面有介绍
      • 底层 用 HDFS作为存储
      • 场景再强调一下:通常用作在线事务型数据库(OLTP场景/系统)和作为实时查询的、大型数据集的存储和处理平台。(如 处理高速、随机的读写访问)
      • HBase虽然被设计为一个非关系型数据库,它也被广泛应用于需要快速随机访问 大量数据的场景中,包括某些类型的OLTP应用
        尽管HBase不直接支持SQL或提供传统的关系型数据库特性如复杂的事务管理,但它通过一些机制来满足特定类型的OLTP需求:

        强一致性读写:对于单行的操作可以提供类似于ACID事务的支持。
        版本控制:允许对同一单元格的数据进行多版本存储,这对于历史数据分析很有用。
        Region分割:自动将表分成多个region以便于并行处理和负载均衡。

        因此,当说HBase“用作在线事务型数据库”时,更多是指它在大规模数据集上的实时读写能力以及其在特定应用场景下的表现,而不是说它完全替代了传统的RDBMS所有功能。例如,在社交网络、即时消息服务等领域,HBase因其高效的数据检索能力和良好的扩展性而成为一种受欢迎的选择。

    • OLAP数据库:
      • 底层不一定是 HDFS 存储(如 Doris 主要以本地文件系统(如 ext4、xfs)存储其数据,也支持导入各种数据源 如 HDFS)
      • 场景再强调一下:提供实时到近实时的查询性能,如对存储的数据执行快速、复杂的分析查询,Doris比Hbase 可能更适合
      • 几种常见的OLAP数据库
        • Doris/StarRocks:Doris(之前也称为Apache Doris 或 Palo)和StarRocks实际上同源于一个项目。它们都是高性能、实时分析型、MPP(Massively Parallel Processing)架构的列式数据仓库系统;
          • Doris是Baidu开源的
          • StarRocks是由Doris衍生而来的商业化产品,提供更多的企业级特性和优化,以及更高性能的查询处理能力
        • 阿里的adb、hologres
  • 工作台(批处理-maxcompute/流处理-flink /OLAP数据库-hologres等):大数据的场景比较特殊的一个地方是通常需要面向数据分析师等角色使用,这个需要一个工作平台,像阿里云的dataworks之类的。

AnalyticDB和Hologress关系(都是实时数仓/OLAP数据库):

ADB是阿里云数据库事业部团队提供的云原生数据仓库AnalyticDB MySQL版(放弃了对事务的支持),简称ADB(历史上也叫ADS)

Hologres是阿里云计算平台事业部出品,来自Holographic全息的+Postgress(PostgreSQL,MySQL 是一个纯粹的关系数据库。而另一方面,PostgreSQL 是一个对象关系数据库。这意味着在 PostgreSQL 中,您可以将数据存储为具有属性的对象)。

一款是AnalyticDB for MySQL,一款是Hologres。ADB是阿里云数据库事业部团队提供的云原生数据仓库AnalyticDB MySQL版,是阿里巴巴自主研发的海量数据实时高并发在线分析云计算服务。
Hologres是阿里云计算平台事业部提供的一款全面兼容PostgreSQL协议并与大数据生态无缝打通的实时交互式分析产品。

从实际业务场景出发,两者的主要区别有以下几点:

1)与MaxCompute的打通性

Hologres:与MaxCompute打通,可以直接通过外部表读取MaxCompute数据进行查询分析,无需存储就能查询。

ADB:能加速查询MaxCompute,提供复杂交互式分析、实时混合数据仓库等多种场景。

2)成本方面

从我们每年ADB和Hologres的的单价上对比,Hologres成本相比ADB略微低。

3)灵活度

均能满足OLAP场景,Hologres兼容兼容PostgreSQL生态,ADB坚兼容MySQL协议,均能满足实时和离线批量的数据导入和分析。

4)性能
对于需要处理大量随机查询的场景,AnalyticDB 是一个更好的选择。而对于需要处理大规模的数据集的场景,Hologres 是一个更好的选择。

离线批处理(mapreduce)

2.1 基本概念和数据流转流程

  1. 基本概念
    • 大数据计算:大数据计算是指对海量数据进行处理和分析的一系列技术和方法。以单词计数为例,当面对大量文本文件(如互联网上的海量网页文本、社交媒体的大量用户发言等),需要统计每个单词出现的次数,传统的计算方式在数据量巨大时会面临性能瓶颈,而大数据计算技术可以高效地完成此类任务。
    • MapReduce:它是一种编程模型,用于大规模数据集(大于 1TB)的并行运算。MapReduce 主要包含两个阶段:Map 阶段和 Reduce 阶段。
      • Map 阶段:将输入数据分解为多个键 - 值对(key - value pairs),对于单词计数来说,输入是文本文件中的每一行内容,Map 函数会将每行文本拆分成一个个单词,每个单词作为键(key),值(value)设为 1,代表这个单词出现了一次。例如,对于输入行 “hello world”,会输出两个键 - 值对 < hello,1 > 和 < world,1>。
      • Reduce 阶段:对 Map 阶段输出的具有相同键的键 - 值对进行合并和汇总。在单词计数中,Reduce 函数会将相同单词(相同键)的所有值相加,得到每个单词的总出现次数。例如,输入多个 <hello,1> 的键 - 值对,Reduce 阶段会输出 < hello, n>,其中 n 是单词 “hello” 出现的总次数。
  2. Yarn 基本概念与作用
    • Yarn(Yet Another Resource Negotiator):是一个资源管理和任务调度框架,用于管理集群中的计算资源并调度应用程序。在大数据计算环境中,集群包含众多节点(物理服务器或虚拟机),每个节点有自己的 CPU、内存等资源。Yarn 负责分配这些资源给不同的 MapReduce 任务(以及其他大数据计算任务)。
    • 主要组件:
      • ResourceManager(RM):整个集群资源的管理者,负责接收用户的任务请求,分配资源给各个应用程序(如 MapReduce 任务),并监控资源的使用情况。它就像是集群资源的 “总指挥”,决定每个任务可以使用多少 CPU、内存等资源。
      • NodeManager(NM):运行在集群的每个节点上,负责管理本节点的资源(如 CPU、内存等),并向 ResourceManager 汇报资源的使用情况。同时,它会接收 ResourceManager 的指令,启动、停止任务容器(Container)来执行具体的计算任务。
  3. 数据在集群中的流转过程(以单词计数为例)
    • 数据存储与分片:假设我们有大量文本文件存储在集群的分布式文件系统(如 HDFS - Hadoop Distributed File System)中。这些文件会被划分为多个数据块(Data Block),数据块是存储和处理的基本单位,例如 HDFS 默认的数据块大小是 128MB。这些数据块分布在集群的不同节点上,这是数据在集群中的初始存储状态。
    • 任务提交与资源分配:
      • 当用户提交单词计数的 MapReduce 任务时,任务请求先到达 Yarn 的 ResourceManager。ResourceManager 会根据集群的资源情况(如各节点的空闲 CPU、内存等),为这个任务分配一定数量的资源,包括在哪些节点上启动 Map 任务和 Reduce 任务。
      • 例如,ResourceManager 可能会决定在存储了部分文本数据块的节点上启动 Map 任务容器(Container),这些容器是在 NodeManager 的管理下在节点上创建的。每个 Map 任务容器被分配一定的内存和 CPU 资源,用于处理分配给它的数据块。
    • Map 阶段的数据处理与流转:
      • 每个 Map 任务容器读取分配给它的数据块中的文本内容,按照 Map 函数的逻辑,将每行文本拆分成单词,并生成键 - 值对(<单词,1>)。
      • 这些键 - 值对会暂时存储在本地缓存(Local Buffer)中,当本地缓存达到一定大小或者 Map 任务完成一定比例时,数据会被溢写到磁盘(Spill to Disk),形成中间文件。这个过程中,数据可能会经过分区(Partition)操作,将具有相同键(或者符合某种分区规则的键)的数据划分到同一个分区中,方便后续 Reduce 任务的处理。
      • 中间文件会被存储在本地节点上,并且其位置信息会被传递给 Reduce 任务。
    • Reduce 阶段的数据处理与流转:
      • Reduce 任务在开始之前,会从各个 Map 任务那里获取中间文件的位置信息,然后通过网络从多个 Map 任务节点拉取属于自己处理范围(根据分区和键范围确定)的中间数据。
      • 每个 Reduce 任务将拉取到的相同键的键 - 值对进行合并和汇总,计算出每个单词的总出现次数,生成最终的结果。这些结果可以输出到指定的存储位置,如另一个文件或者数据库等。

 

通过这样的流程,利用 MapReduce 编程模型和 Yarn 的资源管理与调度,大数据计算任务可以在集群环境中高效地处理海量数据,实现诸如单词计数这样的复杂数据分析任务。整个过程中,数据在集群的不同节点之间合理地分配、处理和流转,充分利用了集群的计算资源。

2.2 例子:单词计数

以下是以 wordcount.txt 文件为例,在包含一个 ResourceManager 和两个 NodeManager(对应两个节点)的集群环境下,数据文件如何存储以及 MapReduce 计算过程的详细说明,为了简化理解,省略了部分复杂的细节处理:
#wordcount.txt文件内容
hello,word,nihao
csust,hello
hello,csust,nihao
nihao,hello,word

数据文件存储

假设 wordcount.txt 文件大小为 200MB(大于 HDFS 默认的数据块大小 128MB,便于展示分片情况),并且存储在集群的分布式文件系统(如 HDFS)中。
  • 数据块划分:HDFS 会按照设定的数据块大小将 wordcount.txt 文件划分为两个数据块,例如 Block1(大小为 128MB,包含文件前面部分内容)和 Block2(大小为 72MB,包含文件剩余部分内容)。
  • 节点分配:这些数据块会被分散存储在集群的不同节点上,假设 Block1 存储在 NodeManager1 管理的节点上Block2 存储在 NodeManager2 管理的节点上,以此实现数据的分布式存储,提高数据的可靠性和可并行处理性。

MapReduce 计算过程

任务提交与资源分配
  • 用户提交针对 wordcount.txt 文件进行单词计数的 MapReduce 任务后,任务请求先发送到 ResourceManagerResourceManager 会根据集群当前的资源状况(如两个 NodeManager 汇报的节点空闲 CPU、内存等资源信息)来分配资源,并决定在哪些节点上启动 Map 任务和 Reduce 任务。
  • 由于数据块 Block1 在 NodeManager1 对应的节点Block2 在 NodeManager2 对应的节点,ResourceManager 会指示 NodeManager1 在其节点上启动一个 Map 任务(记为 Map1)来处理 Block1,同时指示 NodeManager2 在其节点上启动另一个 Map 任务(记为 Map2)来处理 Block2
    • 在 Hadoop 集群中,计算节点一般和存储节点相同,即 MapReduce 框架和 Hadoop 分布式文件系统均运行在同一组节点上。这种配置允许框架有效地调度已经存在数据的节点上的作业,使得跨集群的带宽具有较高的聚合度,能够有效利用资源。
Map 阶段
  • Map1 任务处理(NodeManager1 节点上):
    • Map1 任务读取所在节点存储的 Block1 数据(也就是 wordcount.txt 文件前面部分内容),按照 Map 函数逻辑,对每行文本进行处理。例如,对于第一行 “hello,word,nihao”,会将其拆分成三个键 - 值对 <hello,1><word,1><nihao,1>。以此类推,对 Block1 中的所有文本行都进行这样的处理,生成一系列键 - 值对。
    • 生成的这些键 - 值对会先缓存在本地内存的缓冲区(假设为 Local Buffer1)中,当 Local Buffer1 达到一定大小(比如 80% 满了)或者 Map1 任务处理完一定比例的数据后,就会将缓存中的数据溢写到本地磁盘上,形成中间文件(记为 IntermediateFile1)。在溢写过程中,可能还会根据一定规则对键进行分区等操作,方便后续 Reduce 任务准确获取对应的数据。
  • Map2 任务处理(NodeManager2 节点上):
    • 与 Map1 类似,Map2 任务读取所在节点存储的 Block2 数据(wordcount.txt 文件后面部分内容),对每行文本进行拆分,比如对于其中一行 “nihao,hello,word”,会生成 <hello,1><word,1><nihao,1> 等键 - 值对。
    • 同样将生成的键 - 值对缓存在本地内存缓冲区(Local Buffer2),当满足溢写条件后,将数据溢写到本地磁盘形成中间文件(记为 IntermediateFile2),也会进行相应的分区等操作。
Reduce 阶段
  • 资源分配与数据拉取:
    • ResourceManager 会根据整体任务情况,在某个节点(可以是 NodeManager1 或 NodeManager2 对应的节点,或者其他有空闲资源的节点,这里假设分配在 NodeManager1 对应的节点上)上启动一个 Reduce 任务(记为 Reduce1),并为其分配相应的资源(如一定的内存、CPU 时间等)。
    • Reduce1 任务启动后,会从 Map1 和 Map2 所在节点获取中间文件(IntermediateFile1 和 IntermediateFile2)的位置信息,然后通过网络分别从 NodeManager1 和 NodeManager2 对应的节点拉取属于自己处理范围(在这个简单例子中,就是所有的中间键 - 值对,因为我们要汇总所有单词的出现次数)的中间数据。
  • 合并汇总计算:
    • Reduce1 任务拿到拉取过来的所有中间数据(包含众多的键 - 值对)后,会按照键进行分组,将相同键(也就是相同单词)的所有值(都是 1)进行合并汇总。例如,对于单词 “hello”,会把从各个中间文件中拉取到的所有 <hello,1> 键 - 值对中的值相加,最终得到单词 “hello” 的总出现次数,以此类推,对所有单词都进行这样的处理。
  • 结果输出:
    • 最后,Reduce1 任务将每个单词及其对应的总出现次数作为最终结果,可以按照要求输出到指定的存储位置,比如输出到 HDFS 中的另一个文件(例如 result.txt)中,方便后续查看和分析。


通过这样的流程,在一个简单的包含 ResourceManager 和两个 NodeManager 的集群环境下,利用 MapReduce 编程模型实现了对 wordcount.txt 文件中单词的计数任务,展示了数据在不同节点上存储以及如何在各个节点间流转进行计算处理的过程。

2.3 不好理解的概念:容器与分区

1. 关于任务容器以及和 K8s 容器对比、任务销毁的问题

(1)Map/Reduce 任务与容器的关系
  • 在 Yarn 环境下,Map 任务(如Map1)和 Reduce 任务(如Reduce1)是在容器(Container)中运行的。这些容器是由NodeManager在节点上启动和管理的。Yarn 的容器和 K8s 容器有一些相似之处,但也有区别。
  • 相似点:
    • 它们都是一种资源隔离和封装的机制。在运行任务时,都会为任务分配一定的资源(如 CPU、内存等),使得任务可以在相对独立的环境中运行,避免相互干扰。例如,在 Yarn 的容器和 K8s 容器中,都可以指定任务使用的内存大小,从而保证每个任务都能在分配的资源范围内运行。
  • 不同点:
    • 应用场景和管理范围:K8s 主要用于管理和编排容器化的应用程序,它提供了更广泛的功能,如服务发现、负载均衡、自动伸缩等,适用于部署和管理各种复杂的在线应用。而 Yarn 的容器主要是针对大数据计算任务,重点在于资源分配和任务调度,以确保 MapReduce 等大数据任务能够高效地在集群中运行。
    • 资源分配粒度和灵活性:K8s 在资源分配上可能更加灵活,可以根据应用的实际需求进行更精细的资源调整(如通过资源请求和限制来动态分配 CPU 和内存),并且支持多种类型的资源分配方式(如基于 CPU 和内存的比例分配等)。Yarn 的容器资源分配相对来说更侧重于大数据任务的特点,根据 Map 和 Reduce 任务的类型和需求分配固定的资源块(如固定大小的内存和 CPU 核心数),以满足数据处理阶段的要求。
(2)Map 和 Reduce 容器的区别
  • 基础镜像(如果有)差异:
    • 在实际应用中,Map 和 Reduce 容器可以基于相同或不同的基础镜像。如果是相同的基础镜像,它们在运行时可能会加载不同的配置和依赖库来满足各自任务阶段的需求。如果是不同的基础镜像,通常是因为 Map 和 Reduce 任务的功能有较大差异。例如,Map 任务可能只需要包含用于数据读取、拆分和初步处理的工具和库,而 Reduce 任务可能需要额外的聚合、排序等功能相关的库。
  • 任务功能和资源需求:
    • Map 容器:主要负责读取和初步处理输入数据块。它的重点在于将输入数据分解为键 - 值对,因此在数据读取和解析方面需要更多的资源(如 I/O 带宽)。在资源分配上,可能会有较大的内存用于缓存中间数据(在溢写之前),并且需要足够的 CPU 来快速处理文本行的拆分等操作。
    • Reduce 容器:专注于接收来自多个 Map 容器的中间数据,并进行合并汇总。它需要更多的网络资源来从不同节点拉取中间数据,并且在内存中需要足够的空间来存储和处理这些中间数据进行分组和聚合操作。在 CPU 资源上,主要用于对相同键的数据进行计算(如求和、计数等)。
(3)任务容器的销毁
  • 在每次计算完成后,这些启动的 Map 任务(容器)和 Reduce 任务(容器)通常会被销毁。这是因为 Yarn 是按照任务来分配资源的,当一个 MapReduce 任务的所有阶段(Map 和 Reduce)都完成后,这些容器占用的资源就可以被释放,以便为其他任务提供资源。
  • 不过,在一些特殊情况下,如任务需要进行调试或者后续可能会有类似任务复用部分配置等情况,也可以通过一些配置或者管理手段来保留容器或者其相关配置信息,但这不是默认的做法。

2. 关于中间文件分区操作的问题

(1)分区操作的目的和规则
  • Map1任务将数据溢写到本地磁盘形成中间文件(IntermediateFile1)的过程中,分区操作是为了方便后续 Reduce 任务能够高效地获取和处理数据。分区的规则通常是基于键(也就是单词)的某种哈希函数或者范围划分。
  • 例如,一种简单的分区规则可以是根据单词的首字母进行分区。假设分为三个分区:以 a - g 开头的单词为一个分区,以 h - n 开头的单词为一个分区,以 o - z 开头的单词为一个分区。这样,当 Map 任务生成键 - 值对(如<hello,1>)时,因为 “hello” 以 “h” 开头,就会将这个键 - 值对划分到对应的分区(h - n 开头单词的分区)中。
  • 更复杂的分区规则可能会考虑数据的分布均匀性Reduce 任务的数量等因素,以确保每个分区的数据量相对均匀,避免某个 Reduce 任务处理的数据量过大或过小,从而提高整个任务的并行性和效率。
(2)分区后中间文件的存储形式
    • 分区操作后,在NodeManager1节点的本地磁盘上,会根据分区规则形成多个中间文件或者文件片段。例如,按照上述简单的分区规则,可能会形成三个中间文件(或文件片段),每个文件(或片段)对应一个分区。
    • 这些中间文件除了存储键 - 值对外,还会包含一些元数据信息,如分区编号、每个分区的数据大小、键 - 值对的数量等。这些信息对于 Reduce 任务准确地拉取和处理数据非常重要。当 Reduce 任务从NodeManager1拉取数据时,它可以根据这些元数据信息快速定位到自己需要处理的分区对应的中间文件,从而提高数据获取和处理的效率。

Apache Flink 

3.1 数据源定义(订阅外部数据源的topic)

在 Apache Flink 的 SQL API 中,CREATE TABLE 语句用于定义表的结构,包括其元数据列。Flink SQL 允许用户在表定义中包括一系列的列属性,这些属性不仅仅限于传统的数据列,还包括一些特殊的元数据列,用以提供额外的信息,如时间戳、水印等,对于处理时间敏感的流数据尤为重要。

以下是一个简化的 CREATE TABLE 语句示例,展示了如何在 Flink SQL 中定义包含元数据列的表:

 1 CREATE TABLE my_table (
 2    -- 数据列
 3    id INT,
 4    name STRING,
 5  
 6    -- 元数据列
 7    event_time TIMESTAMP(3) METADATA FROM 'timestamp', -- 对应源数据的时间戳字段
 8    watermark FOR event_time AS event_time - INTERVAL '5' SECOND -- 设置水印,以支持事件时间的窗口操作
 9  
10    -- 表的连接器、格式化器、以及其它配置信息
11  ) WITH (
12    'connector' = 'kafka', -- 使用 Kafka 连接器
13    'topic' = 'my_topic',
14    'format' = 'json', -- 数据格式为 JSON
15    -- 更多的 Kafka 连接器设置...
16  );

 

在这个例子中,除了常规的数据列 id 和 name 外,

第7行解释:还定义了一个名为 event_time 的元数据列,该列用来从数据源(如 Kafka 消息的某个字段)中提取时间戳信息。

  • 1. event_time TIMESTAMP(3):这定义了一个名为 event_time 的列,其数据类型是带有3位小数秒精度的时间戳(TIMESTAMP)。这意味着它可以精确到毫秒。
  • 2. METADATA FROM 'timestamp':这一部分说明了 event_time 列的实际值是从消息元数据中获取的,而不是直接包含在消息体内的数据字段里。这里的 'timestamp' 是指Kafka消息的一个特殊属性头信息键名,通常用于存储事件发生的确切时间。Flink会从每条记录的消息元数据中提取这个键对应的值作为 event_time 的值。

 

第8行解释:此外,还利用 event_time 列设置了水印策略(watermark),这对于时间窗口操作至关重要,因为它允许系统处理乱序事件,并为事件时间提供一定的延迟容忍度。

  • 1. watermark FOR event_time:这表示我们正在为 event_time 列设置一个水印(Watermark)。水印是流处理系统用来处理乱序事件的一种机制,它允许系统确定某个时间点之后不会再有更早的数据到达。
  • 2. AS event_time - INTERVAL '5' SECOND:这部分定义了如何生成水印。具体来说,每个事件的水印值等于该事件的 event_time 减去5秒。这意味着系统假设所有迟到的数据最多晚于它们的真实事件时间5秒钟。一旦处理到了某个特定时间点的水印,任何比这个水印时间还要早的数据都将被视为不再可能到达,并且可以安全地进行窗口计算等操作而不必等待更多延迟数据。

 

请注意,实际的元数据列和配置参数可能会根据你使用的具体连接器(如 Kafka、JDBC 等)和数据格式(如 JSON、Avro 等)有所不同。因此,了解具体连接器和格式化器的文档是非常重要的。

最后,WITH 子句中的属性用于指定表的物理数据存储相关的配置,如数据来源是 Kafka 时的主题(topic)、数据格式(format)等信息。这部分配置确保了 Flink 能够正确地读写外部系统的数据。

3.2 结果写入外部系统的topic

在 Apache Flink 中,使用 CREATE TABLE 语句写入外部系统是通过定义一个动态表连接到外部存储或消息系统来实现的。这个表可以连接到各种外部系统,比如 Kafka、Elasticsearch、JDBC 数据库等。通过定义这样的表,Flink 可以从这些外部系统读取数据作为输入流,也可以将数据输出到这些系统。

下面是一个简单示例,展示如何使用 CREATE TABLE 语句在 Flink SQL 中定义一个表来写入一个外部系统(假设是 Kafka):

写入 Kafka 示例

 1 CREATE TABLE kafka_output_table (
 2   id INT,
 3   name STRING,
 4   event_time TIMESTAMP(3),
 5   WATERMARK FOR event_time AS event_time - INTERVAL '5' SECOND
 6 ) WITH (
 7   'connector' = 'kafka', -- 使用 Kafka 连接器
 8   'topic' = 'output_topic', -- Kafka 主题名称
 9   'properties.bootstrap.servers' = 'localhost:9092', -- Kafka 集群地址
10   'format' = 'json', -- 数据格式
11   'sink.parallelism' = '1' -- 设置 sink 的并行度
12 );

 

在这个示例中,我们定义了一个名为 kafka_output_table 的表,它指定了要写入 Kafka 主题 output_topic 的数据结构。通过 WITH 子句提供连接器配置、Kafka 服务器的地址、以及数据的格式化方式。

写入操作

定义了输出表之后,你可以使用 INSERT INTO 语句将数据写入这个表(即写入外部系统):

1 INSERT INTO kafka_output_table
2 SELECT 
3   id, 
4   name, 
5   event_time 
6 FROM 
7   input_table; -- 假设已经定义了一个名为 input_table 的输入表

 

这个 INSERT INTO 语句会将 input_table 表中的数据写入到 kafka_output_table进而写入到配置的 Kafka 主题中

注意事项

  • 确保你选择的连接器(比如 Kafka、Elasticsearch)和你希望使用的数据格式(比如 JSON、Avro)已经在 Flink 中支持。
  • 外部系统的配置如 Kafka 主题、JDBC 的数据库连接细节是在 WITH 子句中指定的,这些配置会根据连接的系统和使用场景而有所不同。
  • 请参考 Flink 官方文档以了解更多与具体连接器相关的配置选项和示例。

Apache Flink 的强大之处在于它提供了统一的批处理和流处理模型,使得处理时间驱动的数据流与传统的批量数据处理得以无缝集成,从而可以灵活地满足各种复杂数据处理场景的需求。

 

3.3 水印和滚动窗口

Apache Flink 中的水印(Watermarks)是一种机制,用于处理事件时间(Event Time)中的乱序事件,以及在流数据处理中为窗口操作提供正确的时间语义。水印允许 Flink 对事件数据进行时间标记,以确定何时可以认为某个时间点的所有数据都已到达,从而可以对这些数据进行处理或聚合操作。

下面是一个使用水印的简单示例,假设我们有一个流式数据源,它不断地发送带有时间戳的事件数据,我们想要按照事件时间处理这些数据,并且使用基于时间的窗口进行一些聚合操作,比如计算每5分钟内的事件数量。

在定义表时引入水印:

 1 CREATE TABLE events (
 2   id INT,
 3   eventName STRING,
 4   eventTime TIMESTAMP(3), -- 事件时间字段
 5   WATERMARK FOR eventTime AS eventTime - INTERVAL '5' SECONDS -- 定义水印策略
 6 ) WITH (
 7   'connector' = 'kafka', -- 示例使用 Kafka 连接器
 8   'topic' = 'event_topic',
 9   'properties.bootstrap.servers' = 'localhost:9092',
10   'scan.startup.mode' = 'earliest-offset',
11   'format' = 'json',
12   ...
13 );

在这个示例中,WATERMARK FOR eventTime AS eventTime - INTERVAL '5' SECONDS 表示水印被定义为 事件时间戳 减去5秒。意味着在观察到某个事件时间戳后系统会等待5秒,以接收可能延迟到达的数据。这样,即使数据到达的顺序有所乱序,系统也能够基于事件的时间戳正确地处理它们。

接下来,我们可以定义一个窗口查询,来计算每5分钟内的事件数量

1 SELECT
2   TUMBLE_START(eventTime, INTERVAL '5' MINUTE) as windowStart,
3   COUNT(*) as eventCount
4 FROM events
5 GROUP BY TUMBLE(eventTime, INTERVAL '5' MINUTE);

在上述查询中,TUMBLE 是一个内置的窗口函数,用于基于时间对事件进行分组。这里它被配置为创建长度为5分钟的滚动窗口。窗口的开始时间通过 TUMBLE_START 函数获得,同时我们对每个窗口内的事件数进行计数。

利用水印,即使事件以乱序的方式到达,Flink也能保证窗口操作的正确性,因为它会根据水印和事件时间来触发窗口的计算,确保所有被认为属于该窗口的事件都已被处理。

总结,Flink中使用水印可以优雅地处理乱序事件流,保证时间窗口操作的准确性,非常适用于实时数据分析和流处理的场景。

 

3.4 水印和滑动窗口

在 Apache Flink 中,滑动窗口(Sliding Window)是一种时间窗口,它以固定的时间间隔(滑动间隔)在数据流上滑动,并为每个窗口期间内的数据执行聚合操作。与滚动窗口(Tumbling Window)相比,滑动窗口允许窗口之间有重叠,因此它非常适用于需要频灭聚合和分析的场景。

以下是结合使用水印(Watermarks)和滑动窗口的一个简单例子。在这个例子中,我们将计算过去10分钟内,每隔1分钟的事件数量,这意味着我们的窗口大小是10分钟滑动间隔是1分钟。同时,我们使用水印来处理可能出现的乱序事件,确保窗口的正确触发。

首先,定义一个带有时间戳和水印的数据源表:跟3.3一样,

数据源定义中,我们设置了水印策略。在这个策略中,水印设置为事件时间戳减去五秒,这意味着系统或许等待最多五秒获取晚于事件时间戳的数据。

 

然后,执行一个窗口聚合查询:

1 SELECT 
2   window_start, 
3   window_end, 
4   COUNT(*) AS event_count
5 FROM TABLE(
6   TUMBLE(TABLE event_stream, DESCRIPTOR(event_timestamp), INTERVAL '10' MINUTES // 窗口大小 , INTERVAL '1' MINUTES // 滑动间隔)
7 )
8 GROUP BY window_start, window_end;

 

在这个查询中:

  • 使用了 TUMBLE 函数的一个变体,它接收一个额外的参数来指定滑动间隔。这里,窗口的长度(INTERVAL '10' MINUTES)是10分钟,滑动间隔(第四个参数INTERVAL '1' MINUTES)是1分钟。
  • 我们按事件时间戳进行分组,并为每个窗口计算事件数量。
  • window_start 和 window_end 表示窗口的起始和结束时间

请注意,Flink SQL 中的滑动窗口聚合查询语法可能会根据版本有所不同。确认在你的 Flink 环境中使用正确的语法非常重要。

水印机制以及滑动窗口的应用,允许我们在保证窗口计算准确性的同时,灵活地处理乱序事件和延迟数据,这对实时数据处理尤为重要。

3.3 水印和滑动窗口(例子还不错)

【背景和例子】
假设我们正在处理一个实时的网页点击流数据,用户的点击事件包含时间戳和用户ID。我们的目标是计算每5分钟内,每个用户的点击次数。水印的时间戳定义为事件时间戳的最大值 - 1s。假设滑动窗口  大小为5 分钟,每次滑动步长为1分钟,即窗口1:[2023-10-08 10:00 , 2023-10-08 10:05)、窗口2:[2023-10-08 10:01 , 2023-10-08 10:06)

例如,某个用户的事件可能因网络延迟而晚于其他事件到达。以下是时间戳和用户ID的几个事件示例:

事件ID       时间戳                    用户ID
---------------------------------
1              2023-10-08 10:01     A
2             2023-10-08 10:03     B
3             2023-10-08 10:04     A
4             2023-10-08 10:05     B
5             2023-10-08 10:06     C
6             2023-10-08 10:02     A

【问题】请根据上述背景和例子,详细说明下每个事件到达时:水印、滑动窗口、点击次数的计算和更新情况,乱序情况下flink如何处理。

 

一、水印的定义及作用

 

水印在这个场景中定义为事件时间戳的最大值减去 1 秒。其作用是衡量事件时间的进度,帮助 Flink 确定在某个时间点之前的事件是否都已到达,以便触发窗口的计算。

 

二、滑动窗口的定义及特点

 

滑动窗口大小为 5 分钟,每次滑动步长为 1 分钟。这意味着窗口会随着时间的推移不断向前滑动,对不同时间段内的数据进行处理。在这个问题中,有多个滑动窗口,如窗口 1 为 [2023 - 10 - 08 10:00, 2023 - 10 - 08 10:05),窗口 2 为 [2023 - 10 - 08 10:01, 2023 - 10 - 08 10:06) 等。

 

三、每个事件到达时的计算和更新情况

 

  1. 事件 1:时间戳为 2023 - 10 - 08 10:01,用户 ID 为 A
    • 水印计算:此时只有这一个事件,最大时间戳为 2023 - 10 - 08 10:01,所以水印时间戳为 2023 - 10 - 08 10:00。
    • 窗口计算:
      • 窗口 1:事件 1 属于该窗口。为用户 A 在窗口 1 中创建一个计数器,并将其点击次数加 1。此时窗口 1 中用户 A 的点击次数为 1。
      • 窗口 2:事件 1 也在窗口 2 的时间范围内,但目前窗口 2 的计算暂未开始。记录用户 A 在窗口 2 中有一次潜在点击(暂不增加计数器)。
  2. 事件 2:时间戳为 2023 - 10 - 08 10:03,用户 ID 为 B
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:03,水印时间戳更新为 2023 - 10 - 08 10:02。
    • 窗口计算:
      • 窗口 1:事件 2 属于该窗口。为用户 B 在窗口 1 中创建一个计数器,并将其点击次数加 1。此时窗口 1 中用户 B 的点击次数为 1,用户 A 的点击次数仍为 1。
      • 窗口 2:事件 2 也在窗口 2 内,但窗口 2 未开始计算。记录用户 B 在窗口 2 中有一次潜在点击(暂不增加计数器)。
  3. 事件 3:时间戳为 2023 - 10 - 08 10:04,用户 ID 为 A
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:04,水印时间戳更新为 2023 - 10 - 08 10:03。
    • 窗口计算:
      • 窗口 1:事件 3 属于该窗口。用户 A 在窗口 1 中的点击次数再加 1。此时窗口 1 中用户 A 的点击次数为 2,用户 B 的点击次数为 1。
      • 窗口 2:事件 3 在窗口 2 内,但窗口 2 未开始计算。记录用户 A 在窗口 2 中有一次潜在点击(暂不增加计数器)。
  4. 事件 4:时间戳为 2023 - 10 - 08 10:05,用户 ID 为 B
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:05,水印时间戳更新为 2023 - 10 - 08 10:04。
    • 窗口计算:
      • 窗口 1:该窗口即将关闭,不再接收新事件。此时窗口 1 中用户 A 的点击次数为 2,用户 B 的点击次数为 1。
      • 窗口 2:事件 4 在窗口 2 的时间范围内,但此时窗口 2 未开始计算。记录用户 B 在窗口 2 中有一次潜在点击(暂不增加计数器)。
  5. 事件 5:时间戳为 2023 - 10 - 08 10:06,用户 ID 为 C
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:06,水印时间戳更新为 2023 - 10 - 08 10:05。
    • 窗口计算:
      • 由于最新产生的水印(2023 - 10 - 08 10:05)等于窗口 2 的结束时间(2023 - 10 - 08 10:06 的前一秒),根据假设(最新产生的水印大于或等于某个窗口的结束时间时,触发该窗口的计算;随着事件的不断到来,水印时间不断更新。当水印时间戳达到或超过 2023 - 10 - 08 10:06(窗口 2 的结束时间)时,Flink 会触发窗口 2 的计算。),触发窗口 2 的计算
      • 检查之前记录的潜在点击
        • 用户 A 在窗口 2 中有两次点击(来自事件 1 和事件 3),所以用户 A 的点击次数加 2,现在为 2。
        • 用户 B 在窗口 2 中有两次点击(来自事件 2 和事件 4),所以用户 B 的点击次数加 2,现在为 2。
        • 用户 C 的点击次数为 1(新事件)。
  6. 事件 6:时间戳为 2023 - 10 - 08 10:02,用户 ID 为 A
    • 水印计算:最大时间戳仍为 2023 - 10 - 08 10:06(因为事件 6 是迟到事件,最大时间戳未被更新),水印时间戳保持为 2023 - 10 - 08 10:05。
    • 窗口计算:
      • 窗口 1:已关闭,不再考虑
      • 窗口 2:事件 6 属于该窗口。用户 A 在窗口 2 中的点击次数再加 1。此时窗口 2 中用户 A 的点击次数为 3,用户 B 的点击次数为 2,用户 C 的点击次数为 1。

 

四、乱序情况下 Flink 的处理方式

 

在这个场景中,如果事件以乱序的方式到达,Flink 依靠水印机制来处理。当一个迟到的事件到达时,如事件 6,Flink 会根据当前的水印时间来判断该事件是否属于已经处理过的窗口。如果迟到事件的时间戳小于当前水印时间,就像事件 6 的时间戳 2023 - 10 - 08 10:02 小于水印时间 2023 - 10 - 08 10:05,Flink 会将其标记为迟到事件。

 

对于迟到事件,Flink 可以有不同的处理策略。一种常见的策略是将迟到事件放入侧输出流,以便后续分析或处理。另一种策略是更新已经计算过的窗口结果,但这可能会带来额外的计算开销和复杂性。具体的处理方式取决于应用的需求和性能考虑。

 

综上所述,通过水印机制和滑动窗口的结合,以及明确的触发条件假设,Flink 能够在处理实时网页点击流数据时,有效地应对乱序事件,确保每 5 分钟内每个用户的点击次数计算的准确性。同时,随着窗口的不断滑动,能够持续地对不同时间段的数据进行处理。
 

3.4 水印和滚窗口(例子还不错)

 同上述例子,改为滚动窗口,如窗口 1 为 [2023 - 10 - 08 10:00, 2023 - 10 - 08 10:05),窗口 2 为 [2023 - 10 - 08 10:06, 2023 - 10 - 08 10:10) 等)
一、水印的定义及作用

 

水印在这个场景中定义为事件时间戳的最大值减去 1 秒。其作用是衡量事件时间的进度,帮助 Flink 确定在某个时间点之前的事件是否都已到达,以便触发窗口的计算。

 

二、滚动窗口的特点

 

滚动窗口是固定大小且不重叠的窗口。在这个问题中,窗口大小为 5 分钟,如窗口 1 为 [2023 - 10 - 08 10:00, 2023 - 10 - 08 10:05),窗口 2 为 [2023 - 10 - 08 10:06, 2023 - 10 - 08 10:10) 等。

 

三、每个事件到达时的计算和更新情况

 

  1. 事件 1:时间戳为 2023 - 10 - 08 10:01,用户 ID 为 A
    • 水印计算:此时只有这一个事件,最大时间戳为 2023 - 10 - 08 10:01,所以水印时间戳为 2023 - 10 - 08 10:00。
    • 窗口计算:
      • 窗口 1:事件 1 属于该窗口。为用户 A 在窗口 1 中创建一个计数器,并将其点击次数加 1。此时窗口 1 中用户 A 的点击次数为 1。
      • 窗口 2 及其他后续窗口:由于时间未到,这些窗口暂不考虑。此时这些窗口中用户 A 的点击次数为 0。
  2. 事件 2:时间戳为 2023 - 10 - 08 10:03,用户 ID 为 B
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:03,水印时间戳更新为 2023 - 10 - 08 10:02。
    • 窗口计算:
      • 窗口 1:事件 2 属于该窗口。为用户 B 在窗口 1 中创建一个计数器,并将其点击次数加 1。此时窗口 1 中用户 B 的点击次数为 1,用户 A 的点击次数仍为 1。
      • 窗口 2 及其他后续窗口:由于时间未到,这些窗口暂不考虑。此时这些窗口中用户 B 的点击次数为 0。
  3. 事件 3:时间戳为 2023 - 10 - 08 10:04,用户 ID 为 A
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:04,水印时间戳更新为 2023 - 10 - 08 10:03。
    • 窗口计算:
      • 窗口 1:事件 3 属于该窗口。用户 A 在窗口 1 中的点击次数再加 1。此时窗口 1 中用户 A 的点击次数为 2,用户 B 的点击次数为 1。
      • 窗口 2 及其他后续窗口:由于时间未到,这些窗口暂不考虑。此时这些窗口中用户 A 的点击次数为 0。
  4. 事件 4:时间戳为 2023 - 10 - 08 10:05,用户 ID 为 B
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:05,水印时间戳更新为 2023 - 10 - 08 10:04。
    • 窗口计算:
      • 窗口 1:该窗口即将关闭,不再接收新事件。此时窗口 1 中用户 A 的点击次数为 2,用户 B 的点击次数为 1。
      • 窗口 2 及其他后续窗口:由于时间未到,这些窗口暂不考虑。此时这些窗口中用户 B 的点击次数为 0。
  5. 事件 5:时间戳为 2023 - 10 - 08 10:06,用户 ID 为 C
    • 水印计算:最大时间戳变为 2023 - 10 - 08 10:06,水印时间戳更新为 2023 - 10 - 08 10:05。
    • 窗口计算:
      • 窗口 1:已关闭,不再考虑。
      • 窗口 2:事件 5 属于该窗口。为用户 C 在窗口 2 中创建一个计数器,并将其点击次数加 1。此时窗口 2 中用户 C 的点击次数为 1。
  6. 事件 6:时间戳为 2023 - 10 - 08 10:02,用户 ID 为 A
    • 水印计算:最大时间戳仍为 2023 - 10 - 08 10:06(因为事件 6 是迟到事件,最大时间戳未被更新),水印时间戳保持为 2023 - 10 - 08 10:05。
    • 窗口计算:
      • 窗口 1:已关闭,不再考虑。
        • 若 假设可以将迟到事件放入已关闭窗口重新计算。  窗口 1:虽然已关闭,但重新纳入事件 6 进行计算。用户 A 在窗口 1 中的点击次数再加 1,现在为 3。对于乱序事件,Flink 需要额外的逻辑来处理迟到事件并将其放入已关闭的窗口重新计算。这可能会带来一些复杂性,例如需要维护已关闭窗口的状态以便重新计算,同时也可能会影响性能,因为需要对已关闭的窗口进行额外的操作。
        • 在一般情况下,对于滚动窗口,当一个事件迟到且其对应的窗口已经关闭通常不会将其放入已关闭的窗口重新计算
      • 窗口 2:事件 6 不属于该窗口,因为其时间戳小于窗口 2 的起始时间。所以窗口 2 中用户 A 的点击次数不变,仍为 0。

 

四、乱序情况下 Flink 的处理方式

 

在滚动窗口的情况下,对于乱序事件的处理相对简单。如果一个事件迟到,且其时间戳不在当前正在处理的窗口范围内,那么这个事件通常会被丢弃或者放入侧输出流以便后续分析。例如事件 6,由于其时间戳小于窗口 2 的起始时间,所以不会被纳入窗口 2 的计算。

 

综上所述,在使用滚动窗口处理实时网页点击流数据时,每个窗口独立计算,不重叠的特点使得计算更加清晰明确。通过水印机制,可以确保窗口在合适的时间进行计算,同时对于乱序事件也能根据窗口的定义进行恰当的处理。

AI时代的技术栈(后面补充)

  •  硬件:CPU --> GPU
  • 软件定义计算/存储/网络资源:怎么管理好GPU卡,去调度好GPU的池子,做好GPU的虚拟化等
  • 计算引擎&数据库:Ray、Milvus等
    • Ray 是一个开源的分布式计算框架,用于构建大规模的机器学习应用程序
    • 发Ray 是一个开源的分布式计算框架,用于构建大规模的机器学习应用程序。
      • 强大的并行处理能力:Ray 提供了简单的API,能够轻松地将串行代码并行化,支持多核CPU和GPU的计算资源。
      • 任务调度和执行:Ray 可以高效调度大量的小任务,这对于现代机器学习和AI应用特别重要。
      • 动态任务图执行:Ray 能够创建一个动态任务图,它将计算任务表示为图中的节点,这些任务可以并发执行。
      • Actor模型:Ray 支持类似 Erlang 的 Actor 模型,允许用户构建并行的,有状态的任务。
      • 内存对象存储:Ray 有内置的分布式对象存储,可以在工作节点之间共享大型数据对象而无需显式复制。
      • 轻量级:Ray 被设计成轻量级的,易于安装,快速启动。
      • 由于其效率和易用性,Ray 在机器学习社区中获得了很多支持,特别是与像 RLlib (强化学习库)和 Tune(超参数调优库,可以与多种机器学习框架集成(如 TensorFlow、PyTorch))这样的库结合使用。

    • 应用如
      • 模型训练与分布式深度学习:

        Ray 可以用于加速大规模的深度学习模型训练
        分布式计算能力允许将复杂的模型和大型数据集划分到多个处理单元(GPU或CPU)进行并行训练

      • 数据预处理和流水线:

        Ray 提供了强大的数据预处理功能,可以对大型数据集进行并行和异步预处理,从而为模型训练做准备。

      •  和RLlib (强化学习库)和 Tune(超参数调优库,可以与多种机器学习框架集成(如 TensorFlow、PyTorch))这样的库结合使用
    • Milvus 是一个开源的向量数据库,用于存储、索引和管理大规模的向量集合。它特别适合处理那些需要通过向量相似性搜索来检索信息的应用,例如图像识别和深度学习。

      •  

        强大的搜索能力:Milvus 支持多种相似性搜索算法,包括最近邻搜索(K-NN)和范围搜索等。
        高效的索引策略:Milvus 使用多种索引策略来优化不同规模数据集的搜索性能,如 IVF FLAT, IVF SQ8, HNSW 等。
        水平可伸缩:Milvus 设计用来支持水平扩展,它可在多个节点之间平衡存储和计算负载。

            

参考:AI时代的技术栈 https://mp.weixin.qq.com/s/cc1IrhTSbOyoTZ96VOikZw

 
 
 
 
 
 

posted on 2024-04-03 10:27  gogoy  阅读(345)  评论(0)    收藏  举报

导航