在上一章,我们学习了 Paimon 如何保证每一次写入的原子性和一致性。但数据仓库的核心需求不仅是写入,更重要的是更新。想象一个场景:我们需要实时更新用户的最新信息,或者实时累加计算用户的消费总额。传统的 Hive 数据湖对此无能为力,每次更新都需要重写整个分区,成本极高。

  Paimon 通过引入主键(Primary Key)和一套精巧的更新机制,完美解决了这个问题。本章,我们将揭秘 Paimon 高效更新背后的两大支柱:LSM-Tree 思想的巧妙运用,以及功能强大的合并引擎。

  6.1 实现原理:LSM 树 (Log-Structured Merge-Tree)

    Paimon 并没有在本地磁盘上实现一个完整的 LSM-Tree,而是巧妙地借鉴其核心思想,并将其应用在分布式文件系统(如 HDFS, S3)之上来组织数据文件。

    6.1.1 Paimon 如何借鉴 LSM 思想

    传统 LSM-Tree 有 MemTable、Immutable MemTable 和多层 SSTable。

    Paimon 将其映射为:

  • MemTable (内存表) -> Flink Writer 的内存缓冲区

  • SSTable (有序字符串表) -> Paimon 的数据文件 (Data File),如 Parquet。

    核心思想是:写入操作非常快,只追加新文件;读取操作需要合并多个文件来获取最新结果;后台任务(Compaction)会不断合并小文件,优化读取性能。

    Paimon 将属于同一个 Bucket 的数据文件组织成一个逻辑上的 LSM-Tree,分为多个层级 (Level):

  • L0 (Level 0): 所有新的写入(来自 Flink 的新数据)都会生成新的数据文件,并被放入 L0。这一层的文件可能很小,并且它们之间的主键范围可能重叠

  • Ln (Level n, n > 0): 更高层级的文件。同一层内的文件,其主键范围互不重叠。文件通常更大,数据也更“陈旧”。

    6.1.2 原理图解:数据写入与后台合并 (Compaction)

  流程讲解:

  1. 写入 (Write): Flink 作业持续不断地将数据写入 Paimon 表。每次 Checkpoint 成功后,内存中的数据会被刷写成一个新的数据文件,并放置在 L0。如图中所示,L0 中有多个文件,它们的主键范围是相互重叠的(比如 File 1File 2 都包含主键 35)。

  2. 合并 (Compaction):

    1. 触发时机:当 L0 的文件数量达到某个阈值(例如 num-sorted-run.compaction-trigger)时,Paimon 会触发一次 Compaction。

    2. 合并过程:Compaction 任务会选择 L0 中的一些文件,以及 L1 中与这些文件主键范围重叠的文件,将它们一起读出。

    3. 数据去重/合并:在读取过程中,Paimon 会应用指定的合并引擎 (Merge Engine) 来处理主键冲突的行(我们将在下一节详述),最终得到每个主键唯一且最新的记录。

    4. 生成新文件:合并后的结果被写入到 L1,成为一个或多个新的、更大的、并且在 L1 内部主键不重叠的文件。

    5. 级联合并:当 L1 的文件大小或数量也达到阈值时,会触发 L1 到 L2 的合并,以此类推。

  3. 读取 (Read): 当用户查询数据时,Paimon 需要同时读取 L0, L1, L2... 中所有与查询条件相关的文件,并在查询时进行合并,以确保返回最新的数据。后台 Compaction 的目的就是减少读取时需要合并的文件数量,从而大幅提升查询性能。

  6.2 实现原理:合并引擎 (Merge Engine)

合并引擎定义了当 Compaction 或 Query 时,遇到相同主键的多条记录时应该如何处理。Paimon 提供了三种核心引擎,通过 merge-engine 表属性来指定。

