使用 Apache Iceberg 处理流数据

560 阅读29分钟

流数据指的是数据的连续生成和处理,通常来自各种来源。这些来源可以包括日志文件、传感器数据、社交媒体动态和金融交易等。数据以小尺寸(或数据包)的形式发送,以便实时获取见解和反应。流数据的本质是它处于不断运动状态,没有固定的起点或终点。

流数据的概念在当前数字信息时代尤为重要,因为企业、研究机构和政府机构通常需要基于最新数据进行分析和决策。例如,金融机构可能使用流数据来实时检测欺诈交易。同样,社交媒体平台使用流数据根据实时互动指标来定制和更新用户动态。

有几个理由需要将数据流入Apache Iceberg表:

可扩展性和性能

Apache Iceberg设计用于高效地存储和检索大数据集的信息。其文件管理程序使其能够优化不断变化/增长的数据集的性能,使其成为流分析的绝佳选择。

模式演变

随着时间的推移,数据结构(模式)可能需要演变。Apache Iceberg允许在不中断正在进行的数据流处理的情况下进行模式演变,使其更容易适应变化的数据需求。

可靠性

Apache Iceberg提供快照隔离,这意味着每个事务在一个不变的表快照上操作。此功能确保数据在许多并发操作环境中保持一致和可靠。

时间旅行

Iceberg存储了表的完整元数据历史,这允许进行时间旅行查询,可以访问表的先前版本。

通过将数据流入Apache Iceberg表,组织可以更高效、更灵活地管理和分析实时数据,适应变化,并在数据分析操作中保持可靠性。

使用 Spark 处理流数据

Apache Spark 的主要功能之一是处理流数据的能力。这通过 Spark Streaming 实现,Spark Streaming 是一个允许对实时数据流进行可扩展、高吞吐量、容错处理的 Spark 组件。以下是 Spark Streaming 的一些关键功能:

容错能力

Spark Streaming 设计为能够对故障具有弹性,具有内置的恢复机制。如果在计算期间某个节点出现故障,系统可以快速恢复并继续处理。

集成

Spark Streaming 无缝集成了其他 Spark 组件,例如 Spark MLlib 和 Spark SQL,从而实现强大的组合用例。例如,您可以使用 MLlib 在大型数据集上构建机器学习模型,然后使用 Spark Streaming 将该模型应用于实时数据流。

实时处理

Spark Streaming 可以实时处理实时数据流。它将传入的数据划分为批次,然后由 Spark 引擎处理这些批次生成最终的批次结果流。

窗口操作

Spark Streaming 提供窗口计算,允许在滑动窗口内对弹性分布式数据集(RDD)进行转换。在许多场景中非常有用,例如计算数据流中最近几小时的趋势。

高吞吐量

Spark Streaming 设计用于处理大量数据,使其适用于需要处理高容量实时数据流的应用程序。

多种数据源

Spark Streaming 可以从各种数据源获取数据,包括 Kafka、Flume 和 Kinesis 等。

Apache Spark 的微批处理方法使其与其他流引擎区别开来,使其成为实时数据处理的有力选择。此方法涉及将数据分成小的离散批次进行处理,而不是单独处理每个数据点。这种设计决策在延迟、吞吐量和成本等因素之间进行了权衡。

通过将数据分解为微批处理,Apache Spark 获得了多种优势。首先,它增强了容错能力,因为任何故障都可以在每个批次内更容易地管理。其次,它与其他 Spark 组件无缝集成,允许批处理和实时处理的统一生态系统。此外,这种方法实现了实时处理能力,因为微批处理以快速连续的速度处理。最后,它实现了高吞吐量,能够有效处理大数据量。

总体而言,Apache Spark 的微批处理方法在响应性、数据量处理和资源效率之间取得了平衡,使其成为大数据和流应用领域的首选。

使用 Spark 流入 Iceberg

使用 Spark 的 DataSourceV2 API 允许数据工程师以结构化和可扩展的方式从表中读取和写入数据。在 Spark 结构化流处理中,Spark 的基于 SQL 的流处理 API 从 Spark 3 开始完全兼容 Iceberg 的 DataFrame 读取和写入。此集成的一个关键特性是支持从历史时间戳开始处理增量数据。在从 Iceberg 表进行流读取的上下文中,仅支持追加快照。

Spark 的 DataStreamWriter 用于以流方式将数据写入 Iceberg 表。Iceberg 支持两种输出模式:append(将每个微批次的行追加到表中)和 complete(在每个微批次替换表内容)。

在对分区表进行写入时,Iceberg 要求数据按分区规范进行排序。虽然明确排序建议用于批量查询,但由于产生的延迟,流处理可能不实用。为此,Iceberg 提供了 fanout writer 选项,消除了对排序的需求,从而减少了延迟。fanout writer 是一种优化性能的技术。

