IceBerg自我学习(1)

在最近的知识学习中,感觉到自己技术栈的匮乏,因此想扩展一下自己的技术面,在自我了解和咨询下,决定开始学习IceBerg,以下皆是自我学习的成果,希望记录下来也能更好的让自己理解,如果有不对的希望大佬可以指正

IceBerg是数据湖的一种,在我看来,他的出现主要是为了解决Hive的一些痛点,比如

  • 海量分区耗时过长

  • 元数据信息分散到HDFS和MYSQL上,难以保证写入原子性

  • 没有文件级别的统计信息,因此fillter过滤的时候,只能过滤partition

  • 对底层文件系统的复杂语义依赖

这是我在网上了解到的一些痛点,之后又了解到了IceBerg针对这些痛点所提出的改善

  • 构建于存储格式之上的数据组织方式

IceBerg的存储格式还是基于如ORC,Parquet等存储格式,只是基于这些,在他之上提供了更好的元数据管理模式,有V1和V2版本,这些后面介绍

  • 保证ACID的能力

  • 提供行级别的修改能力

  • 保证schema的准确性,并提供一定的扩展性

当然IceBerg的功能特性远不止这些,我再举几个例

  • 模式演化:支持对表的增加、修改、删除等操作且没有副作用

  • 隐藏分区:分区字段可以不用显示展示,可以通过计算字段值得出

  • 快照控制:可实现完全相同的快照下进行可重复查询

IceBerg的架构

在架构体系上,有两个版本,分别是V1版本和V2版本

在这里插入图片描述

这是在网上找到的三层元数据结构

  • VersionMetadata

记录当前最新的元数据版本

  • Snapshot

快照信息,指向一个Manfest List清单列表文件,其中包含父快照ID,快照时间戳等

  • Manifest List

列表清单文件,每个清单文件占据一行。每行中存储了清单文件的路径、清单文件里面存储数据文件的分区范围、增加了几个数据文件、删除了几个数据文件等信息。这些信息可以用来在查询时提供过滤

  • Manifest

这是清单文件,里面记录了实际数据Data的详细描述,包括存储路径、状态、分区信息、列级别的统计信息(比如每一列的最大值/最小值等,可以用来进行分区过滤)、文件的大小等

  • data files

实际的数据存储处,一般存储在HDFS的Data路径下

如果要进行数据查询,顺序应该是

  1. 进入menstore目录下,先找到VersionMetadata文件(JSON格式),从其中找到对应的快照文件

  2. 找到以snap开头avro结尾的Snapshot文件,从其中找到清单文件的对应名称

  3. 找到对应的清单文件,以avro结尾,再从清单文件中,找到data数据在data路径下的存储位置和存储名称,然后进行访问

接下来要提到的,就是我之前说的V1和V2版本了

上述的架构可以说就是V1版本,而V2版本也是基于这个架构,不过是在他之上进行了增强设计

合并了Manifest List 和Snapshot
行级修改能力

新增Delete File,存储在Metadata目录下,与Manifest同级,负责记录删除行的信息,通过delete_file_path进行关联

有两种类型,分别是

  • Position Delete : 记录被删除行的文件路径和行偏移量(适合随机删除)
  • Equality Delete: 存储匹配删除条件的列值,比如user_id =1001(适合批量删除)

实现的原理

  • 写入流程 : 执行Delete时生成DELETE FILE,执行UPDATE的时候先执行DELETE再INSERT
  • 读取合并 : 查询的时候动态合并DATA FILE 和 DELETE FILE,过滤掉标记行
统计信息增强
  1. 增加了统计维度:
    除了基础的min/max等,还增加了null_value_count等,以及记录了每个文件的row_count,file_size_in_bytes等优化查询效率

  2. 查询加速场景:
    利用partition_summaries来快速跳过无用的分区,通过column_sizes来优化IO读取,优先读取小文件等

元数据示例

Manifest_content:{
	"contain_null" : False,
	"lower_bound" :  "2025-01-01", // 这个是代表上边界和下边界,方便时间过滤/数值列值裁剪
	"upper_bound" : "2025-01-30",
	"column_sizes" : {"id" : 128MB,"name" : 256MB}
}

//文件级的统计
//可用于小文件合并等策略驱动
{
	"record_count" : 100000 //文件总行数
	,"value_count" : {"id" : 10000} //非NULL值统计
}

//分区摘要
//可快速定位分区
"partition_summaries": [
  {
    "contains_null": true,
    "partition": {"dt": "2025-01-01"},
    "file_count": 5
  }
]
并发控制的优化

写入通过检查快照ID是否变化,冲突时自动报错或重试(乐观锁)

通过commit.retry_num-retries来配置,默认是3次重试

以及支持多个任务同时修改不同分区(V1需串行)

V1采取的是全局锁的形式,经典场景就是:任务A写入dt=20250422的分区,任务B写入dt=20250430就会阻塞。V2引入乐观并发控制OOC来并发写入,写入时只锁定目标分区,非目标分区不受影响,但是要开启write.spark.fanout.enable = true,要注意V1版本无法实现真正的并行

Catalog

Catalog在IceBerg中处于核心管理地位,作为用户和Iceberg表交互的唯一入口,所有表的元数据操作都要通过Catalog来发起

在Hive生态中,Catalog是通过Metastore(如Hive Metastore Service)统一管理数据库和表的元数据系统。访问表时可通过HiveServer2/JDBC等接口,其层级关系为表→数据库→Catalog,其中Catalog提供了跨数据库的元数据管理能力,并支持多引擎共享。

因此我们可以简单的认为:Catalog就是一个让我们访问不同数据库的接口

