From ea6d26700f48087e2f4b10faf4e2295bd57b1655 Mon Sep 17 00:00:00 2001 From: Gezi-lzq Date: Mon, 18 Nov 2024 03:37:57 +0000 Subject: [PATCH] deploy: 64653e0172a791e079885f75ba6247fdf9a7a3d5 --- atom.xml | 22 +++++++++++++++++++--- index-full.html | 8 ++++---- index.html | 8 ++++---- service-worker.js | 2 +- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/atom.xml b/atom.xml index d055e03..c649da0 100644 --- a/atom.xml +++ b/atom.xml @@ -11,8 +11,24 @@        Gezi-lzq    -  2024-11-17T12:03:23+02:00      Data Lake Brief History    https://gezi-lzq.github.io/wiki/#Data%20Lake%20Brief%20History        2024-11-09T16:39:31+02:00    在最初,你会采用 Hadoop ,一个开源的分布式计算框架,以及其HDFS文件系统组件,在由廉价计算机组成的集群中 存储和处理大量结构化和非结构化的数据集。 但仅仅存储这些数据还不够,你还希望对其进行分析。 +  2024-11-18T03:22:25+02:00      Data Lake Brief History    https://gezi-lzq.github.io/wiki/#Data%20Lake%20Brief%20History        2024-11-09T16:39:31+02:00    在最初,你会采用 Hadoop ,一个开源的分布式计算框架,以及其HDFS文件系统组件,在由廉价计算机组成的集群中 存储和处理大量结构化和非结构化的数据集。 但仅仅存储这些数据还不够,你还希望对其进行分析。 -Hadoop 生态系统包含了 MapReduce,这是一个分析框架,用户可以用 Java 编写分析作业并在 Hadoop 集群上运行。编写 MapReduce 作业时代码冗长且复杂,而许多分...        Kafka Replace ZooKeeper    https://gezi-lzq.github.io/wiki/#Kafka%20Replace%20ZooKeeper        2024-11-11T02:01:10+02:00    在 KIP-500 中,提出了使用“Replace ZooKeeper with a Self-Managed Metadata Quorum”在这个KIP中,提出了希望去除对 ZooKeeper 的依赖。使用更稳健且可扩展的方式来管理元数据,支持更多分区。 +Hadoop 生态系统包含了 MapReduce,这是一个分析框架,用户可以用 Java 编写分析作业并在 Hadoop 集群上运行。编写 MapReduce 作业时代码冗长且复杂,而许多分...        Iceberg Data Layer    https://gezi-lzq.github.io/wiki/#Iceberg%20Data%20Layer        2024-11-18T03:21:01+02:00    Apache Iceberg 表的数据层是存储表的实际数据的地方,主要由 datafile 组成,尽管 deletefile 也包括在内。 -我们如今常用的代码版本管理工具git,通过一个个记录变更的commit来管理整个项目的代码版本。如果希望恢复到某个时间点的代码版本,只需要...        MY FIRST FEED    https://gezi-lzq.github.io/wiki/#MY%20FIRST%20FEED        2024-11-09T07:40:05+02:00    新增加了 RSS 功能 ,仅 feed 标签的会被筛选出来,第一个feed,测试一下...   +数据层为用户查询提供所需的数据。虽然在某些情况下,metadata 层的结构也可以提供结构(例如, 获取某列的最大值),但通常由数据层来参与提供用户查询并提供结果。 + +数据层中的files构成了 Apache Iceberg 表树结构的叶子部分。 + +在实际使...        Iceberg Metadata Layer    https://gezi-lzq.github.io/wiki/#Iceberg%20Metadata%20Layer        2024-11-18T03:21:44+02:00    The metadata layer 是 Iceberg table 架构中不可或缺的一部分,包含了所有的 metadata files。它是一个 tree structure,追踪 datafiles 及其 metadata, 以及导致这些文件创建的operations。 + +这个 tree structure 由三种 file types 组成,所有这些都与 datafiles colocate...        Kafka Replace ZooKeeper    https://gezi-lzq.github.io/wiki/#Kafka%20Replace%20ZooKeeper        2024-11-11T02:01:10+02:00    在 KIP-500 中,提出了使用“Replace ZooKeeper with a Self-Managed Metadata Quorum”在这个KIP中,提出了希望去除对 ZooKeeper 的依赖。使用更稳健且可扩展的方式来管理元数据,支持更多分区。 + +我们如今常用的代码版本管理工具git,通过一个个记录变更的commit来管理整个项目的代码版本。如果希望恢复到某个时间点的代码版本,只需要...        MY FIRST FEED    https://gezi-lzq.github.io/wiki/#MY%20FIRST%20FEED        2024-11-09T07:40:05+02:00    新增加了 RSS 功能 ,仅 feed 标签的会被筛选出来,第一个feed,测试一下...        The Catalog    https://gezi-lzq.github.io/wiki/#The%20Catalog        2024-11-18T03:22:07+02:00    任何从一个 table 读取数据的人(更不用说几十、几百甚至几千个 table)需要知道首先去哪里;他们需要一个地方来找出在哪里读取/写入特定 table 的数据。任何想要与 table 交互的人第一步是找到当前 metadata pointer 的 metadata file 的位置。 + +这个用于查找当前 metadata pointer 位置的中心位置就是 Iceberg Catalog。 + +...        在 Apache Iceberg 中写 queries    https://gezi-lzq.github.io/wiki/#%E5%9C%A8%20Apache%20Iceberg%20%E4%B8%AD%E5%86%99%20queries        2024-11-18T03:22:25+02:00    Apache Iceberg 中的 write process 涉及一系列步骤,使 query engines 能够高效地插入和更新数据。当一个 write query 被启动时,它会被发送到 engine 进行解析。 + +然后 consult catalog 以确保数据的一致性和完整性,并根据定义的 partition strategies 写入数据。 + +数据文件和 metadata 文件会根据 ...   diff --git a/index-full.html b/index-full.html index 4d58f24..93ffa4a 100644 --- a/index-full.html +++ b/index-full.html @@ -436,8 +436,8 @@ {"title":"hierarchy_of_iceberg_components.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/hierarchy_of_iceberg_components.png"}, {"title":"iceberg","color":"#8cd0f2","created":"20241113161556427","creator":"Gezi-lzq","modified":"20241117120323676","modifier":"Gezi-lzq","tags":"笔记","text":"此 Tag 下大多内容的来源为《The Definitive Guide to Apache Iceberg》进行摘抄与翻译\n\nhttps://github.com/developer-advocacy-dremio/definitive-guide-to-apache-iceberg"}, {"title":"Iceberg Architecture","created":"20241103083308055","creator":"Gezi-lzq","modified":"20241113161842970","modifier":"Gezi-lzq","tags":"iceberg","text":"Apache Iceberg table 有三个不同的层次:\n\n* catalog layer\n* metadata layer\n* data layer\n\nApache Iceberg 通过一个元数据数来跟踪表分区、排序、schema 随时间的变化以及更多内容,这个元数据树可以让引擎以极短的时间来规划查询,这比使用传统的数据湖模式要快更多。\n\n[img width=700 [the_apache_iceberg_architecture.png]]\n\n这个 metadate tree 将表的元数据分解为四个组成部分:\n\n!!! ''Manifest file''\n\n\tA list of datafiles, 包含了每个 datafile 的 location/path 以及 关于这些 datafile 的 key metadate,这有助于创建更高效的执行计划。\n\n!!! ''Manifest list''\n\n\t定义表的单一快照的文件作为 manifest file 的列表,以及这些 manifest file 的统计信息,这些信息可以帮助创建更高效的执行计划。\n\n!!! ''Metadate file ''\n\n\t文件定义了表的 structure 包括其 schema,分区方案以及快照列表。\n\n!!! ''Catalog''\n\n\t跟踪表的位置(类似 Hive Metastore),但它包含的是表名到表最新的 metadate file location 的映射,而不是表名到一组目录的映射。\n"}, -{"title":"Iceberg Data Layer","created":"20241113161823167","creator":"Gezi-lzq","modified":"20241113165735113","modifier":"Gezi-lzq","tags":"iceberg","text":"Apache Iceberg 表的数据层是存储表的实际数据的地方,主要由 datafile 组成,尽管 deletefile 也包括在内。\n\n数据层为用户查询提供所需的数据。虽然在某些情况下,metadata 层的结构也可以提供结构(例如, 获取某列的最大值),但通常由数据层来参与提供用户查询并提供结果。\n\n数据层中的files构成了 Apache Iceberg 表树结构的叶子部分。\n\n在实际使用中,data layer 是由一个分布式文件系统(例如, HDFS)或者类似分布式文件系统的东西支持的,比如 object storage(例如 Amazon S3, Azure DLS, GCS)。这使得 data lakehouse 架构可以建立在这些可扩展且极低成本的 storage systems 上并从中受益。\n\n! Datafiles\n\n数据存储在 Datafile 中。Apache Iceberg 是 file format agnostic,目前支持 Apache Parquet, Apache ORC, 和 Apache Avro。\n\n“file format agnostic(不可知的文件结构)”这个设计很重要,原因如下:\n\n* 当前现状中,许多组织将数据存储为 multiple file formats, 这往往是业务需求或者在规模扩张中一些历史原因导致的。\n* 这样提供了很大的灵活性,可以根据实际 workload 来选择 file format。例如,Parquet 可能用于大规模的OLAP,而 Avro 可能用于低延迟的 streaming analytics tables。\n* 这样可以使组织的file format 是 future-proof(不会过时)。若新的file format 被开发出来,更适合一组 workload, 那么这个 file format 可以在 Apache Iceberg table 中使用。\n\n虽然 Apache Iceberg 对 file format 不敏感,但是在现实世界中,最常用的 file format 是 Apache Parquet。 Parquet 最常用的原因是它的 columnar structure 为 OLAP workloads 提供了比 row-based file formats 更大的 performance gains,并且它已经成为 industry 的事实标准,基本上每个 engine 和 tool 都支持 Parquet。它的 columnar structure 为 performance features 奠定了基础,比如单个文件可以多次 split 以增加 parallelism,每个 split points的 statistics,以及增加 compression,从而提供更低的 storage volume 和更高的 read throughput。\n\n根据下面的例子,你可以看到一个给定的 Parquet file 如何有一组 rows(图中的“Row group 0”),然后这些 rows 的值按给定的 column 一起存储(图中的“Column a”)。所有 rows 的值进一步被分解成这个 column 的值的子集,称为 pages(图中的“Page 0”)。每个这些层次可以被 engines 和 tools 独立读取,因此每个可以被一个给定的 engine 或 tool 并行读取。\n\n此外,Parquet 存储统计数据(例如,一个给定 row group 的一个给定 column 的最小值和最大值),使得 engines 和 tools 可以决定是否需要读取所有数据,或者是否可以修剪不符合查询的 row groups。\n\n[img[The_architecture_of_a_Parquet_file.png]]\n\n\n! Delete Files\nDelete files 记录了哪些数据集中的记录已被删除。由于将 data lake 存储视为不可变是最佳实践,因此你不能直接更新文件中的行。相反,你需要写一个新文件。这个新文件可以是旧文件的副本,其中反映了更改(称为 copy-on-write [COW]),或者它可以是一个仅包含更改的新文件,然后读取数据的引擎将其合并(称为 merge-on-read [MOR])。Delete files 支持 MOR 策略来执行 Iceberg tables 的 updates 和 deletes。\n\n请注意,delete files 只适用于 MOR tables,delete files 仅在 Iceberg v2 format 中支持。\n\n[img[显示在运行 DELETE 之前和之后的 MOR 表的 diagram|MOR_table_and_after_a_Delete_is_run_on_it.png]]\n\n有两种方法可以 identify 需要从 logical dataset 中 remove 的特定 row,当一个 engine 读取 dataset 时:可以通过 row 在 dataset 中的 准确位置 来 identify,或者通过 row 的一个或多个 field 的 values 来 identify。\n\n因此,有两种类型的 delete files。前者称为 positional delete files,后者称为 equality delete files。\n\n!! Positional delete files\n\n位置删除文件表示哪些行已经被逻辑删除,因此引擎在使用表时会从其表示中删除它们,通过识别行所在表中的确切位置来实现。这是通过指定包含该行的特定文件的 file path 和该文件中的 row number 来完成的。\n\n[img[positional_delete_files.png]]\n\n!! Equality delete files\n\nEquality delete files 表示哪些 rows 已经被逻辑删除,因此 engine 在使用 table 时需要从其表示中移除这些 rows,通过识别 row 的一个或多个字段的值来实现。当每个 row 在 table 中有一个 unique identifier(即 primary key)时,这种操作效果最好,因为单个字段的值可以唯一标识一个 row(例如,“delete the row where the row has a value for order_id of 1234”)。然而,也可以通过这种方法删除多个 rows(例如,“delete all rows where interaction_customer_id = 5678”)。\n\n图 2-5 展示了使用 equality delete file 删除 order_id 为 1234 的行。一个 engine 写入一个 delete file,内容是“delete 任何 order_id 等于 1234 的行”,任何读取它的 engine 都会遵循这一点。注意,与 positional delete files 相比,这里没有提到这些行在 table 中的具体位置。\n\n请注意,还有一种情况可能发生,即一个 equality delete file 通过 column values 删除了一条记录,然后在随后的 commit 中,一条记录被重新添加到与 delete file 的 column values 匹配的数据集中。在这种情况下,你不希望 query engines 在读取时从 logical table 中移除新添加的记录。\n\nApache Iceberg 对此的解决方案是 sequence numbers。例如,覆盖图 2-5 中的情况,manifest file 会记录图 2-5 左侧的数据文件都有一个 sequence number 为 1。然后,跟踪右侧 delete file 的 manifest file 会有一个 sequence number 为 2。然后,在随后的 insert 中创建的新行 order_id 为 1234 的数据文件会有一个 sequence number 为 3。所以,当一个 engine 读取表时,它知道要将 delete file 应用于所有 sequence number 小于 2(delete file 的 sequence number)的数据文件,但不应用于 sequence number 为 2 或更高的数据文件。通过这种方法,随着时间的推移,表的正确状态得以维护。\n\n[img[equality_delete_files.png]]\n"}, -{"title":"Iceberg Metadata Layer","created":"20241113165808215","creator":"Gezi-lzq","modified":"20241114172634351","modifier":"Gezi-lzq","tags":"iceberg","text":"The metadata layer 是 Iceberg table 架构中不可或缺的一部分,包含了所有的 metadata files。它是一个 tree structure,追踪 datafiles 及其 metadata, 以及导致这些文件创建的operations。\n\n这个 tree structure 由三种 file types 组成,所有这些都与 datafiles colocated(同等定位):\n\n* manifest files\n* manifest lists\n* metadata files\n\nThe metadata layer 对于高效管理 large datasets 以及启用核心功能如 time travel 和 schema evolution 至关重要。\n\n! Manifest files\n\nManifest files 记录了 data layer 中的文件(即 datafiles 和 delete files)以及每个文件的附加细节和统计信息,例如 datafile 列的最小值和最大值。\n\n使 Iceberg 能够解决 Hive table format 问题的主要区别是跟踪表格中的 data 到''文件级别''(而不是目录级别)。\n\n> 虽然 manifest 文件跟踪 datafiles 以及 delete files,但每个文件类型使用一套独立的 manifest 文件(即,一个 manifest 文件只包含 datafiles 或 delete files),尽管 manifest 文件 schemas 是相同的。\n\n每个 manifest file 会记录一部分 datafiles 的信息。它们包含关于 partition membership、record counts 以及 columns 的 lower 和 upper bounds 等信息,这些信息用于提高从这些 datafiles 读取数据时的效率和性能。\n\n虽然有些 statistics 也存储在 datafiles 本身,但一个 manifest file 会为多个 datafiles 存储这些 statistics,这意味着从一个 manifest file 中的 stats 进行修剪大大减少了需要打开许多 datafiles 的需求,这可以提高性能(即使只是打开许多 datafiles 的 footer,这也可能花费很长时间)。\n\n这些 statistics 在 write 操作期间由 engine/tool 为每个 manifest file 跟踪的一部分 datafiles 写入。\n\n因为这些 statistics 是由每个 engine 对它们负责写入的数据文件子集,进行较小批次的写入,所以写这些 statistics 比 Hive 表格式要轻量得多。\n\n在 Hive 表格式中,statistics 是作为一个长而昂贵的读取作业的一部分进行收集和存储的,需要 engine 读取整个分区或整个表,计算所有数据的 statistics,然后为该分区/表写入 stats。\n\n而在Icebreg当中,因为数据的 writer 已经处理了它正在写入的所有数据,因此对于这个 writer 来说,添加一个步骤来收集这些数据的 statistics 是更轻量的。\n\n在实践中,这意味着在使用 Hive 表格式时,会因为statistics 收集作业不会经常重新运行(如果有的话),engine 没有必要的信息来做出关于如何执行给定查询的明智决策,而导致查询性能较差。\n\n然而 Iceberg 表更容易获得最新和准确的 statistics,使 engine 在处理它们时能够做出更好的决策,从而提高作业性能。\n\n[[Manifest Files Example]]\n\n! Manifest Lists\n\nA manifest list 是在特定时间点的 Iceberg table 的 snapshot。对于该时间点的 table,它包含所有 manifest files 的列表,包括位置、所属的 partitions,以及它(该manifest file)追踪的数据文件的 partition columns 的 upper 和 lower bounds。\n\nA manifest list 包含一个 array of structs,每个 struct 记录一个 manifest file。\n\n[[Manifest Lists Example]]\n\n! Metadata Files\n\nManifest lists 由 metadata 文件追踪。另一个恰如其名的文件,metadata 文件存储关于 Iceberg 表在特定时间点的 metadata。这包括表的 schema 信息、partition 信息、snapshots 以及当前的 snapshot 是哪个。\n\n每次对 Iceberg table 进行更改时,都会创建一个新的 metadata file,并通过 catalog 原子性地注册为最新版本的 metadata file.\n\n[[Metadata Files Example]]\n\n! Puffin Files\n\n虽然 datafiles 和 delete files 的结构可以增强与 Iceberg table 中数据交互的 performance,有时候你需要更 advanced 的结构来增强特定类型查询的 performance。\n\n例如,假设你想知道在过去 30 天内有多少 unique 的人下了订单。数据文件中的 statistics,而不是 metadata 文件中的 statistics,涵盖了这种 use case。当然,你可以使用这些 statistics 来在一定程度上 improve performance(例如,只修剪过去 30 天的数据),但你仍然需要读取那 30 天内的每个 order 并在 engine 中进行 aggregations,这可能会花费很长时间,具体取决于数据的 size、分配给 engine 的 resources 以及字段的 cardinality 等因素。\n\n进入 puffin file format。一个 puffin file 存储关于表中数据的 statistics 和 indexes,这些可以提升更广泛查询的 performance,例如前面提到的例子,比 datafiles 和 metadata files 中存储的 statistics 更有效。\n\n[img width=500 [The_structure_of_a_puffin_file.png]]\n\n......"}, +{"title":"Iceberg Data Layer","created":"20241113161823167","creator":"Gezi-lzq","modified":"20241118032101160","modifier":"Gezi-lzq","tags":"iceberg feed","text":"Apache Iceberg 表的数据层是存储表的实际数据的地方,主要由 datafile 组成,尽管 deletefile 也包括在内。\n\n数据层为用户查询提供所需的数据。虽然在某些情况下,metadata 层的结构也可以提供结构(例如, 获取某列的最大值),但通常由数据层来参与提供用户查询并提供结果。\n\n数据层中的files构成了 Apache Iceberg 表树结构的叶子部分。\n\n在实际使用中,data layer 是由一个分布式文件系统(例如, HDFS)或者类似分布式文件系统的东西支持的,比如 object storage(例如 Amazon S3, Azure DLS, GCS)。这使得 data lakehouse 架构可以建立在这些可扩展且极低成本的 storage systems 上并从中受益。\n\n! Datafiles\n\n数据存储在 Datafile 中。Apache Iceberg 是 file format agnostic,目前支持 Apache Parquet, Apache ORC, 和 Apache Avro。\n\n“file format agnostic(不可知的文件结构)”这个设计很重要,原因如下:\n\n* 当前现状中,许多组织将数据存储为 multiple file formats, 这往往是业务需求或者在规模扩张中一些历史原因导致的。\n* 这样提供了很大的灵活性,可以根据实际 workload 来选择 file format。例如,Parquet 可能用于大规模的OLAP,而 Avro 可能用于低延迟的 streaming analytics tables。\n* 这样可以使组织的file format 是 future-proof(不会过时)。若新的file format 被开发出来,更适合一组 workload, 那么这个 file format 可以在 Apache Iceberg table 中使用。\n\n虽然 Apache Iceberg 对 file format 不敏感,但是在现实世界中,最常用的 file format 是 Apache Parquet。 Parquet 最常用的原因是它的 columnar structure 为 OLAP workloads 提供了比 row-based file formats 更大的 performance gains,并且它已经成为 industry 的事实标准,基本上每个 engine 和 tool 都支持 Parquet。它的 columnar structure 为 performance features 奠定了基础,比如单个文件可以多次 split 以增加 parallelism,每个 split points的 statistics,以及增加 compression,从而提供更低的 storage volume 和更高的 read throughput。\n\n根据下面的例子,你可以看到一个给定的 Parquet file 如何有一组 rows(图中的“Row group 0”),然后这些 rows 的值按给定的 column 一起存储(图中的“Column a”)。所有 rows 的值进一步被分解成这个 column 的值的子集,称为 pages(图中的“Page 0”)。每个这些层次可以被 engines 和 tools 独立读取,因此每个可以被一个给定的 engine 或 tool 并行读取。\n\n此外,Parquet 存储统计数据(例如,一个给定 row group 的一个给定 column 的最小值和最大值),使得 engines 和 tools 可以决定是否需要读取所有数据,或者是否可以修剪不符合查询的 row groups。\n\n[img[The_architecture_of_a_Parquet_file.png]]\n\n\n! Delete Files\nDelete files 记录了哪些数据集中的记录已被删除。由于将 data lake 存储视为不可变是最佳实践,因此你不能直接更新文件中的行。相反,你需要写一个新文件。这个新文件可以是旧文件的副本,其中反映了更改(称为 copy-on-write [COW]),或者它可以是一个仅包含更改的新文件,然后读取数据的引擎将其合并(称为 merge-on-read [MOR])。Delete files 支持 MOR 策略来执行 Iceberg tables 的 updates 和 deletes。\n\n请注意,delete files 只适用于 MOR tables,delete files 仅在 Iceberg v2 format 中支持。\n\n[img[显示在运行 DELETE 之前和之后的 MOR 表的 diagram|MOR_table_and_after_a_Delete_is_run_on_it.png]]\n\n有两种方法可以 identify 需要从 logical dataset 中 remove 的特定 row,当一个 engine 读取 dataset 时:可以通过 row 在 dataset 中的 准确位置 来 identify,或者通过 row 的一个或多个 field 的 values 来 identify。\n\n因此,有两种类型的 delete files。前者称为 positional delete files,后者称为 equality delete files。\n\n!! Positional delete files\n\n位置删除文件表示哪些行已经被逻辑删除,因此引擎在使用表时会从其表示中删除它们,通过识别行所在表中的确切位置来实现。这是通过指定包含该行的特定文件的 file path 和该文件中的 row number 来完成的。\n\n[img[positional_delete_files.png]]\n\n!! Equality delete files\n\nEquality delete files 表示哪些 rows 已经被逻辑删除,因此 engine 在使用 table 时需要从其表示中移除这些 rows,通过识别 row 的一个或多个字段的值来实现。当每个 row 在 table 中有一个 unique identifier(即 primary key)时,这种操作效果最好,因为单个字段的值可以唯一标识一个 row(例如,“delete the row where the row has a value for order_id of 1234”)。然而,也可以通过这种方法删除多个 rows(例如,“delete all rows where interaction_customer_id = 5678”)。\n\n图 2-5 展示了使用 equality delete file 删除 order_id 为 1234 的行。一个 engine 写入一个 delete file,内容是“delete 任何 order_id 等于 1234 的行”,任何读取它的 engine 都会遵循这一点。注意,与 positional delete files 相比,这里没有提到这些行在 table 中的具体位置。\n\n请注意,还有一种情况可能发生,即一个 equality delete file 通过 column values 删除了一条记录,然后在随后的 commit 中,一条记录被重新添加到与 delete file 的 column values 匹配的数据集中。在这种情况下,你不希望 query engines 在读取时从 logical table 中移除新添加的记录。\n\nApache Iceberg 对此的解决方案是 sequence numbers。例如,覆盖图 2-5 中的情况,manifest file 会记录图 2-5 左侧的数据文件都有一个 sequence number 为 1。然后,跟踪右侧 delete file 的 manifest file 会有一个 sequence number 为 2。然后,在随后的 insert 中创建的新行 order_id 为 1234 的数据文件会有一个 sequence number 为 3。所以,当一个 engine 读取表时,它知道要将 delete file 应用于所有 sequence number 小于 2(delete file 的 sequence number)的数据文件,但不应用于 sequence number 为 2 或更高的数据文件。通过这种方法,随着时间的推移,表的正确状态得以维护。\n\n[img[equality_delete_files.png]]\n"}, +{"title":"Iceberg Metadata Layer","created":"20241113165808215","creator":"Gezi-lzq","modified":"20241118032144988","modifier":"Gezi-lzq","tags":"iceberg feed","text":"The metadata layer 是 Iceberg table 架构中不可或缺的一部分,包含了所有的 metadata files。它是一个 tree structure,追踪 datafiles 及其 metadata, 以及导致这些文件创建的operations。\n\n这个 tree structure 由三种 file types 组成,所有这些都与 datafiles colocated(同等定位):\n\n* manifest files\n* manifest lists\n* metadata files\n\nThe metadata layer 对于高效管理 large datasets 以及启用核心功能如 time travel 和 schema evolution 至关重要。\n\n! Manifest files\n\nManifest files 记录了 data layer 中的文件(即 datafiles 和 delete files)以及每个文件的附加细节和统计信息,例如 datafile 列的最小值和最大值。\n\n使 Iceberg 能够解决 Hive table format 问题的主要区别是跟踪表格中的 data 到''文件级别''(而不是目录级别)。\n\n> 虽然 manifest 文件跟踪 datafiles 以及 delete files,但每个文件类型使用一套独立的 manifest 文件(即,一个 manifest 文件只包含 datafiles 或 delete files),尽管 manifest 文件 schemas 是相同的。\n\n每个 manifest file 会记录一部分 datafiles 的信息。它们包含关于 partition membership、record counts 以及 columns 的 lower 和 upper bounds 等信息,这些信息用于提高从这些 datafiles 读取数据时的效率和性能。\n\n虽然有些 statistics 也存储在 datafiles 本身,但一个 manifest file 会为多个 datafiles 存储这些 statistics,这意味着从一个 manifest file 中的 stats 进行修剪大大减少了需要打开许多 datafiles 的需求,这可以提高性能(即使只是打开许多 datafiles 的 footer,这也可能花费很长时间)。\n\n这些 statistics 在 write 操作期间由 engine/tool 为每个 manifest file 跟踪的一部分 datafiles 写入。\n\n因为这些 statistics 是由每个 engine 对它们负责写入的数据文件子集,进行较小批次的写入,所以写这些 statistics 比 Hive 表格式要轻量得多。\n\n在 Hive 表格式中,statistics 是作为一个长而昂贵的读取作业的一部分进行收集和存储的,需要 engine 读取整个分区或整个表,计算所有数据的 statistics,然后为该分区/表写入 stats。\n\n而在Icebreg当中,因为数据的 writer 已经处理了它正在写入的所有数据,因此对于这个 writer 来说,添加一个步骤来收集这些数据的 statistics 是更轻量的。\n\n在实践中,这意味着在使用 Hive 表格式时,会因为statistics 收集作业不会经常重新运行(如果有的话),engine 没有必要的信息来做出关于如何执行给定查询的明智决策,而导致查询性能较差。\n\n然而 Iceberg 表更容易获得最新和准确的 statistics,使 engine 在处理它们时能够做出更好的决策,从而提高作业性能。\n\n[[Manifest Files Example]]\n\n! Manifest Lists\n\nA manifest list 是在特定时间点的 Iceberg table 的 snapshot。对于该时间点的 table,它包含所有 manifest files 的列表,包括位置、所属的 partitions,以及它(该manifest file)追踪的数据文件的 partition columns 的 upper 和 lower bounds。\n\nA manifest list 包含一个 array of structs,每个 struct 记录一个 manifest file。\n\n[[Manifest Lists Example]]\n\n! Metadata Files\n\nManifest lists 由 metadata 文件追踪。另一个恰如其名的文件,metadata 文件存储关于 Iceberg 表在特定时间点的 metadata。这包括表的 schema 信息、partition 信息、snapshots 以及当前的 snapshot 是哪个。\n\n每次对 Iceberg table 进行更改时,都会创建一个新的 metadata file,并通过 catalog 原子性地注册为最新版本的 metadata file.\n\n[[Metadata Files Example]]\n\n! Puffin Files\n\n虽然 datafiles 和 delete files 的结构可以增强与 Iceberg table 中数据交互的 performance,有时候你需要更 advanced 的结构来增强特定类型查询的 performance。\n\n例如,假设你想知道在过去 30 天内有多少 unique 的人下了订单。数据文件中的 statistics,而不是 metadata 文件中的 statistics,涵盖了这种 use case。当然,你可以使用这些 statistics 来在一定程度上 improve performance(例如,只修剪过去 30 天的数据),但你仍然需要读取那 30 天内的每个 order 并在 engine 中进行 aggregations,这可能会花费很长时间,具体取决于数据的 size、分配给 engine 的 resources 以及字段的 cardinality 等因素。\n\n进入 puffin file format。一个 puffin file 存储关于表中数据的 statistics 和 indexes,这些可以提升更广泛查询的 performance,例如前面提到的例子,比 datafiles 和 metadata files 中存储的 statistics 更有效。\n\n[img width=500 [The_structure_of_a_puffin_file.png]]\n\n......"}, {"title":"Iceberg ParquetWriter","created":"20241115032625360","creator":"Gezi-lzq","modified":"20241115101457107","modifier":"Gezi-lzq","tags":"iceberg","text":"* ParquetWriter 的 可见性较低,无法直接继承修改\n* ParquetWriter 实现的 Append 接口, 在每次 append 的过程中会进行 checkSize, 若触发条件则进行 fluse\n* checkSize 的 条件 是:\n** 第一次判断是当前record大于10条,且缓冲区大小是否接近目标RowGroup大小,若不接近,则估算还需要多少个record达到targetRowGroupSize,当达到估算值当一半进行flush。(估算值需要在[MinRowCountForPageSizeCheck, MaxRowCountForPageSizeCheck]范围内)\n** 完成一次flush后,会重新计算record数量,根据上次RowGroup的record大小的一半(且需要在[MinRowCountForPageSizeCheck, MaxRowCountForPageSizeCheck]范围内)作为下次flush的条件。\n** 若上一个 RowGroup 的 record size 的二分之一也在这个范围,则以此为 nextCheckRecordCount\n** 判断缓冲区大小是否接近目标行组大小,如果缓冲区大小 (bufferedSize) 大于目标行组大小 (targetRowGroupSize) 减去两倍平均记录大小 (2 * avgRecordSize),则需要刷新当前行组。这里使用 2 * avgRecordSize 是为了在缓冲区接近满时(但还没有完全满)进行刷新,以避免记录过多导致超出缓冲区限制。\n\n* 如何进行flush\n** 初始化 ParquetFileWriter\n** 在Parquet文件中开始一个新的Block的写入流程\n** 将所有 Column 的数据从内存写入到底层存储系统,在Column Chunk中按Page管理数据,并将Page写入存储\n** 设置块级元数据(如记录数和序号),添加块到块集合,更新列索引、偏移索引和布隆过滤器集合。在块结束后重置相应的数据结构,确保下一个块写入的干净起点。\n** 再次调用 flush(),并迭代并关闭所有 ColumnWriterBase 实例\n** 启动准备一个新的 RowGroup\n\n\n"}, {"title":"Iceberg TaskWriter Interface","created":"20241113130852994","creator":"Gezi-lzq","modified":"20241113160923251","modifier":"Gezi-lzq","tags":"iceberg","text":"TaskWriter 有大量实现类,通用实现类有如下:\n\n!! BaseTaskWriter\n\n* 定位 :这是一个基础的抽象类,通常不会直接实例化。它提供了一些共同功能和属性,是其他具体实现类的基础。\n* 职责 :包含公共的处理逻辑,比如资源管理(打开和关闭),错误处理等。它是不可变对象,通常在子类中进行扩展。\n\n!!! 2. UnpartitionedWriter\n\n* 定位 :这是一个继承自BaseTaskWriter的具体实现,用于没有分区的写操作。\n* 职责 :直接进行写入操作,将数据写入到一个单一的目标而无需考虑分区。它适用于没有分区的场景。\n\n!!! 3. PartitionedWriter\n\n* 定位 :这是一个继承自BaseTaskWriter的具体实现,用于按分区进行写操作。\n* 职责 :适用于按分区写的场景,每个分区Key对应一个实际写操作的对象。在写操作时,会在每次切换分区时关闭当前的写操作对象,创建一个新的。\n\n!!! 4. PartitionedFanoutWriter\n\n* 定位 :同样是继承自BaseTaskWriter的具体实现,也是用于按分区进行写操作。\n* 职责 :每个分区Key也对应一个实际写操作对象,但在分区切换时,不会关闭已有的写对象,而是通过一个缓存机制(PartitionKey -> RollingFileWriter)来管理和复用这些对象。\n\n"}, {"title":"iceberg_component's_hierarchy_after_executing_INSERT.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/iceberg_component%27s_hierarchy_after_executing_INSERT.png"}, @@ -629,7 +629,7 @@ {"title":"TCP协议理解","created":"20230316030559955","creator":"Gezi","modified":"20230316031506291","modifier":"Gezi","tags":"计算机网络","type":"text/vnd.tiddlywiki","text":"TCP协议是面向''字节流''的协议。TCP中的“流”(stream)指的是流入到进程或从进程流出的字节序列。\n\n''面向字节流的含义''是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但是,TCP把应用程序交付下来的数据仅仅看成是一串无结构的字节流,TCP并不知道所传送的字节流的含义。对于应用程序来说,它看到的数据之间没有边界,也无法得知一条报文从哪里开始,到哪里结束,每条报文有多少字节。"}, {"title":"Technical_components_of_a_data_lake.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/Technical_components_of_a_data_lake.png"}, {"title":"The actor model in 10 minutes","created":"20240720055206128","creator":"Gezi-lzq","modified":"20240720055213082","modifier":"Gezi-lzq","tags":"","text":"https://www.brianstorti.com/the-actor-model/"}, -{"title":"The Catalog","created":"20241114172706984","creator":"Gezi-lzq","modified":"20241114173656125","modifier":"Gezi-lzq","tags":"iceberg","text":"任何从一个 table 读取数据的人(更不用说几十、几百甚至几千个 table)需要知道首先去哪里;他们需要一个地方来找出在哪里读取/写入特定 table 的数据。任何想要与 table 交互的人第一步是找到当前 metadata pointer 的 metadata file 的位置。\n\n这个用于查找当前 metadata pointer 位置的中心位置就是 Iceberg Catalog。\n\nIceberg catalog 的 primary requirement 是它必须支持 atomic operations 来更新 current metadata pointer。Iceberg必须要求有 atomic operations 的支持,只有这样所有的 readers 和 writers 在某个时间点都能看到 table 的相同状态。\n\n在 catalog 中,每个 table 都有一个 reference 或 pointer 指向该 table 当前的 metadata 文件。\n\n[img width=600 [catalog_and_metadata.png]]\n\n因为对 Iceberg catalog 的唯一要求是它需要存储当前的 metadata pointer 并提供 atomic guarantees,所以有很多不同的 backends 可以作为 Iceberg catalog。\n\n不过,不同的 catalog 存储当前的 metadata pointer 的方式不同。以下是一些例子:\n\n* 使用 Amazon S3 作为 catalog 时,在表的 metadata 文件夹中有一个名为 version-hint.text 的文件,其内容是当前 metadata 文件的 version number。请注意,任何时候你使用分布式文件系统(或类似的东西)来存储当前 metadata pointer 时,实际使用的 catalog 被称为 hadoop catalog。\n* 使用 Hive Metastore 作为 catalog 时,Hive Metastore 中的 table entry 有一个称为 location 的 table property,用于存储当前 metadata file 的位置。\n* 使用 Nessie 作为 catalog,Nessie 中的 table entry 有一个 table property 叫做 metadataLocation,存储当前表的 metadata file 的位置。"}, +{"title":"The Catalog","created":"20241114172706984","creator":"Gezi-lzq","modified":"20241118032207053","modifier":"Gezi-lzq","tags":"iceberg feed","text":"任何从一个 table 读取数据的人(更不用说几十、几百甚至几千个 table)需要知道首先去哪里;他们需要一个地方来找出在哪里读取/写入特定 table 的数据。任何想要与 table 交互的人第一步是找到当前 metadata pointer 的 metadata file 的位置。\n\n这个用于查找当前 metadata pointer 位置的中心位置就是 Iceberg Catalog。\n\nIceberg catalog 的 primary requirement 是它必须支持 atomic operations 来更新 current metadata pointer。Iceberg必须要求有 atomic operations 的支持,只有这样所有的 readers 和 writers 在某个时间点都能看到 table 的相同状态。\n\n在 catalog 中,每个 table 都有一个 reference 或 pointer 指向该 table 当前的 metadata 文件。\n\n[img width=600 [catalog_and_metadata.png]]\n\n因为对 Iceberg catalog 的唯一要求是它需要存储当前的 metadata pointer 并提供 atomic guarantees,所以有很多不同的 backends 可以作为 Iceberg catalog。\n\n不过,不同的 catalog 存储当前的 metadata pointer 的方式不同。以下是一些例子:\n\n* 使用 Amazon S3 作为 catalog 时,在表的 metadata 文件夹中有一个名为 version-hint.text 的文件,其内容是当前 metadata 文件的 version number。请注意,任何时候你使用分布式文件系统(或类似的东西)来存储当前 metadata pointer 时,实际使用的 catalog 被称为 hadoop catalog。\n* 使用 Hive Metastore 作为 catalog 时,Hive Metastore 中的 table entry 有一个称为 location 的 table property,用于存储当前 metadata file 的位置。\n* 使用 Nessie 作为 catalog,Nessie 中的 table entry 有一个 table property 叫做 metadataLocation,存储当前表的 metadata file 的位置。"}, {"title":"the_apache_iceberg_architecture.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/the_apache_iceberg_architecture.png"}, {"title":"The_arch_of_an_iceberg_table.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/The_arch_of_an_iceberg_table.png"}, {"title":"The_architecture_of_a_Parquet_file.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/The_architecture_of_a_Parquet_file.png"}, @@ -741,7 +741,7 @@ {"title":"四总线结构","created":"20221027164214245","creator":"lzq","modified":"20221027164346295","modifier":"lzq","tags":"总线","type":"text/vnd.tiddlywiki","text":"[img[四总线结构.png]]\n\n将高速设备与低速设备进行分而治之\n"}, {"title":"四总线结构.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/%E5%9B%9B%E6%80%BB%E7%BA%BF%E7%BB%93%E6%9E%84.png"}, {"title":"固定窗口-计数器算法","created":"20230328065426323","creator":"Gezi","modified":"20230328065747544","modifier":"Gezi","tags":"分布式限流系统","type":"text/vnd.tiddlywiki","text":"确定时间窗口大小,例如每秒、每分钟或每小时等。 \n\n维护一个计数器,初始化为0。 \n\n每当收到一个请求时,将计数器加1。 \n\n每次check时检查是否在当前时间窗口内,若进入了下一个窗口则清零。 \n\ncheck时检查当前时间窗口内的请求数是否超过了设定的阈值,如果超过则返回为false。\n\n\n```go\n// NewLimiter 初始化简单计数器限流\nfunc NewLimiter(interval int64, maxCount int) *CounterLimiter {\n return &CounterLimiter{\n Interval: interval,\n LastTime: time.Now(),\n MaxCount: maxCount,\n lock: new(sync.Mutex),\n ReqCount: 0,\n }\n}\n\n // counterLimit 简单计数器限流实现\nfunc (r *CounterLimiter) Check() bool {\n r.lock.Lock()\n defer r.lock.Unlock()\n now := time.Now()\n if now.Unix()-r.LastTime.Unix() > r.Interval {\n r.LastTime = now\n r.ReqCount = 0\n }\n if r.ReqCount \u003C r.MaxCount {\n r.ReqCount += 1\n return true\n }\n return false\n}\n```\n\n!! 不足\n\nQPS的定义是1s内的请求数量,但是1s中的请求也可能是不能均匀的,这就导致在1s中切换边界丢失限流状态,如果在此时流量突增会导致服务雪崩。\n\n[img[边界丢失限流状态.png]]\n"}, -{"title":"在 Apache Iceberg 中写 queries","created":"20241116162300973","creator":"Gezi-lzq","modified":"20241117105840127","modifier":"Gezi-lzq","tags":"iceberg","text":"Apache Iceberg 中的 write process 涉及一系列步骤,使 query engines 能够高效地插入和更新数据。当一个 write query 被启动时,它会被发送到 engine 进行解析。\n\n然后 consult catalog 以确保数据的一致性和完整性,并根据定义的 partition strategies 写入数据。\n\n数据文件和 metadata 文件会根据 query 写入。最后,catalog file 会被更新以反映最新的 metadata,从而支持后续的 read operations 去 access 最新版本的数据。\n\n[img[Iceberg_write_process.png]]\n\n{{Create The Table}}\n\n{{INSERT the query}}"}, +{"title":"在 Apache Iceberg 中写 queries","created":"20241116162300973","creator":"Gezi-lzq","modified":"20241118032225755","modifier":"Gezi-lzq","tags":"iceberg feed","text":"Apache Iceberg 中的 write process 涉及一系列步骤,使 query engines 能够高效地插入和更新数据。当一个 write query 被启动时,它会被发送到 engine 进行解析。\n\n然后 consult catalog 以确保数据的一致性和完整性,并根据定义的 partition strategies 写入数据。\n\n数据文件和 metadata 文件会根据 query 写入。最后,catalog file 会被更新以反映最新的 metadata,从而支持后续的 read operations 去 access 最新版本的数据。\n\n[img[Iceberg_write_process.png]]\n\n{{Create The Table}}\n\n{{INSERT the query}}"}, {"title":"在线实时消息的可靠投递","created":"20230318084804690","creator":"Gezi","modified":"20230318101817951","modifier":"Gezi","tags":"IM系统-plato","type":"text/vnd.tiddlywiki","text":"!IM消息送达保证机制实现(一)\n!! 保证在线实时消息的可靠投递\n\n消息的可靠性,即消息的不丢失和不重复\n\n> [[消息“可靠性”和“一致性”问题]]\n\n!! 报文类型\n\n报文分为三种:\n\n1. 请求报文 (request,简称为R):客户端主动发送给服务器的报文\n\n2. 应答报文 (acknowledge,简称为A):客户端主动发送给服务器的报文\n\n3. 通知报文 (notify,简称为N):服务器主动发送给客户端的报文\n\n!!! 普通消息投递流程\n\n[img[普通消息投递过程.png]]\n\n1. client-A向im-server发送一个消息请求包,即msg:R\n\n2. im-server在成功处理后,回复client-A一个消息响应包,即msg:A\n\n3. 如果此时client-B在线,则im-server主动向client-B发送一个消息通知包,即msg:N(当然,如果client-B不在线,则消息会存储离线)\n\n!!!! 消息投递流程出现的隐患\n\n当发送方client-A收到msg:A后,是否可以说明client-B接收到了消息呢?\n\n答案是不能的,只能说明im-server成功接收到了消息。\n\n在若干场景下,可能出现msg:N包丢失,且发送方client-A完全不知道,例如:\n\n* 服务器崩溃,msg:N包未发出\n* 网络抖动,msg:N包被网络设备丢弃\n* client-B崩溃,msg:N包未接收\n\n因此对于im这样一个三方通信的系统,如何''保证消息的可靠送达''?\n\n!!! 应用层确认+im消息可靠投递的六个报文\n\nTCP只能保证端到端之间传输的可靠,对于im这种三方之间的传输,若要保证消息的可靠投递,则需要通过应用层去实现这个特性。\n\n因此,为了保障IM通信中A端-Server-B端的三方通信的可靠性,可以采用在应用层加入确认机制的办法来实现。\n\n1. client-B 向 im-server 发送一个ack请求包,即ack:R\n\n2. im-sercer在处理成功后,回复client-B一个ack响应包,即ack-A\n\n3. im-server主动向client-A发送一个ack通知包,即ack-N\n\n[img[应用层确认-消息投递过程.png]]\n\n所以,当client-A收到了ack:N报文,才算真正确认client-B收到了消息。\n\n> 一条消息的发送,分别包含(上)(下)两个半场,即msg的R/A/N三个报文,ack的R/A/N三个报文。一个应用层即时通讯消息的可靠投递,共涉及6个报文,这就是im系统中消息投递的最核心技术(如果某个im系统不包含这6个报文,不要谈什么消息的可靠性)。\n\n!!!! 此方案仍然存在的隐患\n\n1. msg:R,msg:A 报文可能丢失:\n此时直接提示“发送失败”即可,问题不大;\n\n2. msg:N,ack:R,ack:A,ack:N这四个报文都可能丢失:\n\n此时client-A都收不到期待的ack:N报文,即client-A不能确认client-B是否收到“你好”。\n\n!!! 消息的超时与重传\n\nclient-A发出了msg:R,收到了msg:A之后,在一个期待的时间内,如果没有收到ack:N,client-A会尝试将msg:R重发。\n\n可能client-A同时发出了很多消息,故client-A''需要在本地维护一个等待ack队列,并配合timer超时机制'',来记录哪些消息没有收到ack:N,以定时重发。\n\n一旦收到了ack:N,说明client-B收到了“你好”消息,对应的消息将从“等待ack队列”中移除。\n\n!!!! 消息的重传存在什么问题\n\n若ack:N 报文丢失:说明client-B之前已经收到了“你好”报文(只是client-A不知道而已),超时与重传机制将导致client-B收到重复的消息。\n\n!!! 消息的去重\n\n由发送方client-A生成一个消息去重的msgid,保存在“等待ack队列”里,同一条消息使用相同的msgid来重传,供client-B去重,而不影响用户体验。\n\n"}, {"title":"垃圾回收(gc)","color":"#6f597d","created":"20230126100247338","creator":"Gezi","modified":"20230318173236790","modifier":"Gezi","tags":"笔记","type":"text/vnd.tiddlywiki","text":"\u003C\u003Clist-links \"[tag[垃圾回收(gc)]sort[title]]\">>\n"}, {"title":"基础SQL语句","created":"20220519152627612","creator":"lzq","modified":"20220519152854287","modifier":"lzq","tags":"软件构造-应用数据库 SQL语句","type":"text/vnd.tiddlywiki"}, diff --git a/index.html b/index.html index 7d54a75..b2cffdd 100644 --- a/index.html +++ b/index.html @@ -436,8 +436,8 @@ {"title":"hierarchy_of_iceberg_components.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/hierarchy_of_iceberg_components.png"}, {"title":"iceberg","color":"#8cd0f2","created":"20241113161556427","creator":"Gezi-lzq","modified":"20241117120323676","modifier":"Gezi-lzq","tags":"笔记","text":"此 Tag 下大多内容的来源为《The Definitive Guide to Apache Iceberg》进行摘抄与翻译\n\nhttps://github.com/developer-advocacy-dremio/definitive-guide-to-apache-iceberg"}, {"title":"Iceberg Architecture","created":"20241103083308055","creator":"Gezi-lzq","modified":"20241113161842970","modifier":"Gezi-lzq","tags":"iceberg","text":"Apache Iceberg table 有三个不同的层次:\n\n* catalog layer\n* metadata layer\n* data layer\n\nApache Iceberg 通过一个元数据数来跟踪表分区、排序、schema 随时间的变化以及更多内容,这个元数据树可以让引擎以极短的时间来规划查询,这比使用传统的数据湖模式要快更多。\n\n[img width=700 [the_apache_iceberg_architecture.png]]\n\n这个 metadate tree 将表的元数据分解为四个组成部分:\n\n!!! ''Manifest file''\n\n\tA list of datafiles, 包含了每个 datafile 的 location/path 以及 关于这些 datafile 的 key metadate,这有助于创建更高效的执行计划。\n\n!!! ''Manifest list''\n\n\t定义表的单一快照的文件作为 manifest file 的列表,以及这些 manifest file 的统计信息,这些信息可以帮助创建更高效的执行计划。\n\n!!! ''Metadate file ''\n\n\t文件定义了表的 structure 包括其 schema,分区方案以及快照列表。\n\n!!! ''Catalog''\n\n\t跟踪表的位置(类似 Hive Metastore),但它包含的是表名到表最新的 metadate file location 的映射,而不是表名到一组目录的映射。\n"}, -{"title":"Iceberg Data Layer","created":"20241113161823167","creator":"Gezi-lzq","modified":"20241113165735113","modifier":"Gezi-lzq","tags":"iceberg","text":"Apache Iceberg 表的数据层是存储表的实际数据的地方,主要由 datafile 组成,尽管 deletefile 也包括在内。\n\n数据层为用户查询提供所需的数据。虽然在某些情况下,metadata 层的结构也可以提供结构(例如, 获取某列的最大值),但通常由数据层来参与提供用户查询并提供结果。\n\n数据层中的files构成了 Apache Iceberg 表树结构的叶子部分。\n\n在实际使用中,data layer 是由一个分布式文件系统(例如, HDFS)或者类似分布式文件系统的东西支持的,比如 object storage(例如 Amazon S3, Azure DLS, GCS)。这使得 data lakehouse 架构可以建立在这些可扩展且极低成本的 storage systems 上并从中受益。\n\n! Datafiles\n\n数据存储在 Datafile 中。Apache Iceberg 是 file format agnostic,目前支持 Apache Parquet, Apache ORC, 和 Apache Avro。\n\n“file format agnostic(不可知的文件结构)”这个设计很重要,原因如下:\n\n* 当前现状中,许多组织将数据存储为 multiple file formats, 这往往是业务需求或者在规模扩张中一些历史原因导致的。\n* 这样提供了很大的灵活性,可以根据实际 workload 来选择 file format。例如,Parquet 可能用于大规模的OLAP,而 Avro 可能用于低延迟的 streaming analytics tables。\n* 这样可以使组织的file format 是 future-proof(不会过时)。若新的file format 被开发出来,更适合一组 workload, 那么这个 file format 可以在 Apache Iceberg table 中使用。\n\n虽然 Apache Iceberg 对 file format 不敏感,但是在现实世界中,最常用的 file format 是 Apache Parquet。 Parquet 最常用的原因是它的 columnar structure 为 OLAP workloads 提供了比 row-based file formats 更大的 performance gains,并且它已经成为 industry 的事实标准,基本上每个 engine 和 tool 都支持 Parquet。它的 columnar structure 为 performance features 奠定了基础,比如单个文件可以多次 split 以增加 parallelism,每个 split points的 statistics,以及增加 compression,从而提供更低的 storage volume 和更高的 read throughput。\n\n根据下面的例子,你可以看到一个给定的 Parquet file 如何有一组 rows(图中的“Row group 0”),然后这些 rows 的值按给定的 column 一起存储(图中的“Column a”)。所有 rows 的值进一步被分解成这个 column 的值的子集,称为 pages(图中的“Page 0”)。每个这些层次可以被 engines 和 tools 独立读取,因此每个可以被一个给定的 engine 或 tool 并行读取。\n\n此外,Parquet 存储统计数据(例如,一个给定 row group 的一个给定 column 的最小值和最大值),使得 engines 和 tools 可以决定是否需要读取所有数据,或者是否可以修剪不符合查询的 row groups。\n\n[img[The_architecture_of_a_Parquet_file.png]]\n\n\n! Delete Files\nDelete files 记录了哪些数据集中的记录已被删除。由于将 data lake 存储视为不可变是最佳实践,因此你不能直接更新文件中的行。相反,你需要写一个新文件。这个新文件可以是旧文件的副本,其中反映了更改(称为 copy-on-write [COW]),或者它可以是一个仅包含更改的新文件,然后读取数据的引擎将其合并(称为 merge-on-read [MOR])。Delete files 支持 MOR 策略来执行 Iceberg tables 的 updates 和 deletes。\n\n请注意,delete files 只适用于 MOR tables,delete files 仅在 Iceberg v2 format 中支持。\n\n[img[显示在运行 DELETE 之前和之后的 MOR 表的 diagram|MOR_table_and_after_a_Delete_is_run_on_it.png]]\n\n有两种方法可以 identify 需要从 logical dataset 中 remove 的特定 row,当一个 engine 读取 dataset 时:可以通过 row 在 dataset 中的 准确位置 来 identify,或者通过 row 的一个或多个 field 的 values 来 identify。\n\n因此,有两种类型的 delete files。前者称为 positional delete files,后者称为 equality delete files。\n\n!! Positional delete files\n\n位置删除文件表示哪些行已经被逻辑删除,因此引擎在使用表时会从其表示中删除它们,通过识别行所在表中的确切位置来实现。这是通过指定包含该行的特定文件的 file path 和该文件中的 row number 来完成的。\n\n[img[positional_delete_files.png]]\n\n!! Equality delete files\n\nEquality delete files 表示哪些 rows 已经被逻辑删除,因此 engine 在使用 table 时需要从其表示中移除这些 rows,通过识别 row 的一个或多个字段的值来实现。当每个 row 在 table 中有一个 unique identifier(即 primary key)时,这种操作效果最好,因为单个字段的值可以唯一标识一个 row(例如,“delete the row where the row has a value for order_id of 1234”)。然而,也可以通过这种方法删除多个 rows(例如,“delete all rows where interaction_customer_id = 5678”)。\n\n图 2-5 展示了使用 equality delete file 删除 order_id 为 1234 的行。一个 engine 写入一个 delete file,内容是“delete 任何 order_id 等于 1234 的行”,任何读取它的 engine 都会遵循这一点。注意,与 positional delete files 相比,这里没有提到这些行在 table 中的具体位置。\n\n请注意,还有一种情况可能发生,即一个 equality delete file 通过 column values 删除了一条记录,然后在随后的 commit 中,一条记录被重新添加到与 delete file 的 column values 匹配的数据集中。在这种情况下,你不希望 query engines 在读取时从 logical table 中移除新添加的记录。\n\nApache Iceberg 对此的解决方案是 sequence numbers。例如,覆盖图 2-5 中的情况,manifest file 会记录图 2-5 左侧的数据文件都有一个 sequence number 为 1。然后,跟踪右侧 delete file 的 manifest file 会有一个 sequence number 为 2。然后,在随后的 insert 中创建的新行 order_id 为 1234 的数据文件会有一个 sequence number 为 3。所以,当一个 engine 读取表时,它知道要将 delete file 应用于所有 sequence number 小于 2(delete file 的 sequence number)的数据文件,但不应用于 sequence number 为 2 或更高的数据文件。通过这种方法,随着时间的推移,表的正确状态得以维护。\n\n[img[equality_delete_files.png]]\n"}, -{"title":"Iceberg Metadata Layer","created":"20241113165808215","creator":"Gezi-lzq","modified":"20241114172634351","modifier":"Gezi-lzq","tags":"iceberg","text":"The metadata layer 是 Iceberg table 架构中不可或缺的一部分,包含了所有的 metadata files。它是一个 tree structure,追踪 datafiles 及其 metadata, 以及导致这些文件创建的operations。\n\n这个 tree structure 由三种 file types 组成,所有这些都与 datafiles colocated(同等定位):\n\n* manifest files\n* manifest lists\n* metadata files\n\nThe metadata layer 对于高效管理 large datasets 以及启用核心功能如 time travel 和 schema evolution 至关重要。\n\n! Manifest files\n\nManifest files 记录了 data layer 中的文件(即 datafiles 和 delete files)以及每个文件的附加细节和统计信息,例如 datafile 列的最小值和最大值。\n\n使 Iceberg 能够解决 Hive table format 问题的主要区别是跟踪表格中的 data 到''文件级别''(而不是目录级别)。\n\n> 虽然 manifest 文件跟踪 datafiles 以及 delete files,但每个文件类型使用一套独立的 manifest 文件(即,一个 manifest 文件只包含 datafiles 或 delete files),尽管 manifest 文件 schemas 是相同的。\n\n每个 manifest file 会记录一部分 datafiles 的信息。它们包含关于 partition membership、record counts 以及 columns 的 lower 和 upper bounds 等信息,这些信息用于提高从这些 datafiles 读取数据时的效率和性能。\n\n虽然有些 statistics 也存储在 datafiles 本身,但一个 manifest file 会为多个 datafiles 存储这些 statistics,这意味着从一个 manifest file 中的 stats 进行修剪大大减少了需要打开许多 datafiles 的需求,这可以提高性能(即使只是打开许多 datafiles 的 footer,这也可能花费很长时间)。\n\n这些 statistics 在 write 操作期间由 engine/tool 为每个 manifest file 跟踪的一部分 datafiles 写入。\n\n因为这些 statistics 是由每个 engine 对它们负责写入的数据文件子集,进行较小批次的写入,所以写这些 statistics 比 Hive 表格式要轻量得多。\n\n在 Hive 表格式中,statistics 是作为一个长而昂贵的读取作业的一部分进行收集和存储的,需要 engine 读取整个分区或整个表,计算所有数据的 statistics,然后为该分区/表写入 stats。\n\n而在Icebreg当中,因为数据的 writer 已经处理了它正在写入的所有数据,因此对于这个 writer 来说,添加一个步骤来收集这些数据的 statistics 是更轻量的。\n\n在实践中,这意味着在使用 Hive 表格式时,会因为statistics 收集作业不会经常重新运行(如果有的话),engine 没有必要的信息来做出关于如何执行给定查询的明智决策,而导致查询性能较差。\n\n然而 Iceberg 表更容易获得最新和准确的 statistics,使 engine 在处理它们时能够做出更好的决策,从而提高作业性能。\n\n[[Manifest Files Example]]\n\n! Manifest Lists\n\nA manifest list 是在特定时间点的 Iceberg table 的 snapshot。对于该时间点的 table,它包含所有 manifest files 的列表,包括位置、所属的 partitions,以及它(该manifest file)追踪的数据文件的 partition columns 的 upper 和 lower bounds。\n\nA manifest list 包含一个 array of structs,每个 struct 记录一个 manifest file。\n\n[[Manifest Lists Example]]\n\n! Metadata Files\n\nManifest lists 由 metadata 文件追踪。另一个恰如其名的文件,metadata 文件存储关于 Iceberg 表在特定时间点的 metadata。这包括表的 schema 信息、partition 信息、snapshots 以及当前的 snapshot 是哪个。\n\n每次对 Iceberg table 进行更改时,都会创建一个新的 metadata file,并通过 catalog 原子性地注册为最新版本的 metadata file.\n\n[[Metadata Files Example]]\n\n! Puffin Files\n\n虽然 datafiles 和 delete files 的结构可以增强与 Iceberg table 中数据交互的 performance,有时候你需要更 advanced 的结构来增强特定类型查询的 performance。\n\n例如,假设你想知道在过去 30 天内有多少 unique 的人下了订单。数据文件中的 statistics,而不是 metadata 文件中的 statistics,涵盖了这种 use case。当然,你可以使用这些 statistics 来在一定程度上 improve performance(例如,只修剪过去 30 天的数据),但你仍然需要读取那 30 天内的每个 order 并在 engine 中进行 aggregations,这可能会花费很长时间,具体取决于数据的 size、分配给 engine 的 resources 以及字段的 cardinality 等因素。\n\n进入 puffin file format。一个 puffin file 存储关于表中数据的 statistics 和 indexes,这些可以提升更广泛查询的 performance,例如前面提到的例子,比 datafiles 和 metadata files 中存储的 statistics 更有效。\n\n[img width=500 [The_structure_of_a_puffin_file.png]]\n\n......"}, +{"title":"Iceberg Data Layer","created":"20241113161823167","creator":"Gezi-lzq","modified":"20241118032101160","modifier":"Gezi-lzq","tags":"iceberg feed","text":"Apache Iceberg 表的数据层是存储表的实际数据的地方,主要由 datafile 组成,尽管 deletefile 也包括在内。\n\n数据层为用户查询提供所需的数据。虽然在某些情况下,metadata 层的结构也可以提供结构(例如, 获取某列的最大值),但通常由数据层来参与提供用户查询并提供结果。\n\n数据层中的files构成了 Apache Iceberg 表树结构的叶子部分。\n\n在实际使用中,data layer 是由一个分布式文件系统(例如, HDFS)或者类似分布式文件系统的东西支持的,比如 object storage(例如 Amazon S3, Azure DLS, GCS)。这使得 data lakehouse 架构可以建立在这些可扩展且极低成本的 storage systems 上并从中受益。\n\n! Datafiles\n\n数据存储在 Datafile 中。Apache Iceberg 是 file format agnostic,目前支持 Apache Parquet, Apache ORC, 和 Apache Avro。\n\n“file format agnostic(不可知的文件结构)”这个设计很重要,原因如下:\n\n* 当前现状中,许多组织将数据存储为 multiple file formats, 这往往是业务需求或者在规模扩张中一些历史原因导致的。\n* 这样提供了很大的灵活性,可以根据实际 workload 来选择 file format。例如,Parquet 可能用于大规模的OLAP,而 Avro 可能用于低延迟的 streaming analytics tables。\n* 这样可以使组织的file format 是 future-proof(不会过时)。若新的file format 被开发出来,更适合一组 workload, 那么这个 file format 可以在 Apache Iceberg table 中使用。\n\n虽然 Apache Iceberg 对 file format 不敏感,但是在现实世界中,最常用的 file format 是 Apache Parquet。 Parquet 最常用的原因是它的 columnar structure 为 OLAP workloads 提供了比 row-based file formats 更大的 performance gains,并且它已经成为 industry 的事实标准,基本上每个 engine 和 tool 都支持 Parquet。它的 columnar structure 为 performance features 奠定了基础,比如单个文件可以多次 split 以增加 parallelism,每个 split points的 statistics,以及增加 compression,从而提供更低的 storage volume 和更高的 read throughput。\n\n根据下面的例子,你可以看到一个给定的 Parquet file 如何有一组 rows(图中的“Row group 0”),然后这些 rows 的值按给定的 column 一起存储(图中的“Column a”)。所有 rows 的值进一步被分解成这个 column 的值的子集,称为 pages(图中的“Page 0”)。每个这些层次可以被 engines 和 tools 独立读取,因此每个可以被一个给定的 engine 或 tool 并行读取。\n\n此外,Parquet 存储统计数据(例如,一个给定 row group 的一个给定 column 的最小值和最大值),使得 engines 和 tools 可以决定是否需要读取所有数据,或者是否可以修剪不符合查询的 row groups。\n\n[img[The_architecture_of_a_Parquet_file.png]]\n\n\n! Delete Files\nDelete files 记录了哪些数据集中的记录已被删除。由于将 data lake 存储视为不可变是最佳实践,因此你不能直接更新文件中的行。相反,你需要写一个新文件。这个新文件可以是旧文件的副本,其中反映了更改(称为 copy-on-write [COW]),或者它可以是一个仅包含更改的新文件,然后读取数据的引擎将其合并(称为 merge-on-read [MOR])。Delete files 支持 MOR 策略来执行 Iceberg tables 的 updates 和 deletes。\n\n请注意,delete files 只适用于 MOR tables,delete files 仅在 Iceberg v2 format 中支持。\n\n[img[显示在运行 DELETE 之前和之后的 MOR 表的 diagram|MOR_table_and_after_a_Delete_is_run_on_it.png]]\n\n有两种方法可以 identify 需要从 logical dataset 中 remove 的特定 row,当一个 engine 读取 dataset 时:可以通过 row 在 dataset 中的 准确位置 来 identify,或者通过 row 的一个或多个 field 的 values 来 identify。\n\n因此,有两种类型的 delete files。前者称为 positional delete files,后者称为 equality delete files。\n\n!! Positional delete files\n\n位置删除文件表示哪些行已经被逻辑删除,因此引擎在使用表时会从其表示中删除它们,通过识别行所在表中的确切位置来实现。这是通过指定包含该行的特定文件的 file path 和该文件中的 row number 来完成的。\n\n[img[positional_delete_files.png]]\n\n!! Equality delete files\n\nEquality delete files 表示哪些 rows 已经被逻辑删除,因此 engine 在使用 table 时需要从其表示中移除这些 rows,通过识别 row 的一个或多个字段的值来实现。当每个 row 在 table 中有一个 unique identifier(即 primary key)时,这种操作效果最好,因为单个字段的值可以唯一标识一个 row(例如,“delete the row where the row has a value for order_id of 1234”)。然而,也可以通过这种方法删除多个 rows(例如,“delete all rows where interaction_customer_id = 5678”)。\n\n图 2-5 展示了使用 equality delete file 删除 order_id 为 1234 的行。一个 engine 写入一个 delete file,内容是“delete 任何 order_id 等于 1234 的行”,任何读取它的 engine 都会遵循这一点。注意,与 positional delete files 相比,这里没有提到这些行在 table 中的具体位置。\n\n请注意,还有一种情况可能发生,即一个 equality delete file 通过 column values 删除了一条记录,然后在随后的 commit 中,一条记录被重新添加到与 delete file 的 column values 匹配的数据集中。在这种情况下,你不希望 query engines 在读取时从 logical table 中移除新添加的记录。\n\nApache Iceberg 对此的解决方案是 sequence numbers。例如,覆盖图 2-5 中的情况,manifest file 会记录图 2-5 左侧的数据文件都有一个 sequence number 为 1。然后,跟踪右侧 delete file 的 manifest file 会有一个 sequence number 为 2。然后,在随后的 insert 中创建的新行 order_id 为 1234 的数据文件会有一个 sequence number 为 3。所以,当一个 engine 读取表时,它知道要将 delete file 应用于所有 sequence number 小于 2(delete file 的 sequence number)的数据文件,但不应用于 sequence number 为 2 或更高的数据文件。通过这种方法,随着时间的推移,表的正确状态得以维护。\n\n[img[equality_delete_files.png]]\n"}, +{"title":"Iceberg Metadata Layer","created":"20241113165808215","creator":"Gezi-lzq","modified":"20241118032144988","modifier":"Gezi-lzq","tags":"iceberg feed","text":"The metadata layer 是 Iceberg table 架构中不可或缺的一部分,包含了所有的 metadata files。它是一个 tree structure,追踪 datafiles 及其 metadata, 以及导致这些文件创建的operations。\n\n这个 tree structure 由三种 file types 组成,所有这些都与 datafiles colocated(同等定位):\n\n* manifest files\n* manifest lists\n* metadata files\n\nThe metadata layer 对于高效管理 large datasets 以及启用核心功能如 time travel 和 schema evolution 至关重要。\n\n! Manifest files\n\nManifest files 记录了 data layer 中的文件(即 datafiles 和 delete files)以及每个文件的附加细节和统计信息,例如 datafile 列的最小值和最大值。\n\n使 Iceberg 能够解决 Hive table format 问题的主要区别是跟踪表格中的 data 到''文件级别''(而不是目录级别)。\n\n> 虽然 manifest 文件跟踪 datafiles 以及 delete files,但每个文件类型使用一套独立的 manifest 文件(即,一个 manifest 文件只包含 datafiles 或 delete files),尽管 manifest 文件 schemas 是相同的。\n\n每个 manifest file 会记录一部分 datafiles 的信息。它们包含关于 partition membership、record counts 以及 columns 的 lower 和 upper bounds 等信息,这些信息用于提高从这些 datafiles 读取数据时的效率和性能。\n\n虽然有些 statistics 也存储在 datafiles 本身,但一个 manifest file 会为多个 datafiles 存储这些 statistics,这意味着从一个 manifest file 中的 stats 进行修剪大大减少了需要打开许多 datafiles 的需求,这可以提高性能(即使只是打开许多 datafiles 的 footer,这也可能花费很长时间)。\n\n这些 statistics 在 write 操作期间由 engine/tool 为每个 manifest file 跟踪的一部分 datafiles 写入。\n\n因为这些 statistics 是由每个 engine 对它们负责写入的数据文件子集,进行较小批次的写入,所以写这些 statistics 比 Hive 表格式要轻量得多。\n\n在 Hive 表格式中,statistics 是作为一个长而昂贵的读取作业的一部分进行收集和存储的,需要 engine 读取整个分区或整个表,计算所有数据的 statistics,然后为该分区/表写入 stats。\n\n而在Icebreg当中,因为数据的 writer 已经处理了它正在写入的所有数据,因此对于这个 writer 来说,添加一个步骤来收集这些数据的 statistics 是更轻量的。\n\n在实践中,这意味着在使用 Hive 表格式时,会因为statistics 收集作业不会经常重新运行(如果有的话),engine 没有必要的信息来做出关于如何执行给定查询的明智决策,而导致查询性能较差。\n\n然而 Iceberg 表更容易获得最新和准确的 statistics,使 engine 在处理它们时能够做出更好的决策,从而提高作业性能。\n\n[[Manifest Files Example]]\n\n! Manifest Lists\n\nA manifest list 是在特定时间点的 Iceberg table 的 snapshot。对于该时间点的 table,它包含所有 manifest files 的列表,包括位置、所属的 partitions,以及它(该manifest file)追踪的数据文件的 partition columns 的 upper 和 lower bounds。\n\nA manifest list 包含一个 array of structs,每个 struct 记录一个 manifest file。\n\n[[Manifest Lists Example]]\n\n! Metadata Files\n\nManifest lists 由 metadata 文件追踪。另一个恰如其名的文件,metadata 文件存储关于 Iceberg 表在特定时间点的 metadata。这包括表的 schema 信息、partition 信息、snapshots 以及当前的 snapshot 是哪个。\n\n每次对 Iceberg table 进行更改时,都会创建一个新的 metadata file,并通过 catalog 原子性地注册为最新版本的 metadata file.\n\n[[Metadata Files Example]]\n\n! Puffin Files\n\n虽然 datafiles 和 delete files 的结构可以增强与 Iceberg table 中数据交互的 performance,有时候你需要更 advanced 的结构来增强特定类型查询的 performance。\n\n例如,假设你想知道在过去 30 天内有多少 unique 的人下了订单。数据文件中的 statistics,而不是 metadata 文件中的 statistics,涵盖了这种 use case。当然,你可以使用这些 statistics 来在一定程度上 improve performance(例如,只修剪过去 30 天的数据),但你仍然需要读取那 30 天内的每个 order 并在 engine 中进行 aggregations,这可能会花费很长时间,具体取决于数据的 size、分配给 engine 的 resources 以及字段的 cardinality 等因素。\n\n进入 puffin file format。一个 puffin file 存储关于表中数据的 statistics 和 indexes,这些可以提升更广泛查询的 performance,例如前面提到的例子,比 datafiles 和 metadata files 中存储的 statistics 更有效。\n\n[img width=500 [The_structure_of_a_puffin_file.png]]\n\n......"}, {"title":"Iceberg ParquetWriter","created":"20241115032625360","creator":"Gezi-lzq","modified":"20241115101457107","modifier":"Gezi-lzq","tags":"iceberg","text":"* ParquetWriter 的 可见性较低,无法直接继承修改\n* ParquetWriter 实现的 Append 接口, 在每次 append 的过程中会进行 checkSize, 若触发条件则进行 fluse\n* checkSize 的 条件 是:\n** 第一次判断是当前record大于10条,且缓冲区大小是否接近目标RowGroup大小,若不接近,则估算还需要多少个record达到targetRowGroupSize,当达到估算值当一半进行flush。(估算值需要在[MinRowCountForPageSizeCheck, MaxRowCountForPageSizeCheck]范围内)\n** 完成一次flush后,会重新计算record数量,根据上次RowGroup的record大小的一半(且需要在[MinRowCountForPageSizeCheck, MaxRowCountForPageSizeCheck]范围内)作为下次flush的条件。\n** 若上一个 RowGroup 的 record size 的二分之一也在这个范围,则以此为 nextCheckRecordCount\n** 判断缓冲区大小是否接近目标行组大小,如果缓冲区大小 (bufferedSize) 大于目标行组大小 (targetRowGroupSize) 减去两倍平均记录大小 (2 * avgRecordSize),则需要刷新当前行组。这里使用 2 * avgRecordSize 是为了在缓冲区接近满时(但还没有完全满)进行刷新,以避免记录过多导致超出缓冲区限制。\n\n* 如何进行flush\n** 初始化 ParquetFileWriter\n** 在Parquet文件中开始一个新的Block的写入流程\n** 将所有 Column 的数据从内存写入到底层存储系统,在Column Chunk中按Page管理数据,并将Page写入存储\n** 设置块级元数据(如记录数和序号),添加块到块集合,更新列索引、偏移索引和布隆过滤器集合。在块结束后重置相应的数据结构,确保下一个块写入的干净起点。\n** 再次调用 flush(),并迭代并关闭所有 ColumnWriterBase 实例\n** 启动准备一个新的 RowGroup\n\n\n"}, {"title":"Iceberg TaskWriter Interface","created":"20241113130852994","creator":"Gezi-lzq","modified":"20241113160923251","modifier":"Gezi-lzq","tags":"iceberg","text":"TaskWriter 有大量实现类,通用实现类有如下:\n\n!! BaseTaskWriter\n\n* 定位 :这是一个基础的抽象类,通常不会直接实例化。它提供了一些共同功能和属性,是其他具体实现类的基础。\n* 职责 :包含公共的处理逻辑,比如资源管理(打开和关闭),错误处理等。它是不可变对象,通常在子类中进行扩展。\n\n!!! 2. UnpartitionedWriter\n\n* 定位 :这是一个继承自BaseTaskWriter的具体实现,用于没有分区的写操作。\n* 职责 :直接进行写入操作,将数据写入到一个单一的目标而无需考虑分区。它适用于没有分区的场景。\n\n!!! 3. PartitionedWriter\n\n* 定位 :这是一个继承自BaseTaskWriter的具体实现,用于按分区进行写操作。\n* 职责 :适用于按分区写的场景,每个分区Key对应一个实际写操作的对象。在写操作时,会在每次切换分区时关闭当前的写操作对象,创建一个新的。\n\n!!! 4. PartitionedFanoutWriter\n\n* 定位 :同样是继承自BaseTaskWriter的具体实现,也是用于按分区进行写操作。\n* 职责 :每个分区Key也对应一个实际写操作对象,但在分区切换时,不会关闭已有的写对象,而是通过一个缓存机制(PartitionKey -> RollingFileWriter)来管理和复用这些对象。\n\n"}, {"title":"iceberg_component's_hierarchy_after_executing_INSERT.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/iceberg_component%27s_hierarchy_after_executing_INSERT.png"}, @@ -629,7 +629,7 @@ {"title":"TCP协议理解","created":"20230316030559955","creator":"Gezi","modified":"20230316031506291","modifier":"Gezi","tags":"计算机网络","type":"text/vnd.tiddlywiki","text":"TCP协议是面向''字节流''的协议。TCP中的“流”(stream)指的是流入到进程或从进程流出的字节序列。\n\n''面向字节流的含义''是:虽然应用程序和TCP的交互是一次一个数据块(大小不等),但是,TCP把应用程序交付下来的数据仅仅看成是一串无结构的字节流,TCP并不知道所传送的字节流的含义。对于应用程序来说,它看到的数据之间没有边界,也无法得知一条报文从哪里开始,到哪里结束,每条报文有多少字节。"}, {"title":"Technical_components_of_a_data_lake.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/Technical_components_of_a_data_lake.png"}, {"title":"The actor model in 10 minutes","created":"20240720055206128","creator":"Gezi-lzq","modified":"20240720055213082","modifier":"Gezi-lzq","tags":"","text":"https://www.brianstorti.com/the-actor-model/"}, -{"title":"The Catalog","created":"20241114172706984","creator":"Gezi-lzq","modified":"20241114173656125","modifier":"Gezi-lzq","tags":"iceberg","text":"任何从一个 table 读取数据的人(更不用说几十、几百甚至几千个 table)需要知道首先去哪里;他们需要一个地方来找出在哪里读取/写入特定 table 的数据。任何想要与 table 交互的人第一步是找到当前 metadata pointer 的 metadata file 的位置。\n\n这个用于查找当前 metadata pointer 位置的中心位置就是 Iceberg Catalog。\n\nIceberg catalog 的 primary requirement 是它必须支持 atomic operations 来更新 current metadata pointer。Iceberg必须要求有 atomic operations 的支持,只有这样所有的 readers 和 writers 在某个时间点都能看到 table 的相同状态。\n\n在 catalog 中,每个 table 都有一个 reference 或 pointer 指向该 table 当前的 metadata 文件。\n\n[img width=600 [catalog_and_metadata.png]]\n\n因为对 Iceberg catalog 的唯一要求是它需要存储当前的 metadata pointer 并提供 atomic guarantees,所以有很多不同的 backends 可以作为 Iceberg catalog。\n\n不过,不同的 catalog 存储当前的 metadata pointer 的方式不同。以下是一些例子:\n\n* 使用 Amazon S3 作为 catalog 时,在表的 metadata 文件夹中有一个名为 version-hint.text 的文件,其内容是当前 metadata 文件的 version number。请注意,任何时候你使用分布式文件系统(或类似的东西)来存储当前 metadata pointer 时,实际使用的 catalog 被称为 hadoop catalog。\n* 使用 Hive Metastore 作为 catalog 时,Hive Metastore 中的 table entry 有一个称为 location 的 table property,用于存储当前 metadata file 的位置。\n* 使用 Nessie 作为 catalog,Nessie 中的 table entry 有一个 table property 叫做 metadataLocation,存储当前表的 metadata file 的位置。"}, +{"title":"The Catalog","created":"20241114172706984","creator":"Gezi-lzq","modified":"20241118032207053","modifier":"Gezi-lzq","tags":"iceberg feed","text":"任何从一个 table 读取数据的人(更不用说几十、几百甚至几千个 table)需要知道首先去哪里;他们需要一个地方来找出在哪里读取/写入特定 table 的数据。任何想要与 table 交互的人第一步是找到当前 metadata pointer 的 metadata file 的位置。\n\n这个用于查找当前 metadata pointer 位置的中心位置就是 Iceberg Catalog。\n\nIceberg catalog 的 primary requirement 是它必须支持 atomic operations 来更新 current metadata pointer。Iceberg必须要求有 atomic operations 的支持,只有这样所有的 readers 和 writers 在某个时间点都能看到 table 的相同状态。\n\n在 catalog 中,每个 table 都有一个 reference 或 pointer 指向该 table 当前的 metadata 文件。\n\n[img width=600 [catalog_and_metadata.png]]\n\n因为对 Iceberg catalog 的唯一要求是它需要存储当前的 metadata pointer 并提供 atomic guarantees,所以有很多不同的 backends 可以作为 Iceberg catalog。\n\n不过,不同的 catalog 存储当前的 metadata pointer 的方式不同。以下是一些例子:\n\n* 使用 Amazon S3 作为 catalog 时,在表的 metadata 文件夹中有一个名为 version-hint.text 的文件,其内容是当前 metadata 文件的 version number。请注意,任何时候你使用分布式文件系统(或类似的东西)来存储当前 metadata pointer 时,实际使用的 catalog 被称为 hadoop catalog。\n* 使用 Hive Metastore 作为 catalog 时,Hive Metastore 中的 table entry 有一个称为 location 的 table property,用于存储当前 metadata file 的位置。\n* 使用 Nessie 作为 catalog,Nessie 中的 table entry 有一个 table property 叫做 metadataLocation,存储当前表的 metadata file 的位置。"}, {"title":"the_apache_iceberg_architecture.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/the_apache_iceberg_architecture.png"}, {"title":"The_arch_of_an_iceberg_table.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/The_arch_of_an_iceberg_table.png"}, {"title":"The_architecture_of_a_Parquet_file.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/The_architecture_of_a_Parquet_file.png"}, @@ -741,7 +741,7 @@ {"title":"四总线结构","created":"20221027164214245","creator":"lzq","modified":"20221027164346295","modifier":"lzq","tags":"总线","type":"text/vnd.tiddlywiki","text":"[img[四总线结构.png]]\n\n将高速设备与低速设备进行分而治之\n"}, {"title":"四总线结构.png","type":"image/png","_canonical_uri":"https://raw.githubusercontent.com/Gezi-lzq/wiki/main/tiddlers/%E5%9B%9B%E6%80%BB%E7%BA%BF%E7%BB%93%E6%9E%84.png"}, {"title":"固定窗口-计数器算法","created":"20230328065426323","creator":"Gezi","modified":"20230328065747544","modifier":"Gezi","tags":"分布式限流系统","type":"text/vnd.tiddlywiki","text":"确定时间窗口大小,例如每秒、每分钟或每小时等。 \n\n维护一个计数器,初始化为0。 \n\n每当收到一个请求时,将计数器加1。 \n\n每次check时检查是否在当前时间窗口内,若进入了下一个窗口则清零。 \n\ncheck时检查当前时间窗口内的请求数是否超过了设定的阈值,如果超过则返回为false。\n\n\n```go\n// NewLimiter 初始化简单计数器限流\nfunc NewLimiter(interval int64, maxCount int) *CounterLimiter {\n return &CounterLimiter{\n Interval: interval,\n LastTime: time.Now(),\n MaxCount: maxCount,\n lock: new(sync.Mutex),\n ReqCount: 0,\n }\n}\n\n // counterLimit 简单计数器限流实现\nfunc (r *CounterLimiter) Check() bool {\n r.lock.Lock()\n defer r.lock.Unlock()\n now := time.Now()\n if now.Unix()-r.LastTime.Unix() > r.Interval {\n r.LastTime = now\n r.ReqCount = 0\n }\n if r.ReqCount \u003C r.MaxCount {\n r.ReqCount += 1\n return true\n }\n return false\n}\n```\n\n!! 不足\n\nQPS的定义是1s内的请求数量,但是1s中的请求也可能是不能均匀的,这就导致在1s中切换边界丢失限流状态,如果在此时流量突增会导致服务雪崩。\n\n[img[边界丢失限流状态.png]]\n"}, -{"title":"在 Apache Iceberg 中写 queries","created":"20241116162300973","creator":"Gezi-lzq","modified":"20241117105840127","modifier":"Gezi-lzq","tags":"iceberg","text":"Apache Iceberg 中的 write process 涉及一系列步骤,使 query engines 能够高效地插入和更新数据。当一个 write query 被启动时,它会被发送到 engine 进行解析。\n\n然后 consult catalog 以确保数据的一致性和完整性,并根据定义的 partition strategies 写入数据。\n\n数据文件和 metadata 文件会根据 query 写入。最后,catalog file 会被更新以反映最新的 metadata,从而支持后续的 read operations 去 access 最新版本的数据。\n\n[img[Iceberg_write_process.png]]\n\n{{Create The Table}}\n\n{{INSERT the query}}"}, +{"title":"在 Apache Iceberg 中写 queries","created":"20241116162300973","creator":"Gezi-lzq","modified":"20241118032225755","modifier":"Gezi-lzq","tags":"iceberg feed","text":"Apache Iceberg 中的 write process 涉及一系列步骤,使 query engines 能够高效地插入和更新数据。当一个 write query 被启动时,它会被发送到 engine 进行解析。\n\n然后 consult catalog 以确保数据的一致性和完整性,并根据定义的 partition strategies 写入数据。\n\n数据文件和 metadata 文件会根据 query 写入。最后,catalog file 会被更新以反映最新的 metadata,从而支持后续的 read operations 去 access 最新版本的数据。\n\n[img[Iceberg_write_process.png]]\n\n{{Create The Table}}\n\n{{INSERT the query}}"}, {"title":"在线实时消息的可靠投递","created":"20230318084804690","creator":"Gezi","modified":"20230318101817951","modifier":"Gezi","tags":"IM系统-plato","type":"text/vnd.tiddlywiki","text":"!IM消息送达保证机制实现(一)\n!! 保证在线实时消息的可靠投递\n\n消息的可靠性,即消息的不丢失和不重复\n\n> [[消息“可靠性”和“一致性”问题]]\n\n!! 报文类型\n\n报文分为三种:\n\n1. 请求报文 (request,简称为R):客户端主动发送给服务器的报文\n\n2. 应答报文 (acknowledge,简称为A):客户端主动发送给服务器的报文\n\n3. 通知报文 (notify,简称为N):服务器主动发送给客户端的报文\n\n!!! 普通消息投递流程\n\n[img[普通消息投递过程.png]]\n\n1. client-A向im-server发送一个消息请求包,即msg:R\n\n2. im-server在成功处理后,回复client-A一个消息响应包,即msg:A\n\n3. 如果此时client-B在线,则im-server主动向client-B发送一个消息通知包,即msg:N(当然,如果client-B不在线,则消息会存储离线)\n\n!!!! 消息投递流程出现的隐患\n\n当发送方client-A收到msg:A后,是否可以说明client-B接收到了消息呢?\n\n答案是不能的,只能说明im-server成功接收到了消息。\n\n在若干场景下,可能出现msg:N包丢失,且发送方client-A完全不知道,例如:\n\n* 服务器崩溃,msg:N包未发出\n* 网络抖动,msg:N包被网络设备丢弃\n* client-B崩溃,msg:N包未接收\n\n因此对于im这样一个三方通信的系统,如何''保证消息的可靠送达''?\n\n!!! 应用层确认+im消息可靠投递的六个报文\n\nTCP只能保证端到端之间传输的可靠,对于im这种三方之间的传输,若要保证消息的可靠投递,则需要通过应用层去实现这个特性。\n\n因此,为了保障IM通信中A端-Server-B端的三方通信的可靠性,可以采用在应用层加入确认机制的办法来实现。\n\n1. client-B 向 im-server 发送一个ack请求包,即ack:R\n\n2. im-sercer在处理成功后,回复client-B一个ack响应包,即ack-A\n\n3. im-server主动向client-A发送一个ack通知包,即ack-N\n\n[img[应用层确认-消息投递过程.png]]\n\n所以,当client-A收到了ack:N报文,才算真正确认client-B收到了消息。\n\n> 一条消息的发送,分别包含(上)(下)两个半场,即msg的R/A/N三个报文,ack的R/A/N三个报文。一个应用层即时通讯消息的可靠投递,共涉及6个报文,这就是im系统中消息投递的最核心技术(如果某个im系统不包含这6个报文,不要谈什么消息的可靠性)。\n\n!!!! 此方案仍然存在的隐患\n\n1. msg:R,msg:A 报文可能丢失:\n此时直接提示“发送失败”即可,问题不大;\n\n2. msg:N,ack:R,ack:A,ack:N这四个报文都可能丢失:\n\n此时client-A都收不到期待的ack:N报文,即client-A不能确认client-B是否收到“你好”。\n\n!!! 消息的超时与重传\n\nclient-A发出了msg:R,收到了msg:A之后,在一个期待的时间内,如果没有收到ack:N,client-A会尝试将msg:R重发。\n\n可能client-A同时发出了很多消息,故client-A''需要在本地维护一个等待ack队列,并配合timer超时机制'',来记录哪些消息没有收到ack:N,以定时重发。\n\n一旦收到了ack:N,说明client-B收到了“你好”消息,对应的消息将从“等待ack队列”中移除。\n\n!!!! 消息的重传存在什么问题\n\n若ack:N 报文丢失:说明client-B之前已经收到了“你好”报文(只是client-A不知道而已),超时与重传机制将导致client-B收到重复的消息。\n\n!!! 消息的去重\n\n由发送方client-A生成一个消息去重的msgid,保存在“等待ack队列”里,同一条消息使用相同的msgid来重传,供client-B去重,而不影响用户体验。\n\n"}, {"title":"垃圾回收(gc)","color":"#6f597d","created":"20230126100247338","creator":"Gezi","modified":"20230318173236790","modifier":"Gezi","tags":"笔记","type":"text/vnd.tiddlywiki","text":"\u003C\u003Clist-links \"[tag[垃圾回收(gc)]sort[title]]\">>\n"}, {"title":"基础SQL语句","created":"20220519152627612","creator":"lzq","modified":"20220519152854287","modifier":"lzq","tags":"软件构造-应用数据库 SQL语句","type":"text/vnd.tiddlywiki"}, diff --git a/service-worker.js b/service-worker.js index 75e81a0..f975383 100644 --- a/service-worker.js +++ b/service-worker.js @@ -11,7 +11,7 @@ const { CacheFirst, StaleWhileRevalidate } = workbox.strategies; const { ExpirationPlugin } = workbox.expiration; const { precacheAndRoute, matchPrecache } = workbox.precaching; -precacheAndRoute([{"revision":"51b1c635de81aaf49c1b674eb91971fa","url":"favicon.ico"},{"revision":"325ff0f39487d4aaf29fa769ae50008a","url":"index.html"},{"revision":"b5c865126953a407bb214fc9114b9d88","url":"tiddlywiki5.js"},{"revision":"713f708b9b2662da54cd38bc98a6483f","url":"TiddlyWikiIconBlack.png"},{"revision":"2c94295d5e6cfa9e5f0b666c4ba1964c","url":"TiddlyWikiIconWhite.png"},{"revision":"a9c3231dc859bbccdd8c14950efcb030","url":"vercel.json"}]); +precacheAndRoute([{"revision":"51b1c635de81aaf49c1b674eb91971fa","url":"favicon.ico"},{"revision":"f72ff69e1cc83476fda9abd2ed935afe","url":"index.html"},{"revision":"b5c865126953a407bb214fc9114b9d88","url":"tiddlywiki5.js"},{"revision":"713f708b9b2662da54cd38bc98a6483f","url":"TiddlyWikiIconBlack.png"},{"revision":"2c94295d5e6cfa9e5f0b666c4ba1964c","url":"TiddlyWikiIconWhite.png"},{"revision":"a9c3231dc859bbccdd8c14950efcb030","url":"vercel.json"}]); registerRoute( /\.css$/,