维护 Iceberg 上的流表至关重要。流查询可以快速生成新的表版本,导致大量表元数据和小数据文件。为有效管理元数据,建议使用调整提交率、过期旧快照、压缩数据文件和重写清单等策略。

调整提交率有助于控制数据文件、清单和快照的数量,简化表维护。如果不进行管理,旧快照可能会迅速累积,应该定期过期和删除。数据文件的压缩有助于减少元数据并提高查询效率,而重写清单优化了写入延迟并压缩小清单文件。第4章详细介绍了这些表维护和优化操作。

让我们看一个使用 Spark 结构化流处理将金融数据流入 Apache Iceberg 表的示例,允许您持续捕获实时市场数据、交易和投资组合更新。我们将通过一个使用假设金融数据的简单示例进行演示。我们假设从 Kafka 主题读取数据并将其写入 Iceberg 表。

首先,我们使用 Scala 设置我们的 Spark Session:

val spark = SparkSession.builder()
    .appName("FinancialDataStreaming")
    .getOrCreate()

假设我们有一个 Kafka 主题 financialData,其中每条消息都是包含以下字段的 JSON 字符串:timestampsymbolpricevolume。我们使用 readStream 方法从 Kafka 读取:

val df = spark.readStream
    .format("kafka")
    .option("kafka.bootstrap.servers", "localhost:9092")
    .option("subscribe", "financialData")
    .load()

然后我们将 Kafka 流转换为 Stock 对象的 DataFrame:

val financialData = df.selectExpr("CAST(value AS STRING)").as[String]
    .map(Stock.from_json(_))

Stock 类可以定义如下:

case class Stock(timestamp: Long, symbol: String, price: Double, volume: Long)
object Stock {
  def from_json(jsonString: String): Stock = {
    val parser = new ObjectMapper()
    parser.registerModule(DefaultScalaModule)
    parser.readValue(jsonString, classOf[Stock])
  }
}

现在我们有了数据流,可以开始将数据追加到 Iceberg 表中。由于我们希望添加新数据而不影响现有数据,因此使用 append 输出模式:

val tableIdentifier = "s3://someBucket/financial_data/stock"

financialData.writeStream
    .format("iceberg")
    .outputMode("append")
    .trigger(Trigger.ProcessingTime(1, TimeUnit.MINUTES))
    .option("path", tableIdentifier)
    .option("checkpointLocation", "/tmp/checkpoints")
    .start()

如果 Iceberg 表按符号分区,我们需要启用 fanout-enabled 选项,以消除对数据排序的要求:

financialData.writeStream
    .format("iceberg")
    .outputMode("append")
    .trigger(Trigger.ProcessingTime(1, TimeUnit.MINUTES))
    .option("path", tableIdentifier)
    .option("fanout-enabled", "true")
    .option("checkpointLocation", "/tmp/checkpoints")
    .start()

最后,考虑到数据的快速增长,我们需要通过调整提交率、过期旧快照、压缩数据文件和重写清单来维护表。这确保了表的性能和可管理性。这些策略的组合将确保您的流数据平台保持可靠和稳健。

使用 Spark 从 Iceberg 中流出数据

流入 Iceberg 表时专注于摄取实时数据,而从 Iceberg 流出数据涉及读取这些数据以进行各种下游应用和分析任务。使用 Spark 结构化流处理,数据工程师可以高效地读取和处理存储在 Iceberg 表中的数据。

在 Spark 3 中使用 DataSourceV2 API,Iceberg 支持从历史时间戳开始处理增量数据,如以下代码所示:

val df = spark.readStream
    .format("iceberg")
    .option("stream-from-timestamp", Long.toString(streamStartTimestamp))
    .load(tableName)

对于从 Iceberg 表进行流读取操作,重要的是注意 Iceberg 仅支持从追加快照读取数据。此外,设计下游应用以适当地处理从 Iceberg 流出的实时数据至关重要。根据用例,这可能涉及实时分析、警报或进一步处理。

在当今互联的商业环境中,与合作伙伴共享实时数据是一个常见用例。当双方都使用 Iceberg 表时,可以使用 Spark 结构化流处理来简化此过程。以下是如何实现这一点的示例。

假设您想将您的 Iceberg 表 our_database.our_table 与合作伙伴共享,并且他们希望将此数据流入他们的 Iceberg 表 partner_database.partner_table。首先,您需要设置 Spark Session 以从您的表中读取:

val spark = SparkSession.builder()
    .appName("IcebergDataSharing")
    .getOrCreate()

接下来,启动一个流以从您的 Iceberg 表中读取数据:

val ourDF = spark.readStream
    .format("iceberg")
    .option("stream-from-timestamp", Long.toString(streamStartTimestamp))
    .load(sourceTableName)

现在,您将此数据流写入合作伙伴的 Iceberg 表。考虑到流数据的性质,您将使用 append 模式:

ourDF.writeStream
    .format("iceberg")
    .outputMode("append")
    .trigger(Trigger.ProcessingTime(1, TimeUnit.MINUTES))
    .option("path", destinationTableName)
    .option("checkpointLocation", checkpointPath)
    .start()

如果合作伙伴的 Iceberg 表按 region 列分区,您需要确保数据写入得到优化:

ourDF.writeStream
    .format("iceberg")
    .outputMode("append")
    .trigger(Trigger.ProcessingTime(1, TimeUnit.MINUTES))
    .option("path", destinationTableName)
    .option("fanout-enabled", "true")
    .option("checkpointLocation", checkpointPath)
    .start()

为了与合作伙伴保持无缝且高效的实时数据共享,遵循一系列最佳实践至关重要。首先,模式一致性至关重要;确保 our_database.our_table 的模式与 partner_database.partner_table 匹配或兼容,以防止写入错误。

此外,如果合作伙伴只对部分数据感兴趣,建议在写入表之前实现数据过滤。这不仅简化了流程,还使其更高效。此外,应建立强大的错误处理机制,以管理流处理失败,从而防止数据丢失或重复。最后,监控和设置流处理作业的警报至关重要。

这种方法将帮助您跟踪作业的性能并快速识别潜在问题,确保在出现问题时通知必要的利益相关者。通过遵循这些最佳实践,可以增强协作,并且双方都能从及时准确的信息中受益。

使用 Flink 进行流处理

Apache Flink 是 Apache 软件基金会开发的开源统一流批处理引擎。Flink 旨在以高速处理大规模数据,非常适用于实时数据处理和分析。

Flink 提供高吞吐量、低延迟的流处理引擎,以及事件时间语义、精确一次语义、反压控制等功能。它在处理流数据方面表现出色,同时也提供批处理能力、机器学习库和图处理能力。以下是 Apache Flink 流处理的一些关键功能:

事件时间处理

Flink 的事件时间处理功能允许处理无序数据并提供准确的结果,这对许多实时应用至关重要。例如,在金融交易和传感器数据处理等场景中,事件的顺序非常重要。

容错能力

与 Spark 类似,Flink 通过检查点和保存点机制提供容错功能。这些功能确保在处理过程中数据不会丢失,并允许从故障中恢复而不丢失数据。

反压控制

Flink 处理反压,即当数据的生产速度超过消费速度时,确保系统的稳定性,即使在面对大量数据涌入时也能保持稳定。

高吞吐量和低延迟

Flink 设计用于处理大规模数据,具有低延迟,非常适合需要实时洞察的应用。

窗口操作和复杂事件处理

Flink 提供基于时间、计数、会话等的灵活窗口操作。此外,它支持复杂事件处理(CEP),允许在数据流中进行模式检测和选择。

集成

Flink 可与各种数据源和数据汇集成,包括 Kafka、Hadoop 分布式文件系统(HDFS)和数据库(如 Cassandra)。

精确一次语义

Flink 支持精确一次处理语义,确保每条记录只被处理一次,从而提供准确的结果。

简而言之,Apache Flink 是一项强大的流处理技术,擅长提供高速、准确且可扩展的数据处理解决方案。其结合了强大的流处理能力、容错功能和与其他数据系统的集成,使其成为实时数据处理的强大选择。

使用 Flink 将数据流入 Iceberg

Apache Iceberg Flink 库允许您使用 Flink 的 DataStream API 和 Table API 将 Apache Iceberg 表作为数据源或数据汇进行操作。首先,我们将探索如何在 Flink 中从 Iceberg 读取数据,然后讨论如何将数据写入 Iceberg。

Flink 的 DataStream 和 Table API 是什么?

Apache Flink 平台具有两个主要的用于数据处理的 API:DataStream API 和 Table API,每个 API 都满足不同的需求。

DataStream API 设计用于处理连续的无界数据流,例如物联网(IoT)传感器数据或日志流,提供低级、表达性的方法,可以详细控制事件时间处理和窗口操作。它非常适合实时、事件驱动的场景,并具有广泛的连接选项。

相比之下,Table API 专注于结构化、有界数据的表格式处理,适用于批处理和分析任务。它提供了更高层次的抽象,具有类 SQL 接口,简化了数据处理任务的表达,并自动优化执行计划。虽然它也支持事件时间处理,但其复杂事件处理能力不如 DataStream API。

选择哪种 API 取决于任务的具体要求,DataStream API 提供详细控制适用于实时应用,而 Table API 更适合结构化数据和分析处理。

Flink 用于流读取

Apache Iceberg 通过 Apache Flink 的 DataStream API 和 Table API 提供对流和批量读取操作的强大支持。这种支持使用户可以轻松灵活地从 Iceberg 表中读取数据。