在IceBerg中,Catalog共有三种

  • Hive Catalog(默认,也是最常用的)
-- 指定catalog为hive,并命名为iceberg_hive
set iceberg.catalog.iceberg_hive.type = hive
-- 指定连接hive的端口
set iceberg.catalog.iceberg_hive.uri = "thrift://hadoop:9083;"
-- 指定Hive在HDFS上的存储路径
set iceberg.catalog.iceberg_hive.warehouse=hdfs://hadoop1:8020/warehouse/iceberg-hive;

CREATE TABLE iceberg_test2
(
	id 			int
)
-- 指定存储格式为Iceberg
STORED BY "org.apache.iceberg.mr.hive.HiveIcebergStorageHandler"
-- 指定Catalog名称
TBLPROPERTIES('iceberg.catalog'='iceberg_hive');
  • Hadoop Catalog
set iceberg.catalog.iceberg_hadoop.type=hadoop;
set iceberg.catalog.iceberg_hadoop.warehouse=hdfs://hadoop1:8020/warehouse/iceberg-hadoop;

CREATE TABLE iceberg_test3 (i int) 
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
-- hadoop Catalog需要显示指引存储地址
LOCATION 'hdfs://hadoop1:8020/warehouse/iceberghadoop/default/iceberg_test3'
TBLPROPERTIES('iceberg.catalog'='iceberg_hadoop');
  • 指定路径加载
CREATE EXTERNAL TABLE iceberg_test4 (i int)
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
-- 一定要指定我们已有数据表的位置
LOCATION 'hdfs://hadoop1:8020/warehouse/iceberghadoop/default/iceberg_test3'
-- 启用配置
TBLPROPERTIES ('iceberg.catalog'='location_based_table');

该语句本质是通过Hive建立到已有Iceberg表的逻辑映射,而非创建新表结构

Hive的集成

这一块学习的时候没有了解特别深,因为我主要想学Spark + Iceberg的应用方向,就简单介绍一下我的理解

创建表
-- 可以选择是否是外部表
CREATE [EXTERNAL] TABLE iceberg_create1 (i int) 
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler';
创建分区表

隐藏分区是我认为IceBerg中很重要的功能特性

CREATE EXTERNAL TABLE iceberg_create3 (
  id int,
  name string,
  ts timestamp 
)
PARTITIONED BY (
  bucket(16, id),      -- 哈希分区
  months(ts)           -- 时间函数分区
)
STORED BY 'org.apache.iceberg.mr.hive.HiveIcebergStorageHandler'
TBLPROPERTIES (
  'iceberg.catalog'='hive',
  'format-version'='2'  -- 推荐使用V2格式
);

与Spark的集成

Spark同样也是使用Hive Catalog和Hadoop Catalog

// Hive Catalog配置(连接HMS元数据库)
spark.sql.catalog.hive_prod = org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.hive_prod.type = hive
spark.sql.catalog.hive_prod.uri = thrift://hadoop1:9083  // Hive Metastore地址
use hive_prod; 

// Hadoop Catalog配置(基于文件系统)
spark.sql.catalog.hadoop_prod = org.apache.iceberg.spark.SparkCatalog
spark.sql.catalog.hadoop_prod.type = hadoop
spark.sql.catalog.hadoop_prod.warehouse = hdfs://hadoop1:8020/warehouse/spark-iceberg  // 需HDFS路径
use hadoop_prod

要注意切换Catalog的时候要use一下

创建表

正常创建表,与Hive语法会有些不同

CREATE TABLE hadoop_prod.default.sample1 (
 id bigint COMMENT 'unique id',
 data string)
USING iceberg

要注意我们的命名规范是Catalog.Database.Table,Spark SQL可以自动解析对应的Catalog和数据库,这是显式指定

同样还可以创建分区表,分区表也有显示分区和隐式分区之分

-- 这是显示分区,显示指定分区字段
CREATE TABLE hadoop_prod.default.sample2 (
 id bigint,
 data string,
 category string)
USING iceberg
PARTITIONED BY (category); -- 显示指定分区字段为category

-- 这是隐式分区,分区字段通过表字段计算得来
CREATE TABLE hadoop_prod.default.sample3 (
 id bigint,
 data string,
 category string,
 ts timestamp)
USING iceberg
PARTITIONED BY (bucket(16, id), days(ts), category)

隐式分区是我们常用的方法,常用的转换还有以下几种

支持的转换有:
➢ years(ts):按年划分
➢ months(ts):按月划分
➢ days(ts)或 date(ts):等效于 dateint 分区
➢ hours(ts)或 date_hour(ts):等效于 dateint 和 hour 分区
➢ bucket(N, col):按哈希值划分 mod N 个桶
➢ truncate(L, col):按截断为 L 的值划分
字符串被截断为给定的长度
整型和长型截断为 bin: truncate(10, i)生成分区 0,10,20,30,…

当我们使用CTAS的方式去创建表的时候,要注意指定分区的属性,不然分区属性就失效了(Replace into的建表方式也类似)

CREATE TABLE hadoop_prod.default.sample5
USING iceberg
PARTITIONED BY (bucket(8, id), hours(ts), category)
TBLPROPERTIES ('key'='value') -- 这是占位符,实际使用是会转换如'format-version'='2'的配置
AS SELECT * from hadoop_prod.default.sample3

要注意执行时会触发Spark作业

删除表

  • 对于Hadoop Catalog来说,使用Drop Table可以直接删除数据和表

  • 对于Hive Catalog来说,使用Drop Table在0.4之后只会删除表,数据并不会删除,所以要使用Drop Table Purge才能删除
    DROP TABLE hive_prod.default.sample7 PURGE

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值