6.2.1 deduplicate: 去重引擎 (默认)

  • 原理:这是最简单的引擎。对于相同主键的多条记录,它只会保留最新的一条。Paimon 内部通过一个序列号来判断新旧,序列号越大表示记录越新。

  • 适用场景

    • 经典的 CDC (Change Data Capture) 场景,同步业务数据库的 INSERT/UPDATE 操作。

    • 只需要保留每条记录的最终状态,不需要历史版本。

    • CREATE TABLE UserProfile (user_id STRING,name STRING,city STRING,PRIMARY KEY (user_id) NOT ENFORCED
      ) WITH ('merge-engine' = 'deduplicate' -- 默认引擎,可以不写
      );-- 写入初始数据
      INSERT INTO UserProfile VALUES ('u001', 'Alice', 'New York');
      -- 更新数据 (实际上是插入一条新的记录)
      INSERT INTO UserProfile VALUES ('u001', 'Alice', 'San Francisco');-- 查询结果:
      -- +---------+-------+-----------------+
      -- | user_id | name  | city            |
      -- +---------+-------+-----------------+
      -- | u001    | Alice | San Francisco   |  <-- 只保留了最新的城市
      -- +---------+-------+-----------------+

6.2.2 partial-update: 部分列更新引擎

  • 原理:一个强大的优化。当新纪录的某些列为 NULL 时,它会保留这些列在旧记录中的值,实现“部分更新”。如果新记录的列不为 NULL,则会覆盖旧值。

  • 优势:避免了“Read-Modify-Write”的模式。更新时,你只需要提供要变更的字段和主键,而不需要先读出整行数据,极大地提升了更新效率。

  • 适用场景

    • 宽表更新,每次只更新少数几个字段。例如,更新用户画像中的某个标签。

  • 示例代码 (Flink SQL):

CREATE TABLE UserProfile (user_id STRING,name STRING,city STRING,email STRING,PRIMARY KEY (user_id) NOT ENFORCED
) WITH ('merge-engine' = 'partial-update'
);-- 写入初始数据
INSERT INTO UserProfile VALUES ('u002', 'Bob', 'London', 'bob@example.com');-- 只更新城市,其他字段设为 NULL
-- 在 DataStream API 中直接传入 null,在 SQL 中可以用 CAST(NULL AS ...)
INSERT INTO UserProfile VALUES ('u002', NULL, 'Paris', NULL);-- 查询结果:
-- +---------+------+--------+-----------------+
-- | user_id | name | city   | email           |
-- +---------+------+--------+-----------------+
-- | u002    | Bob  | Paris  | bob@example.com | <-- name 和 email 被保留,city 被更新
-- +---------+------+--------+-----------------+

6.2.3 aggregation: 聚合引擎

  • 原理:当遇到相同主键的记录时,不再是覆盖,而是根据预设的聚合函数进行聚合。每个非主键列都可以指定一种聚合方式。

  • 支持的函数sum, max, min, last_non_null_value, last_value, listagg, bool_and, bool_or 等。

  • 适用场景

    • 实时指标聚合,如计算每个商品的累计销售额、用户当天的最大消费金额等。

  • 示例代码 (Flink SQL):

CREATE TABLE ProductSales (product_id STRING,sales_count INT,total_revenue DOUBLE,last_sale_time TIMESTAMP(3),PRIMARY KEY (product_id) NOT ENFORCED
) WITH ('merge-engine' = 'aggregation','fields.sales_count.aggregate-function' = 'sum',       -- 销量累加'fields.total_revenue.aggregate-function' = 'sum',     -- 销售额累加'fields.last_sale_time.aggregate-function' = 'max'     -- 保留最新的销售时间
);-- 写入三笔销售记录
INSERT INTO ProductSales VALUES ('p001', 1, 10.5, CAST('2023-10-27 10:00:00' AS TIMESTAMP(3)));
INSERT INTO ProductSales VALUES ('p001', 2, 21.0, CAST('2023-10-27 11:00:00' AS TIMESTAMP(3)));
INSERT INTO ProductSales VALUES ('p001', 1, 9.8, CAST('2023-10-27 10:30:00' AS TIMESTAMP(3)));-- 查询结果:
-- +------------+-------------+---------------+-------------------------+
-- | product_id | sales_count | total_revenue | last_sale_time          |
-- +------------+-------------+---------------+-------------------------+
-- | p001       | 4           | 41.3          | 2023-10-27 11:00:00.000 | <-- 所有字段都按规则聚合了
-- +------------+-------------+---------------+-------------------------+

合并引擎对比图 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/93206.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/93206.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/93206.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第十六届蓝桥杯青少组C++省赛[2025.8.9]第二部分编程题(4、矩阵圈层交错旋转)