在 SQL 上下文中,可以通过简单的 SQL 命令在流模式和批模式之间切换 Flink 作业的执行模式。对于批量读取,用户可以在将运行时模式设置为批量模式后提交一个带有 SELECT 语句的 Flink 批量作业。相比之下,对于流读取,用户首先需要将运行时模式设置为流模式并启用动态表选项,然后可以使用带有 streaming 选项设置为 true 的 SELECT 语句并指定监控间隔。

让我们看一个使用批模式读取批量数据的示例。首先将执行模式设置为批处理模式,然后执行 SELECT 语句以从 sales_data 表中读取数据:

TableEnvironment tableEnv = TableEnvironment.create(EnvironmentSettings.newInstance().build());

// Set the execution mode to 'batch'.
tableEnv.getConfig().getConfiguration().setString("execution.runtime-mode", "batch");

// Execute a SELECT statement.
TableResult result = tableEnv.executeSql("SELECT * FROM sales_data");
result.print();

下面是一个使用流模式读取流数据的示例。首先将执行模式设置为流处理模式并启用动态表选项,然后执行带有 streaming 选项设置为 true 和监控间隔的 SELECT 语句:

// Set the execution mode to 'streaming' and enable dynamic table options.
tableEnv.getConfig().getConfiguration().setString("execution.runtime-mode", "streaming");
tableEnv.getConfig().getConfiguration().setBoolean("table.dynamic-table-options.enabled", true);

// Execute a SELECT statement with the streaming option and a monitor interval of 1 second.
TableResult result = tableEnv.executeSql(
    "SELECT * FROM sales_data /*+ OPTIONS('streaming'='true', 'monitor-interval'='1s')*/");
result.print();

Iceberg 甚至支持从特定历史快照 ID 开始读取增量数据。用户可以为流作业设置不同的 Flink SQL 提示选项以进行进一步定制,例如 start-snapshot-id 选项,如下代码所示。Hints 模式在许多软件框架中可用,您可以通过注释提交在运行代码之前处理的指令:

TableEnvironment tableEnv = TableEnvironment.create(EnvironmentSettings.newInstance().build());

// Set the execution mode to 'streaming' and enable dynamic table options.
tableEnv.getConfig().getConfiguration().setString("execution.runtime-mode", "streaming");
tableEnv.getConfig().getConfiguration().setBoolean("table.dynamic-table-options.enabled", true);

// Specify the historical snapshot ID from which to start reading.
String startSnapshotId = "3821550127947089987";  // Replace with your actual snapshot ID.

// Execute a SELECT statement with the streaming option and a monitor interval of 1 second.
// Start reading incremental data from the specified snapshot ID.
TableResult result = tableEnv.executeSql(
    "SELECT * FROM sales_data /*+ OPTIONS('streaming'='true', 'monitor-interval'='1s', 'start-snapshot-id'='" 
    + startSnapshotId + "')*/");
result.print();

在 DataStream 上下文中,Iceberg 支持批量和流读取,以下示例将演示这两种读取方式。对于批量数据:

StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment();
TableLoader tableLoader = TableLoader.fromHadoopTable("s3://someBucket/retail/sales_data");

IcebergSource<RowData> source = IcebergSource.forRowData()
    .tableLoader(tableLoader)
    .assignerFactory(new SimpleSplitAssignerFactory())
    .build();

DataStream<RowData> batch = env.fromSource(
    source,
    WatermarkStrategy.noWatermarks(),
    "My Iceberg Source",
    TypeInformation.of(RowData.class));

// Print all records.
batch.print();

// Execute batch read job.
env.execute("Iceberg Batch Read");

对于流数据:

StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment();
TableLoader tableLoader = TableLoader.fromHadoopTable("s3://someBucket/retail/sales_data");

IcebergSource source = IcebergSource.forRowData()
    .tableLoader(tableLoader)
    .assignerFactory(new SimpleSplitAssignerFactory())
    .streaming(true)
    .streamingStartingStrategy(StreamingStartingStrategy.INCREMENTAL_FROM_LATEST_SNAPSHOT)
    .monitorInterval(Duration.ofSeconds(60))
    .build()

DataStream<RowData> stream = env.fromSource(
    source,
    WatermarkStrategy.noWatermarks(),
    "My Iceberg Source",
    TypeInformation.of(RowData.class));

// Print all records.
stream.print();

// Execute streaming read job.
env.execute("Iceberg Stream Read");

可以通过在 FlinkSource.Builder 的 branch 或 tag 方法中指定选项来通过 DataStream API 读取分支和标签。用户可以在配置 Flink IcebergSource 或通过 Flink SQL 提示时设置许多选项,从而对读取操作进行细粒度控制:

DataStream<RowData> batch = FlinkSource.forRowData()
    .env(env)
    .tableLoader(tableLoader)
    .branch("etl-branch")
    .streaming(false)
    .build();

Flink 用于流写入

使用 Apache Flink 的 DataStream API 和 Table API 进行流写入 Apache Iceberg,提供了对实时数据摄取和处理的无缝支持。Iceberg 支持批量和流写入,使其成为数据流管道的多功能和强大选择。

