当前位置: 代码迷 >> 综合 >> 《设计数据密集型应用》- Designing Data-Intensive Application - 第11章 流处理 读书笔记
  详细解决方案

《设计数据密集型应用》- Designing Data-Intensive Application - 第11章 流处理 读书笔记

热度:96   发布时间:2024-03-07 08:49:33.0

在这里插入图片描述

流处理

传递事件流

消息系统(ACK)

  • 直接从生产者传递给消费者

    • 常见形式

      • UDP组播
      • 无代理的消息库
      • StatsD,使用UDP收集网络中所有机器的的指标并对其进行监控,只有收到所有消息,才认为计数器指标是正确的
      • HTTP或RPC请求
    • 存在问题

      • 容错能力差,即使有消息重传机制,当消费者或生产者崩溃时,仍会丢失数据
  • 消息代理(消息队列)

    • 容忍客户端连接的上线、崩溃
    • 带来异步与消息延迟问题
  • 消息代理与数据库对比

    • 数据库中的记录需要显式删除,而消息代理当消息消费完成后即自动删除
    • 当消息代理缓存过多数据导致存储从内存溢出到磁盘时,性能会下降
    • 数据库支持二级索引及多种查询方式,而消息代理一般只持按某种模式匹配主题
    • 消息代理没有数据库是快照的概念
  • 多个消费者

    • 负载均衡模式–多个消费者轮流或按一定分配策略消费
    • 扇出模式–多个消费者同时收到同一消息
  • 确认与重新交付

    • 重复消费

      • 为确保消息不丢失,在消费时采用ACK机制。如果消息已经消费完毕,但ACK数据包在网络丢失,导致代理在规定时间内没收到ACK,如果此时消息被转发至另一消费者,则可能会出现重复消费的问题
    • 消费顺序打乱

      • 先收到的消息由于消费者崩溃而导致消息排队到后续消息之后,在外部看来产生了消息消费的顺序打乱的问题

分区日志(OFFSET)

  • 使用日志进行消息存储

    • 解决由于消息无持久化而导致的消息不能被重新运算的问题
    • 为了提高吞吐,可以创建多个分区,每个分区有独立的日志文件,独立的offset
  • 日志与传统消息相比

    • 基于日志(OFFSET)可使用消费者组进行消费

    • 单线程消费分区中的所有消息

      • 消费者工作节点数最多等于分区数
      • 如果某条消息消费缓慢,则会阻塞后续消息的消费
    • 如果消息处理代价高昂,允许并行处理数据对数据处理顺序无要求的情况下,使用AMQP/JMS风格(基于ACK)的代理的可行的;如果要求高吞吐与顺序消费,基于日志方法(基于OFFSET)表现得非常好

  • 消费者偏移量

    • 顺序消费模式中,所有小于当前offset的消息均被消费,大于的未被消费
    • 顺序消费模式中,如果消费者发生了崩溃,分区将会指派给另一消费者,如果崩溃前消息的OFFSET没有更新,则消息有可能会消费2次
  • 磁盘空间使用

    • 日志文件通常分段存放,不时地将旧文件进行删除或归档
    • 如果消费速率跟不上生产速率,由于旧文件会不时清理,可能会错过一些数据。实际上,基于日志的系统会在磁盘上开辟环形缓冲区进行消息存储
    • 消息传递系统在内存队列太长之后才会向磁盘写入消息,当队列很短时,写入速度很快,而写入磁盘后速率会慢得多;相比之下,基于日志的系统消息写入方式不变,吞吐量稳定
  • 当消费者跟不上生产者时

    • 可以提示报警,在由于落后太多超出缓冲区范围前进行修复
    • 单个消费者落后太多不会影响其它消费乾的消费过程
  • 重播旧信息

    • 相比AMQP/JMS系统在确认会删除队列中的消息,基于日志的系统只进行只读操作,因此可对历史数据进行回放重复消费,应用在某些需要进行历史数据修复的场景

流与数据库

保持系统同步

  • 系统中OLTP提供用户服务,缓存加速请求,全文搜索处理搜索查询,数仓用于分析,各数据存储间需要保证数据一致性

  • 实现方式

    • 单写

      • 周期性地从主数据库(如OLTP)的数据副本中执行同步进程。如数据仓库,可以周期性地执行ETL进程更新数据。缺点是转储过程缓慢。
    • 双写

      • 即由应用层代码同时在多个数据存储中执行写入

        • 由于多个请求的竞争,可能会导致数据不一致

        • 一个成功,一个失败同样也会导致数据不一致