参考程序&#xff1a;#include <bits/stdc.h> using namespace std;const int MAXN 105; int a[MAXN][MAXN];int main() {int n;if (!(cin >> n)) return 0;for (int i 0; i < n; i)for (int j 0; j < n; j)cin >> a[i][j];int layers n / 2; // 每…

【FastGTP✨】[01] 使用 FastGPT 搭建简易 AI 应用

简易应用&#xff1a;英语单词解释 例句 1. 前言 FastGPT 是一个低代码 AI 应用构建平台&#xff0c;可以通过简单配置快速创建自己的 AI 应用。 本文将带你用 FastGPT 搭建一个 英语单词解释 例句 的 AI 工具&#xff0c;输入英文单词后&#xff0c;输出&#xff1a; 单词…

【Mysql语句练习】

MysqlMysql语句练习一、建库建表二、插入数据三、查询Mysql语句练习 一、建库建表 1、创建数据库mydb11_stu&#xff0c;并使用数据库 # 创建数据库mydb11_stu mysql> create database mydb11_stu; Query OK, 1 row affected (0.00 sec) # 使用数据库 mysql> use mydb1…

用Python Scrapy征服网络爬虫(反爬技术深入剖析)

目录 第1章:Scrapy是个啥?为什么它是你爬虫路上的最佳拍档? 1.1 Scrapy的核心亮点 1.2 啥时候用Scrapy? 1.3 安装Scrapy 第2章:动手写你的第一个Scrapy爬虫 2.1 创建Scrapy项目 2.2 定义数据结构(Items) 2.3 编写爬虫逻辑 2.4 运行爬虫 2.5 小技巧:调试爬虫 …

解决Electron透明窗口点击不影响其他应用

遇到的问题&#xff1a;在electron透明窗口点击&#xff0c;影响窗口下的应用接受不到点击事件解决方案&#xff1a;CSSIgnoreMouseEvents实现原理&#xff1a;主进程默认设置禁用目标窗口鼠标事件&#xff08;禁用之后能检测到mousemove&#xff09;&#xff0c;UI进程检测页面…

C# 泛型(Generics)详解

泛型是 C# 2.0 引入的核心特性&#xff0c;它允许在定义类、接口、方法、委托等时使用未指定的类型参数&#xff0c;在使用时再指定具体类型。这种机制可以显著提高代码的复用性、类型安全性和性能。一、泛型的核心概念类型参数化泛型允许将类型作为 "参数" 传递给类…

Spring中存在两个相同的Bean是否会报错?

第一种情况&#xff1a;使用XML的方式设置Bean&#xff0c;这种情况在Spring启动时就会报错&#xff0c;因为ID在Spring中是Bean的唯一标识&#xff0c;Spring容器在启动时会校验唯一性&#xff0c;一旦发现重复就会报错。但是如果是在两个不同的XML文件中定义两个相同的Bean&a…

【新手入门】Android基础知识(一):系统架构

目 录 Android 系统架构图 1. 应用 2. JAVA API 框架 3. 原生 C/C 库 4. Android 运行时&#xff08;Android Runtime&#xff09; 5. 硬件抽象层 (HAL) 6. Linux 内核 参考资料 Android 系统架构图 Android底层内核空间以Linux Kernel作为基石&#xff0c;上层用户空…

晶振电路的负载电容、电阻参数设计

系列文章目录 文章目录系列文章目录前言一、晶振主要参数二、有源与无源区别三、无源晶振四、有源晶振总结前言 在硬件电路的设计中&#xff0c;晶振电路是必不可少的&#xff0c;它充当了整个电路心脏的作用。在这个晶振电路的设计中负载电容、电阻参数的选型是很重要的&…

电脑上练打字用什么软件最好:10款打字软件评测

现在孩子们在电脑上练打字&#xff0c;软件一搜一大把&#xff0c;可好多家长和老师都犯愁&#xff1a;到底哪个管用&#xff1f;我带200多个小学生练过字&#xff0c;前前后后试了十款软件&#xff0c;今天就掏心窝子说说——有的看着花哨其实没用&#xff0c;有的专业是专业但…