在 Flink 的 SQL API 中,Iceberg 支持 INSERT INTO 和 INSERT OVERWRITE 操作。在 Flink 流作业中,INSERT INTO 命令用于将新数据追加到 Iceberg 表中,使其能够轻松地将新记录添加到表中。INSERT OVERWRITE 可用于批量作业,将表中的数据替换为查询结果。Iceberg 确保覆盖是原子操作,使数据更新安全可靠。

DataStreams 允许直接使用 FlinkSink.forRowData() API 将 DataStream<RowData>DataStream<Row> 写入 Iceberg 表。例如:

StreamExecutionEnvironment env = ...;
DataStream<RowData> input = ...;
Configuration hadoopConf = new Configuration();
TableLoader tableLoader = TableLoader.fromHadoopTable("hdfs://nn:8020/warehouse/path", hadoopConf);

FlinkSink.forRowData(input)
    .tableLoader(tableLoader)
    .append();

env.execute("Retail Sales Data Example");

对于更高级的场景,Iceberg 还支持覆盖和更新数据。可以通过在 FlinkSink 构建器中设置覆盖标志来覆盖现有数据。基于主键的更新可以在表级属性或构建器中的写选项中启用。以下是两个示例:

// Overwriting data
FlinkSink.forRowData(input)
    .tableLoader(tableLoader)
    .overwrite(true)
    .append();

// Upserting data
FlinkSink.forRowData(input)
    .tableLoader(tableLoader)
    .upsert(true)
    .append();

除了直接的 DataStream 写入,Iceberg 还提供了使用自定义映射器(例如 AvroGenericRecordToRowDataMapper)将数据转换为所需格式后再写入的灵活性。以下是一个示例:

// Define the custom AvroGenericRecordToRowDataMapper
AvroGenericRecordToRowDataMapper mapper = new AvroGenericRecordToRowDataMapper();

// Create a generic DataStream<GenericRecord> from a source (e.g., Kafka)
DataStream<GenericRecord> genericDataStream = ... // your DataStream source

// Write the generic DataStream to the Iceberg table using the custom mapper
FlinkSink.builderFor(
        genericDataStream,
        mapper,
        // Optionally specify the TypeInfo for RowData if needed
        TypeInformation.of(RowData.class)
)
.table(tableName)
.tableLoader(tableLoader)
.build();

env.execute("Iceberg Generic DataStream Example");

使用 Apache Flink 将流写入 Apache Iceberg 提供了处理实时数据摄取和处理的强大且高效的方法,确保即使在高速度的流处理环境中也能保证数据一致性和原子性。提供的代码示例展示了如何利用 Flink 的 API 无缝地与 Iceberg 表进行交互,实现批量和流处理写入。

使用 Flink 将数据流入 Iceberg 的示例

在这个场景中,我们在 Hive 目录中有两个现有的表:retail_sales_data 和 sales_data_summary。retail_sales_data 表包含原始销售数据,包含 purchase_date、quantity 和 price 等详细信息。sales_data_summary 表是一个具有分区模式的 Iceberg 表,用于存储每月汇总的销售数据。我们的目标是每小时进行数据处理,并使用最新的每月汇总销售数据更新 sales_data_summary 表。sales_data_summary 表按 month 列进行分区。相关代码如下;要查看完整代码片段,请访问本书的 GitHub 存储库:

String createCatalogStatement = "CREATE CATALOG " + catalogName + " WITH (\n" +
        "'type'='" + catalogType + "',\n" +
        "'catalog-type'='hive',\n" +
        "'uri'='" + hiveCatalogUri + "',\n" +
        "'warehouse'='" + warehousePath + "',\n" +
        "'clients'='" + hiveClientConfig + "',\n" +
        "'property-version'='1'\n" +
        ")";

// Register Hive Catalog
tEnv.executeSql(createCatalogStatement);
tEnv.useCatalog(catalogName);

// Define the source and destination table names
String sourceTableName = "retail_sales_data";
String destinationTableName = "sales_data_summary";

// Calculate the current month's partition value (e.g., '2023-08' for August 2023)
String currentMonthPartition = getCurrentMonthPartition();

// SQL query to process data for the current month
String sqlQuery = "INSERT OVERWRITE " + destinationTableName + " PARTITION (month = '" + currentMonthPartition + "') " +
        "SELECT date, SUM(sales_amount) as total_sales " +
        "FROM " + sourceTableName + " " +
        "WHERE month = '" + currentMonthPartition + "' " +
        "GROUP BY date";

// Execute the SQL query
tEnv.executeSql(sqlQuery);

// Sleep for one hour before executing the job again
Thread.sleep(3600000);

