数据湖湖仓一体大数据数据存储【免费下载链接】hudiUpserts, Deletes And Incremental Processing on Big Data.项目地址https://gitcode.com/gh_mirrors/hud/hudi点击查看免费下载本篇技术指南以 Apache Hudi 仓库中vector_blob_demo/notebooks目录下的 Jupyter Notebook 变体为核心系统讲解如何用 Hudi 1.2.0 的VECTOR 类型、BLOB 类型INLINE / OUT_OF_LINE 两种存储形态与hudi_vector_search向量检索 TVF在 PySpark Lance 之上完成图片嵌入向量 图像字节的端到端写入与 top-K 相似检索。读完本文你将掌握四个 Notebook 的完整运行方式、所有开关变量的含义与取值、INLINE 读取模式CONTENT / DESCRIPTOR的底层行为差异以及这些能力在 Hudi 源码中的实现位置。一、Notebook 与 .py 脚本的关系vector_blob_demo目录同时提供了两种形态的演示原始.py脚本hudi_blob_reader_demo.py、hudi_sql_vector_blob_demo.py、hudi_dataframe_vector_blob_demo.py与其 Jupyter Notebook 变体notebooks/00_main_demo.ipynb等四个文件。Notebook 变体与.py脚本使用完全相同的 jar 包、虚拟环境与最终状态区别仅在于载体.py脚本是可脚本化的规范参考由run_demos.sh驱动适合批量、无交互执行Notebook 将代码、叙述文字与运行输出图片、top-K 结果面板整合在单一可滚动文档中特性开关退化为文件顶部的普通 Python 变量改一个变量再Run All即可无需操作 shell 环境变量适合现场演示与自助探索。原始.py脚本保持原样二者互不干扰。父目录 README.md 是.py脚本的完整文档本文则聚焦 Notebook 形态并补充源码级原理。二、环境准备与 .py 脚本共用同一套 venvNotebook 与.py脚本共用同一个虚拟环境。在vector_blob_demo父目录下执行cd ../ # 回到 vector_blob_demo/ python3.12 -m venv .venv source .venv/bin/activate pip install -r requirements.txt # 额外安装 jupyter ipykernel其中 requirements.txt 的关键依赖为pyspark3.5.*、pyarrow14.0.0供 SQL 与 blob reader 演示直接写 staging Parquet、torch/torchvision/timm嵌入模型与数据集、scikit-learnL2 归一化、matplotlib结果面板以及jupyter/ipykernel仅 Notebook 需要。注意Python 必须是 3.12——PySpark 3.5 不支持 Python 3.13/3.14在 3.13 上createDataFrame会直接抛RecursionError: Stack overflow。2.1 两个 bundle jar 的默认路径与覆盖方式Notebook 默认将HUDI_BUNDLE_JAR指向~/Downloads/hudi-spark3.5-bundle_2.12-1.2.0.jar将LANCE_BUNDLE_JAR指向~/Downloads/lance-spark-bundle-3.5_2.12-0.4.0.jar与.py脚本一致。若已按父目录 README.md 第 1、2 节的 curl 命令将两个 jar 放入~/Downloads/则无需任何额外导出。如需覆盖例如指向本地构建的 bundleexport HUDI_BUNDLE_JAR/abs/path/to/hudi-spark3.5-bundle_2.12-1.2.0-SNAPSHOT.jar export LANCE_BUNDLE_JAR/abs/path/to/lance-spark-bundle-3.5_2.12-0.4.0.jarLANCE_BUNDLE_JAR仅在BASE_FILE_FORMAT lance时才会被读取——跑纯 Parquet 路径完全不需要它这也是SKIP_LANCE1时 run_demos.sh 会跳过 Lance 组合的原因。2.2 启动 JupyterLab必须在notebooks/目录内启动保证 Notebook 能找到其工作目录cd notebooks/ jupyter lab三、四个 Notebook 逐个拆解3.100_main_demo.ipynb— 从这里开始规范演示唯一的端到端规范演示单 Notebook 走完全程Lance 基础文件 INLINE BLOB然后在一条 SQL 查询里组合hudi_vector_searchread_blob()使得 PNG 字节只在 top-K 最近邻这最后一步被物化——中间数据不搬运。开关变量位于 Notebook 顶部N_SAMPLES 250 TOP_K 5默认是 Lance / INLINE 组合。要在 Parquet 上运行同一演示只需把 DDL 中的hoodie.table.base.file.format lance改成parquet无需其他任何代码改动——这正是 Hudi 将 VECTOR BLOB 向量检索路径做成基础文件格式无关的体现。OUT_OF_LINE / DESCRIPTOR 的故事板见01_blob_reader.ipynb。3.201_blob_reader.ipynb— 补充OUT_OF_LINE BLOB演示 Hudi BLOB 类型作为**引用reference**而非字节的用法一个极小的 Hudi 表可以指向存放在别处的非结构化数据read_blob()在查询时把引用解析为字节。开关变量Notebook 顶部BASE_FILE_FORMAT parquet # parquet 或 lance BLOB_MODE out_of_line # out_of_line 或 inline INLINE_READ_MODE content # content 或 descriptor # 仅在 BLOB_MODE inline 时有意义 N_SAMPLES 100故事板三种模式BLOB_MODE out_of_line→ Hudi 表只保存(external_path, offset, length)引用真正的字节在/tmp/pets_blob_container.bin中足迹对比显示 Hudi 表体积不足容器文件的 1%是湖仓只引用不复制非结构化数据这一叙事的直接证据BLOB_MODE inlineINLINE_READ_MODE content→ 字节内联进 Hudi 基础文件image_bytes.data直接返回原始 PNG 字节BLOB_MODE inlineINLINE_READ_MODE descriptor→image_bytes.data为 nullimage_bytes.reference.*被合成为指向底层基础文件Lance 格式下为.lance文件的引用read_blob()惰性物化字节。3.302_sql_vector_search.ipynb— 补充SQL DDL 深潜纯 SQL 走查三个核心环节CREATE TABLE ... (embedding VECTOR(N), image_bytes BLOB, ...) USING hudiINSERT INTO ... SELECT named_struct(type,INLINE, ...)——在 SQL 层用named_struct手工构造 BLOB 结构体hudi_vector_searchTVF 做余弦相似度 top-K 检索。开关变量BASE_FILE_FORMAT parquet # parquet 或 lance N_SAMPLES 2563.403_dataframe_vector_search.ipynb— 补充DataFrame API与02终点相同但改用 PySpark DataFrame API 构建StructField(metadata{hudi_type: VECTOR(N)})与 BLOB 结构体形状type/data/reference三字段在 Python 侧显式声明由spark.createDataFrame(rows, explicit_schema)以 schema 为准落地。适合想观察 VECTOR/BLOB 逻辑类型在 DataFrame 底层如何组合的库式集成场景。开关变量BASE_FILE_FORMAT parquet # parquet 或 lance N_SAMPLES 256四、开关机制Python 变量如何驱动运行时行为每个 Notebook 都以一个开关单元格toggles cell开头内含普通 Python 变量。编辑取值后Run All变量会流入与.py脚本用os.getenv(...)构建的同一个CONFIG字典——因此 Notebook 的运行时行为与设置了对应环境变量的.py脚本完全等价。HUDI_BUNDLE_JAR与LANCE_BUNDLE_JAR是从环境变量读取的默认~/Downloads/属于主机相关的文件位置而非功能开关。若解析出的 jar 路径不存在Notebook 会以清晰的报错信息快速失败fail fast。从源码结构看这一变量 → CONFIG → Hudi 写选项/会话配置的链路与父目录三个.py脚本中create_spark()的写法一致spark.jars携带两个 bundle、spark.serializer KryoSerializerHudi 必需、spark.sql.extensions HoodieSparkSessionExtension注册含向量检索 TVF 在内的 Hudi SQL 规则、spark.sql.catalog.spark_catalog HoodieCatalog。五、与 .py 脚本共享 /tmp 路径清理是硬性要求Notebook 与.py脚本写入同一批/tmp路径/tmp/hudi_*_pets/、/tmp/pets_blob_container.bin、/tmp/staging_pets_*.parquet。这是有意为之——每个 Notebook 都带一个清理单元格专门擦除这些路径镜像 run_demos.sh 中clean()的行为因此 Notebook 与./run_demos.sh以任意顺序交替运行都不会互相污染。这里有一个值得注意的坑如果跳过清理单元格直接重跑可能触发BatchedBlobReader的EOFException。原因在于 BLOB 容器文件/tmp/pets_blob_container.bin是单一共享路径不同blob_mode会覆盖它而旧 Hudi commit 中记录的引用偏移可能越过新覆盖后文件的 EOFrun_demos.sh的注释也明确指出DROP TABLE IF EXISTS只删 catalog 条目LOCATION处的数据目录与.hoodie/时间线会持久存在旧 commit 会被一并查询。因此始终从顶部Run All至少也要在重跑其余单元格前先重跑清理单元格。六、源码级原理BLOB 结构、读取模式与向量检索 TVF6.1 BLOB 的三字段结构体与 hudi_type 元数据Hudi 的 BLOB 列在 HoodieSchema.java 中被定义为固定三字段结构体type枚举INLINE/OUT_OF_LINE声明字节的存放形态data可空二进制字段——INLINE 时承载真实字节OUT_OF_LINE 时为 nullreference可空结构体包含external_path字符串、offset长整型缺省视为 0、length长整型缺省视为从 offset 到文件尾、managed布尔型false表示外部文件生命周期由用户掌控Hudi 删除行时不会连带删除该文件。同一文件中的HoodieSchema.TYPE_METADATA_FIELD hudi_type第 107 行正是 DataFrame 演示里StructField(metadata{hudi_type: VECTOR(N)})与metadata{hudi_type: BLOB}的源头常量。BLOB 结构体全部字段允许为 null因此 OUT_OF_LINE 时data为 null、INLINE 时reference为 null二者天然互斥。6.2 SQL 解析器如何自动打标在 SQL 路径中无需手工声明hudi_type元数据HoodieSpark3_5ExtendedSqlAstBuilder见 HoodieSpark3_5ExtendedSqlAstBuilder.scala在解析BLOB与VECTOR(N)类型时会调用builder.putString(HoodieSchema.TYPE_METADATA_FIELD, ...)自动把hudi_type元数据写入生成的 StructField。这正是02_sql_vector_search.ipynb无需像 DataFrame 演示那样显式声明元数据的原因——DDL 即来源。6.3hoodie.read.blob.inline.modeCONTENT 与 DESCRIPTORhoodie.read.blob.inline.mode控制 INLINE BLOB 的读取返回形态CONTENTimage_bytes.data直接返回原始字节DESCRIPTORimage_bytes.data为 nullimage_bytes.reference.*被合成指向底层基础文件Lance 基础文件为.lance文件read_blob(image_bytes)惰性物化字节。从 Hudi 1.2.0 起存在按格式的隐式默认值Parquet 默认CONTENTLance 默认DESCRIPTOR同一版本还在BatchedBlobReader中新增了严格守卫——对以 DESCRIPTOR 模式加载的 INLINE 行调用read_blob()会直接抛IllegalStateException旧版本的静默 null 变成硬性失败。TestLanceDataSource.testBlobInlineDescriptorModeTestLanceDataSource.scala即覆盖此行为。这解释了演示中的两个关键工程决策按 load 粒度作用域01_blob_reader.ipynb的 inspection 视图按用户选择的模式读取而read_blob_and_save()在自己的 reader 上显式设置CONTENT——不能依赖 Lance 的隐式默认值已翻转为 DESCRIPTOR否则新守卫会把旧版本的静默 null 变成硬异常在 SparkSession 层面全局设置DESCRIPTOR同样会污染包括read_blob()后端在内的所有读取。对 OUT_OF_LINE 是 no-op那些行本身就是描述符没有可压制的内联字节read_blob()始终经由用户提供的引用解析。6.4hudi_vector_searchTVF 与BatchedBlobReader向量检索侧hudi_vector_search是表值函数TVF签名形如hudi_vector_search(path, embedding_col, ARRAY(...), k, metric)返回结果附_hudi_distance列。其端到端测试见 TestHoodieVectorSearchFunction.scala支持cosine、l2、dot_product三种度量省略度量参数时默认cosine。TVF 要求查询向量必须是常量表达式因此演示采用ARRAY(f1, f2, ...)1024 个浮点内联进 SQL而不是标量子查询请求top_k 1是因为查询图自身也在语料中距离 ≈ 0结果循环里会跳过它。read_blob()是标量函数非 TVF返回BINARY可用于投影、WHERE、JOIN 等任何使用列的位置其底层实现是 BatchedBlobReader.scala 中的BatchedBlobReader——一个批量化字节范围读取器会将同一分区内连续或邻近的读取请求合并为单次 I/O默认 gap 阈值 4KB、lookahead 50 行显著减少 seek 次数。这也解释了为何数据按(external_path, offset)排序时批处理效果最佳以及上文旧 commit 偏移越过 EOF 会抛EOFException的行为来源。七、一句话总结四个 Notebook 是同一套 Hudi 1.2.0 VECTOR/BLOB/向量检索能力的三种表达00是端到端规范路径01深挖 OUT_OF_LINE 引用语义与读取模式02/03分别展示 SQL DDL 与 DataFrame API 两种构建方式。掌握顶部开关变量、jar 默认路径与/tmp清理纪律之后你既可以用 Notebook 做现场演示也可以回到.py脚本用run_demos.sh一键批量跑完全部组合格式 × BLOB 模式矩阵并在源码中定位每一层行为的确切实现。赞分享数据湖湖仓一体大数据数据存储【免费下载链接】hudiUpserts, Deletes And Incremental Processing on Big Data.项目地址https://gitcode.com/gh_mirrors/hud/hudi点击查看免费下载相关推荐Hudi PySpark 快速入门全指南从环境搭建到七大核心数据操作演练Hudi PySpark 快速入门全指南从环境搭建到七大核心数据操作演练 Apache Hudi 作为大数据场景下的数据湖框架提供了完整的 Upsert、D数据湖湖仓一体大数据数据存储10分钟搭建Jupyter Notebook 7开发环境从安装到贡献代码全指南10分钟搭建Jupyter Notebook 7开发环境从安装到贡献代码全指南 你是否还在为复杂的Python开发环境配置而头疼是否想为开源社区贡献代码却不后端前端数据科学Ray Java 测试完整指南从环境搭建到 Bazel 运行与源码级原理剖析Ray Java 测试完整指南从环境搭建到 Bazel 运行与源码级原理剖析 Ray 是一个 AI 计算引擎其核心分布式运行时除了提供 Python API人工智能分布式训练强化学习任务调度模型推理服务后端上一篇华为运动数据迁移终极方案3分钟完成Strava同步下一篇深入剖析NVVL架构如何用CUDA实现视频帧的高效解码与传输创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考