第五天~提取Arxml的模板信息

🌟 ARXML模板信息提取:解锁汽车软件的乐高魔法 在汽车电子的世界里,AUTOSAR(汽车开放系统架构)如同无形的神经系统,而ARXML文件正是承载这套神经系统蓝图的数字载体。当工程师们需要批量创建或修改ECU(电子控制单元)配置时,模板信息提取便成为了一项至关重要的核心技…

react+antd+vite自动引入组件、图标等

前言&#xff1a;react在使用antd的时候&#xff0c;也是需要每个组件都在界面上按需引入的&#xff0c;那能不能自动生成&#xff0c;按需使用呢&#xff1f;我们这里说一说这个。安装插件&#xff0c;组件按需引入unplugin-antd-resolverunplugin-auto-importnpm install unp…

深度学习与遥感入门(六)|轻量化 MobileNetV2 高光谱分类

系列回顾&#xff1a; &#xff08;一&#xff09;CNN 基础&#xff1a;高光谱图像分类可视化全流程 &#xff08;二&#xff09;HybridNet&#xff08;CNNTransformer&#xff09;&#xff1a;提升全局感受野 &#xff08;三&#xff09;GCN 入门实战&#xff1a;基于光谱 KNN…

第4节 神经网络从公式简化到卷积神经网络(CNN)的进化之路

🧙 深度学习的"玄学进化史" 从CNN用卷积层池化层处理图片,循环网络RNN如何利用上下文处理序列数据,到注意力机制让Transformer横空出世,现在的大语言模型已经能写能画能决策!每个新技巧都让人惊呼"还能这么玩",难怪说深度学习像玄学——但这玄学,…

最新去水印小程序系统 前端+后端全套源码 多套模版 免授权(源码下载)

最新去水印小程序系统 前端后端全套源码 多套模版 免授权 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/91669468 更多资源下载&#xff1a;关注我

TCP Socket 编程实战:实现简易英译汉服务

前言&#xff1a;TCP&#xff08;传输控制协议&#xff09;是一种面向连接、可靠的流式传输协议&#xff0c;与 UDP 的无连接特性不同&#xff0c;它通过三次握手建立连接、四次挥手断开连接&#xff0c;提供数据确认、重传机制&#xff0c;保证数据有序且完整传输。本文将基于…

CF566C Logistical Questions Solution

Description 给定一棵 nnn 个点的树 TTT&#xff0c;点有点权 aia_iai​&#xff0c;边有边权 www. 定义 dist⁡(u,v)\operatorname{dist}(u,v)dist(u,v) 为 u→vu\to vu→v 的简单路径上的边权和. 找到一个节点 uuu&#xff0c;使得 W∑i1ndist⁡(u,i)32aiW\sum\limits_{i1}^n…

聊天室全栈开发-保姆级教程(Node.js+Websocket+Redis+HTML+CSS)

前言 最近在学习websocket全双工通信&#xff0c;想要做一个联机小游戏&#xff0c;做游戏之前先做一个聊天室练练手。 跟着本篇博客&#xff0c;可以从0搭建一个属于你自己的聊天室。 准备阶段 什么人适合学习本篇文章&#xff1f; 答&#xff1a;前端开发者&#xff0c;有一…

后台管理系统-2-vue3之路由配置和Main组件的初步搭建布局

文章目录1 路由搭建1.1 路由创建(router/index.js)1.2 路由组件(views/Main.vue)1.3 路由引入并注册(main.js)1.4 路由渲染(App.vue)2 element-plus的应用2.1 完整引入并注册(main.js)2.2 示例应用(App.vue)3 ElementPlusIconsVue的应用3.1 图标引入并注册(main.js)3.2 示例应用…

使用 Let’s Encrypt 免费申请泛域名 SSL 证书,并实现自动续期

使用 Let’s Encrypt 免费申请泛域名 SSL 证书&#xff0c;并实现自动续期 目录 使用 Let’s Encrypt 免费申请泛域名 SSL 证书&#xff0c;并实现自动续期 &#x1f6e0;️ 环境准备&#x1f4a1; 什么是 Let’s Encrypt&#xff1f;&#x1f9e0; Let’s Encrypt 证书颁发原…