这个作业旨在按顺序执行一系列任务。首先,它查询聚合状态以检索当月所有日期的当天销售数据。然后,它执行 INSERT OVERWRITE 命令来覆盖聚合表的数据。这个步骤很重要,因为它会用新获取的信息更新当前月份分区中的数据。完成数据插入后,作业进入休眠模式一小时。在这段时间内,不会进行任何操作。一小时休息结束后,任务将重新启动并重复相同的操作序列。

这个聚合表可以用于销售数据的商业智能(BI)仪表板,并将根据销售情况每小时更新一次数据。我们可以通过调整作业运行的频率来更新数据的新鲜度。如果作业通常需要 30 秒完成,我们可以将作业频率减少到 30 秒,以提高由聚合表驱动的仪表板的数据新鲜度(更频繁的作业可能运行更快,从而允许更短的间隔)。

使用 AWS 进行流处理

Amazon Web Services (AWS) 提供了一套实时数据流服务,使开发人员能够在大规模的实时应用程序和分析解决方案中收集、处理、分析和传递连续的数据流。AWS 的数据流服务是安全、高可用、持久和完全托管的,简化了开发人员构建实时应用程序的过程。

AWS 数据流生态系统由以下组件组成:

  • 数据源:数据由成千上万的设备或应用程序(如移动设备、Web 应用程序、应用程序日志、物联网传感器、智能设备和游戏应用程序)以高速度和高容量产生。
  • 流数据摄取:AWS 提供与超过 15 个 AWS 服务的简单集成,以持久和安全的方式捕获连续数据。
  • 流数据存储:AWS 提供如 Amazon Kinesis Data Streams、Amazon Kinesis Data Firehose 和 Amazon Managed Streaming for Apache Kafka (Amazon MSK) 等解决方案,满足您的存储需求,基于扩展、延迟和处理要求。
  • 流数据处理:AWS 提供一系列数据转换和传递的服务,从 Amazon Kinesis Data Firehose 到定制的实时应用程序和使用 Amazon Kinesis Data Analytics 和 AWS Lambda 进行的机器学习集成。
  • 数据目的地:AWS 将流数据传递到一系列完全集成的数据湖、数据仓库和分析服务中,以便进一步分析或长期存储。

以下是一些关键的 AWS 实时数据流服务:

  • Amazon Kinesis Data Streams:一个可扩展且持久的实时数据流服务,可以从成千上万个来源连续捕获每秒千兆字节的数据。
  • Amazon Kinesis Data Firehose:捕获、转换并将数据流加载到 AWS 数据存储中,使用现有 BI 工具进行近实时分析。
  • Amazon Kinesis Data Analytics:允许您使用 SQL 或 Java (基于 Apache Flink) 实时处理数据流,而无需学习新的编程语言或处理框架。
  • Amazon MSK:一个完全托管的服务,使构建和运行使用 Apache Kafka 处理流数据的应用程序变得容易。

AWS 数据流服务支持广泛的用例,包括实时数据移动、实时分析和事件流处理。这使用户能够在数据生成时分析数据,存储数据以供进一步分析,在整个组织中实现实时决策,捕获并实时响应多个应用程序中的事件,并通过 CDC(变更数据捕获)维护记录系统。

使用 AWS Glue Studio 和 Apache Iceberg 将数据摄取到 Iceberg 表中,为在数据湖中管理和处理数据提供了一种强大而高效的方法。AWS Glue 3.0 及更高版本支持 Apache Iceberg。这种集成允许用户在 Amazon Simple Storage Service (Amazon S3) 上对 Iceberg 表执行读写操作,并利用 AWS Glue 数据目录进行额外操作,包括插入、更新以及 Spark 读写。

使用 AWS Glue Studio 和 Apache Iceberg 的一个关键优势是同时支持 AWS Kinesis Stream 和 Kafka Stream 作为数据源。用户可以无缝地将这些流数据源的数据摄取到 Amazon S3 中的 Iceberg 表,以便进一步处理和分析。

要在 AWS Glue 中启用 Iceberg,您需要为 --datalake-formats 作业参数指定 iceberg 作为值。此外,必须设置特定的 Spark 配置以正确处理 Iceberg 表。这些配置包括将 spark.sql.extensions 设置为 org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions 并配置 glue_catalog 属性,例如 warehouse、catalog-impl 和 io-impl。有关这些设置的详细信息,请参阅第 5 章和第 8 章。

如果您使用 AWS Glue 3.0 和 Iceberg 0.13.1,则还需要设置使用 Amazon DynamoDB 锁管理器的额外配置以确保原子事务。AWS Glue 4.0 则默认使用乐观锁(意味着您无需手动配置锁)。

要使用 AWS Glue 不原生支持的 Iceberg 版本,可以使用 --extra-jars 作业参数指定您自己的 Iceberg JAR 文件。

启用 Iceberg 并设置配置后,您可以轻松地在 AWS Glue Studio 中从 DataFrame 创建和注册 Iceberg 表。例如,您可以从 DataFrame 创建 Iceberg 表并使用 SQL 查询将其注册到 AWS Glue 数据目录中。同样,您可以使用 Glue 数据目录从 Amazon S3 中的 Iceberg 表读取数据,并执行插入操作将数据添加到 Iceberg 表中。