变更数据捕获

  • 变更数据捕获的实现

    • 常见方式:触发器、基于binlog的同步工具
    • 特点:异步,记录数据不会等消费者应用变更再提交
  • 初始快照

    • 从某个时间所对应的位置建立快照,并在之后的同步过程使用增量变更实现
  • 日志压缩

    • 在消息传递给下游之前,先将数据进行压缩(如借助Kafka的日志压缩功能,只保存KEY的最新VALUE值)
  • 变更流的API支持

    • RethinkDB允许查询订阅通知,当查询结果变更时获得通知【36】,Firebase 【37】和CouchDB 【38】基于变更流进行同步,该变更流同样可用于应用。而Meteor使用MongoDB oplog订阅数据变更,并改变了用户接口【39】。
    • VoltDB允许事务以流的形式连续地从数据库中导出数据【40】
    • Kafka Connect

事件溯源

  • 从事件日志中派生出当前状态
  • 命令和事件
  • 状态,流和不变性
  • 不可变事件的优点
  • 从同一事件日志中派生多个视图
  • 并发控制
  • 不变性的限制

流处理

与批处理的区别:由于数据无界,没有排序的概念,因此无法使用排序合并联接;出错后也不能像批处理从头进行运算

流处理的应用

  • 复合事件处理

    • 算子是固定的,流经不同的数据生成不同的结果
  • 流分析

    • 基于流事件计算统计聚合指标
    • 如:Apache Storm,Spark Streaming,Flink,Concord,Samza和Kafka Streams 【74】。托管服务包括Google Cloud Dataflow和Azure Stream Analytics
  • 维护物化视图

    • 维护缓存,搜索索引和数据仓库与主数据库的一致性
  • 在流上搜索

    • 固定查询搜索算子,当有数据FEED时,直接输出结果
  • 消息传递和RPC

    • Actor框架主要是管理模块通信的并发和分布式执行的一种机制,而流处理主要是一种数据管理技术。
    • Actor之间的交流往往是短暂的,一对一的;而事件日志则是持久的,多订阅者的。
    • Actor可以以任意方式进行通信(允许包括循环的请求/响应),但流处理通常配置在无环流水线中,其中每个流都是一个特定作业的输出,由良好定义的输入流中派生而来。

时间推理

  • 事件时间与处理时间

    • 由于网络延迟,消息达到顺序不一定是发生顺序
    • 基于事件时间或处理时间来进行统计,可能会得到不一样的结果
  • 知道什么时候准备好了

    • 无法确定窗口里的事件是否已经收集全
    • 对于滞留的事件,可以忽略这些事件;或者发布更正,这需要重新计算,回收之前窗口计算的值
  • 你用的是谁的时钟?

    • 对于设备端,离线脱机事件的缓存在设备端时钟不准确的情况下,其时钟需要校准
  • 窗口的类型

    • 滚动窗口(非重叠)
    • 滑动窗口(重叠)
    • 跳动窗口(重叠但窗之前间隔不连续)
    • 会话窗口

流式连接

  • 流流连接(窗口连接)

    • 对于前后相继的两个事件A与B,且先有A事件才有可能发生B事件,如果他们同属于同一会话,且B滞后A的时间不确定(几秒或几周不等),则计算B发生的条件概率时,一般考虑两事件时间间隔小于一定时间时,才连接两个事件
  • 流表连接(流扩展)

    • 即为流数据从数据库中添加关联数据,可通过在流处理中远程查询数据库再拼接数据实现,可能会较慢,导致数据库过载;另一种方法是在本地建立数据副本,但需要保证主数据与副本的及时同步
  • 表表连接(维护物化视图)

    • 通过流处理的方式将表与表之间的数据变更进行处理
  • 连接的时间依赖性

    • 当时间相近的流的事件,不同的处理顺序会产生不同的结果。比如对于流表连接,如果表内容发生了更新,需要考虑事件究竟和更新前的数据关联还是更新后的数据关联

容错

  • 微批量与存档点

    • 微批量即将流划分小块,每次处理一个块。如spark streaming
    • Flink基于Chandy-Lamport算法(用于做分布式快照)实现了检查点机制
    • 微批与存档点均实现了恰好一次语义
  • 原子提交再现

    • 在流处理框架同时管理状态变更与消息传递来内化事务
  • 幂等性

    • 可借助外部数据库实现。如消费kafka 数据时,将消费后的值连同OFFSET一同写入数据库,避免重复消费
    • 重启一个失败的任务必须以相同的顺序重放相同的消息(基于日志的消息代理能做这些事),处理必须是确定性的,没有其他节点能同时更新相同的值
    • 当消费出现故障转移时,需要加防护(fence)以防止被假死节点干扰
  • 失败后重建状态

    • 可以采取本地或远端保存状态的策略。如Flink将状态存在HDFS,当状态是由时间非常短的窗口聚合而成时,甚至可以直接从流中恢复
  相关解决方案