以下是使用 Python 将 Iceberg 表写入 Amazon S3 并注册到 AWS Glue 数据目录的示例:

# 示例:从 DataFrame 创建 Iceberg 表并将表注册到 Glue 数据目录
dataFrame.createOrReplaceTempView("incoming_data")

# 插入数据到 Glue Catalog 中的现有 Iceberg 表
query = f"""
INSERT INTO glue.db.destination_table SELECT * FROM incoming_data
"""
spark.sql(query)

在此示例中,创建了 DataFrame dataFrame,并为其分配了一个临时视图。然后使用 SQL 查询语法创建 Iceberg 表,并将其注册到 AWS Glue 数据目录中。

总体而言,使用 AWS Glue Studio 和 Apache Iceberg 将数据摄取到 Iceberg 表中,为在数据湖中管理和分析数据提供了无缝且高效的方法。无论数据是来自 AWS Kinesis Stream 还是 Kafka Stream,用户都可以利用 Iceberg 和 AWS Glue 的强大功能来有效地处理和分析数据。

使用 Kafka Connect 进行流处理

Apache Kafka Connect 是 Apache Kafka 平台的一个开源组件,提供了一种可扩展且可靠的方式将数据移动进出 Kafka。成千上万的公司使用 Kafka Connect 将各种系统与 Apache Kafka 集成,以实现实时数据流处理。

以下是 Apache Kafka Connect 的一些关键功能:

  • 高吞吐量:Kafka Connect 可以有效地传输大量数据,设计与 Apache Kafka 无缝配合,确保系统之间的数据流处理效率。
  • 容错性:Kafka Connect 具有内置的故障管理功能,能自动处理故障,确保不同系统与 Apache Kafka 之间的数据流不中断。
  • 实时集成:通过 Kafka Connect,可以实现数据的实时集成,使系统保持同步和最新。
  • 持久性:Kafka Connect 与 Kafka 主题协同工作,每条记录都存储在磁盘上并复制以实现容错性,确保即使出现问题,数据仍然保持一致和完整。
  • 广泛的集成:Kafka Connect 支持大量的连接器生态系统,便于与各种系统、数据库和应用程序集成。无论是将数据从数据库拉入 Kafka 还是将数据从 Kafka 推送到云存储解决方案,都有相应的连接器。
  • 精确一次语义:与 Apache Kafka 一样,Kafka Connect 支持精确一次处理语义,确保每条记录只传递一次,避免数据重复或丢失。
  • 连接器框架:Kafka Connect 提供了一个框架用于构建和运行可重用的连接器,这些连接器可用于在 Apache Kafka 和其他系统之间传输数据,而无需对 Kafka 本身进行任何代码更改。

Apache Kafka Connect 是 Apache Kafka 生态系统中的一个强大集成工具,提供无缝、实时的集成能力。其广泛的功能使其成为企业在可扩展和可靠的方式下,将各种系统与 Apache Kafka 集成的首选。

Iceberg Kafka Sink

Kafka Sink 是 Apache Kafka 中的一个组件,允许从 Kafka 主题中消费数据并将其写入外部系统或数据库。它在数据集成和数据流动中起着至关重要的作用,实现从 Kafka 到各种数据存储、数据库或分析平台的数据无缝传输。

Apache Iceberg Sink Connector 是一个专门的 Kafka Sink 连接器,用于将数据从 Kafka 写入 Iceberg 表。它提供了多种功能和能力,使其成为数据集成和存储的强大工具。以下是一些关键功能:

  • 提交协调:集中管理的 Iceberg 提交确保数据写入的一致性和原子性。
  • 精确一次传递语义:精确一次传递语义确保即使在故障情况下,数据也只被处理和写入 Iceberg 表一次。
  • 多表分发:根据特定的路由条件将数据写入多个 Iceberg 表,实现灵活的数据分发。
  • 行变更:支持行变更,允许对 Iceberg 表中的现有行进行更新和删除操作。
  • 插入更新模式:插入更新模式允许高效地更新 Iceberg v2 表中的数据,需定义身份字段。

配置 Apache Iceberg Kafka Sink

连接器提供了多种配置选项,以自定义行为并确保数据传输的顺利进行。以下是一些重要的配置属性及其含义:

  • iceberg.tables:此属性允许您指定一个以逗号分隔的目标表列表,将 Kafka 数据写入 Iceberg 表。
  • iceberg.tables.routeField:对于多表分发,此属性定义用于根据值将记录路由到不同目标表的字段。
  • iceberg.tables.<table name>.routeRegex:在多表分发模式中,此属性用于指定 <table name> 的正则表达式模式,用于匹配 routeField 值到特定的目标表。
  • iceberg.tables.dynamic.enabled:设置为 true 可根据 routeField 中的值动态路由记录到不同的目标表。
  • iceberg.tables.cdcField:如果指定了此属性,则表示包含变更数据捕获(CDC)操作代码的字段,例如 I 表示插入,U 表示更新,D 表示删除。
  • iceberg.tables.upsertModeEnabled:当设置为 true 时,此属性启用插入更新模式,确保通过等值删除进行高效的数据更新。
  • iceberg.control.topic:此属性定义用于管理连接器偏移量和提交事务的控制主题名称。
  • iceberg.control.group.id:此属性指定用于在 sink 管理的消费者组中存储偏移量的消费者组 ID。
  • iceberg.control.commitIntervalMs:指定提交间隔(以毫秒为单位),决定连接器提交事务的频率。
  • iceberg.control.commitTimeoutMs:指定提交超时间隔(以毫秒为单位),如果提交时间超过此持续时间,则认为提交失败。
  • iceberg.control.commitThread:指定用于提交事务的线程数,默认为 CPU 核心数的两倍。
  • iceberg.catalog:用于连接 Iceberg 表存储的目录名称,默认值为 "iceberg"。
  • iceberg.catalog. *:用于传递额外的属性以初始化 Iceberg 目录。不同类型的目录(如 REST、Hive 和 Hadoop)可能需要特定的配置属性。
  • iceberg.kafka. *:用于传递额外的属性以初始化 Kafka 客户端,用于连接到控制主题。允许设置自定义的 Kafka 客户端设置。

设置 Kafka Connect 和 Apache Iceberg

要设置 Kafka Connect 和 Apache Iceberg,请确保您的系统上已安装并运行 Kafka Connect。您可以从 Apache Kafka 官方网站下载 Kafka,并按照文档中的安装说明进行安装。

  1. 构建 Apache Iceberg Sink Connector:您可以使用预构建的连接器 JAR,或从源代码自行构建(位于本书的 GitHub 仓库)。如果选择自行构建,请从 GitHub 克隆 Iceberg 仓库,并在 kafka-connect-iceberg 模块内运行以下命令:
./gradlew -xtest clean build
  1. 配置 worker.properties 文件:打开 Kafka Connect worker 属性文件(通常命名为 worker.properties),并添加与您的 Kafka 设置相关的必要 Kafka 客户端属性。例如,启用与 Kafka broker 的 SSL 通信,您可以添加以下属性:
# Worker properties (worker.properties)
bootstrap.servers=kafka-broker1:9092,kafka-broker2:9092
security.protocol=SSL
ssl.truststore.location=/path/to/truststore.jks
ssl.truststore.password=truststore_password
  1. 设置 Apache Iceberg Sink 属性:在同一个 worker.properties 文件中,添加 Apache Iceberg Sink 属性。例如:
# Apache Iceberg Sink properties
connector.class=io.tabular.iceberg.connect.IcebergSinkConnector
tasks.max=2
topics=events
iceberg.tables=default.events
iceberg.catalog.type=rest
iceberg.catalog.uri=https://localhost
iceberg.catalog.credential=<credential>
iceberg.catalog.warehouse=<warehouse name>

根据您的设置修改属性,确保设置正确的 iceberg.catalog.uri、iceberg.catalog.credential 和 iceberg.catalog.warehouse 值以连接到您的 Iceberg 目录。

  1. 启动 Kafka Connect 服务:指定 worker.properties 文件作为命令行参数:
bin/connect-distributed.sh config/worker.properties

或者,您可以在独立模式下运行 Kafka Connect:

bin/connect-standalone.sh config/worker.properties connector.properties

注意,connector.properties 文件应包含前面提到的 Apache Iceberg Sink Connector 属性。

  1. 监控 Kafka Connect:确保 Kafka Connect 正在运行,并自动启动 Apache Iceberg Sink Connector,开始将指定主题的数据写入 Iceberg 表。检查日志以确保连接器已成功启动并按预期处理数据。使用 Kafka Connect REST API 或 Kafka Connect UI 监控连接器的状态和性能。

现在,您已经设置了带有 Kafka 客户端配置的 Apache Iceberg Sink。连接器将负责从 Kafka 主题中写入数据到指定的 Iceberg 表,而 Kafka 客户端将根据配置的属性处理与 Kafka broker 的通信。请查阅 Kafka 和 Apache Iceberg 文档,了解更多关于可用 Kafka 客户端属性和 Iceberg Sink 连接器配置选项的详细信息。

结论

有许多工具可以用于将流式实时数据处理到 Apache Iceberg 表中并从中读取数据,这些工具包括 Apache Spark、Apache Flink、AWS Kinesis 和 Kafka Connect。本书的 GitHub 仓库包含一张图表,总结了本章涵盖的许多工具的功能。

在第十二章中,我们将讨论 Apache Iceberg 表的治理和安全性。