您现在的位置是:首页 >技术杂谈 >数据的存储方式(Parquet、ORC)网站首页技术杂谈

数据的存储方式(Parquet、ORC)

顧棟 2024-09-22 00:01:04
简介数据的存储方式(Parquet、ORC)

数据的存储方式

目前的数据存储方式分为行式和列式。

按行存储

  常见的关系型数据库,如Oracle、DB2、MySQL、SQL SERVER都是行式存储的, 在我们查询的条件需要得到大多数列的时候, 相对列式格式,查询效率更高。基础逻辑存储单元是行数据,在存储介质中是以连续存储的形式存在的。

Hive中的的TextFile文件存储格式中的数据就按行式存储的。

按列存储

大部分分布式分析型数据库,如Hbase、hive、Druid采用是列式存储,

  列式存储, 它存储的方式是采用数据按照行分块,每个块按照列存储。

数据表格
姓名性别年龄
张三20
李四30
王五40
赵六50
逻辑表
张三李四王五赵六20304050
按列存储
张三20李四30王五40赵六50
按行存储

简单的对比

行式存储列式存储
特点1.每一行的所有字段都存在一起
2.查询时即使只涉及某几列,所有数据都会被读取
3.读取时硬盘寻址范围很大
4.适合事务性操作
5.主要用于在线交易处理(OLTP)场景的数据存储
1.每一列的所有数据存在一起,不同列之间支持分开存储
2.查询时只有涉及到的列会被读取
3.读取时硬盘寻道范围小
4.不适合事务性操作
5.主要用于在线分析处理(OLAP)场景的数据存储
优点1.对数据进行插入和修改操作很方便
2.适合随机查询;在整行的读取上,要优于列式存储
1.采用每一列单独存储时,任何列都具有索引能力
2.同列数据具有相同类型,易于压缩,占用空间小
3.为每一列创建一个字典,存储的时候就仅存储数字编码,降低了存储空间需求
4.利用数据聚合操作
缺点1.不适合扫描,这意味着要查询一个范围的数据
2.为加速查询会建索引,建立索引很耗时
3.不利于压缩,占用空间大
4.不适合聚合操作
1.插入和修改操作麻烦,不适合数据频繁变更的场景
2.查询完成时,被查询的列要重新进行组装
**文件格式 **Text File、Sequence File、 Avro fileRCFile、ORC File、Parquet、Arrow

数据通用压缩算法

  • Gzip
  • bzip2
  • LZO
  • LZ4
  • Snappy
  • zstd

Parquest

(一句话介绍)

Apache Parquet是一种列式存储格式,最初的目的是可供Hadoop生态系统中的任何项目使用,无论选择何种数据处理框架、数据模型或编程语言。

支持高校的压缩和编码方案。具有良好的向后兼容性。

文件布局

概念

block (hdfs块): 代表hdfs中的一个块,其含义对于Parquet这种文件格式来说是不变的。Parquet文件格式被设计为能在hdfs之上很好地工作。

File:一个hdfs文件,必须包括文件的元数据。它不需要实际包含数据。

Row group行组: 将数据横向分割成行的逻辑。对于行组来说,没有任何物理结构是可以保证的。一个行组由数据集中的每一列的列块组成。

Column chunk 列块: 一个特定列的数据块。这些数据生活在一个特定的行组中,并保证在文件中是连续的。

Page 页: 列块被划分为页。在概念上,一个页面是一个不可分割的单位(在压缩和编码方面)。在一个列块中可以有多个页面类型,它们交错排列。

在层次上,一个文件由一个或多个行组组成。一个行组中每一列正好包含一个列块。列块包含一个或多个页面。

并行处理的单元
处理框架处理粒度
MapReduceFile/Row group
IOColumn chunk
编码/压缩Page
文件逻辑结构图解

为了支持嵌套类型的数据,Parquet使用了definition层和repetition层的Dremel编码。

Definition 层指定了列的路径中有多少个可选字段被定义。

Repetition 层指定路径中的重复字段在什么地方有重复值。(Repetition 重复 重叠)

配置

Row Group Size 行组的大小

更大的行组允许更大的列块,从而可以执行更大的连续IO。较大的组也需要在写入路径上有更多的缓冲(或两次写入)。我们推荐行组大小为(512MB - 1GB)。由于整个行组可能需要被读取,我们希望它能完全容纳在一个HDFS块中。因此,HDFS块的大小也应该被设置得更大。一个优化的读取设置将是: 1GB的行组,1GB的HDFS块大小,每个HDFS文件有一个HDFS块。

❓HDFS的块的大小不是128MB或者512GB的吗?可以调整成1GB?或者是设置成128MB的倍数

Data Page Size 数据页的大小

数据页应该被认为是不可分割的(即最小的数据处理单元),所以较小的数据页允许更精细的读取(例如,单行查找)。较大的页面尺寸会产生较少的空间开销(较少的页眉)和解析开销(处理页眉)。注意:对于顺序扫描,预计不会一次读取一个页面;这不是IO块。建议页面大小为8KB

元数据

有三种类型的元数据:文件元数据(FileMetaData)、列(chunk)元数据和页眉元数据。所有的thrift结构都使用TCompactProtocol进行序列化。

数据页

对于数据页,这3个信息是在页眉之后连续编码的。我们有

  • 定义层的数据、
  • 重复层的数据、
  • 编码的值。页眉中指定的大小是所有3个部分的和。

数据页是必要存在的数据的。根据模式的定义,定义层和重复层是可选的。如果列不是嵌套的(即列的路径长度为1),我们不对重复层进行编码(它总是有1的值)。对于需要的数据,定义层被跳过(如果编码,它将总是有最大定义层的值)。

例如,在列非嵌套且是必需的情况下,页面中的数据只是编码后的值。

Hive下的Parquet实验

-- 创建专用数据库
create DATABASE if not EXISTS test_fileformat COMMENT '文件格式测试的库' WITH DBPROPERTIES ('createUser'='顾栋','date'='20230609');

-- 创建无非嵌套字段表
CREATE TABLE test_fileformat.parquet_test (
 id int,
 name string,
 d string
)
STORED AS PARQUET tblproperties ("orc.compress"="NONE");
-- 表创建完成之后 并非立即产生hdfs文件,只是创建了对于的hdfs路径

 INSERT INTO TABLE test_fileformat.parquet_test VALUES (1, '张三', '我是张三,呼叫李四'), (2, '李四', '我是李四,呼叫张三');
 INSERT INTO TABLE test_fileformat.parquet_test VALUES (3, '王五', '我是王五,呼叫赵六'),(4, '赵六', '');
 
 Drop table test_fileformat.parquet_test;
 
 -- 创建含嵌套字段表 tag是一个复杂类型 是一个struct的数组 struct里面有两个字段tagid,weight
create table test_fileformat.parquet_nested_test (uid string ,tag array<struct<tagid:string,weight:string>>)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '	'
COLLECTION ITEMS TERMINATED BY ' '
MAP KEYS TERMINATED BY ':'
STORED AS PARQUET tblproperties ("orc.compress"="NONE");

 INSERT INTO TABLE test_fileformat.parquet_nested_test select '221190xxx9',array(named_struct('tagid',cast(0.30 as string),'weight',cast(0.31 as string)),named_struct('tagid',cast(0.21 as string),'weight',cast(0.11 as string))) as tag;
 
 INSERT INTO TABLE test_fileformat.parquet_nested_test select '221190xxx9',array(named_struct('tagid',cast(0.30 as string),'weight',cast(0.31 as string)),named_struct('tagid',cast(0.21 as string),'weight',cast(0.11 as string))) as tag;
 
 Drop table test_fileformat.parquet_nested_test;
 
 DROP DATABASE test_fileformat;

文件系统

# hadoop dfs -ls -r /user/bigdata/hive/warehouse/test_fileformat.db/parque_test
-rwxr-xr-x   2 bigdata supergroup        595 2023-06-09 11:16 /user/bigdata/hive/warehouse/test_fileformat.db/parquet_test/000000_0

# hadoop dfs -ls -r /user/bigdata/hive/warehouse/test_fileformat.db/parquet_nested_test
 -rwxr-xr-x   2 bigdata supergroup        588 2023-06-09 14:46 /user/bigdata/hive/warehouse/test_fileformat.db/parquet_nested_test/000000_0_copy_1
-rwxr-xr-x   2 bigdata supergroup        588 2023-06-09 14:46 /user/bigdata/hive/warehouse/test_fileformat.db/parquet_nested_test/000000_0

Parquet简单工具的使用

根据Parquet文件的定义,每一个Parquet文件都可以单独进行读写和分析。可以使用parquet-cli工具进行简单操作。

java -cp parquet-cli-1.13.1.jar;dependency/* org.apache.parquet.cli.Main meta 000000_0
java -cp parquet-cli-1.13.1.jar;dependency/* org.apache.parquet.cli.Main schema 000000_0
java -cp parquet-cli-1.13.1.jar;dependency/* org.apache.parquet.cli.Main footer 000000_0

在这里插入图片描述

bag:袋子;一套,通过repetition:REPEATED表达字段的嵌套
在这里插入图片描述

支持的组件

  • Apache Hive
  • Apache HDFS
  • Apache Doris
  • Apache iceberg
  • Apace hudi

Apache ORC

(一句话介绍)

Apache Orc也是一种列式存储格式,最初主要是为了极大加速Apache Hive和提高存储在Apache Hadoop中的数据的存储效率,目的是支持高速处理和减小文件大小。

目前有Orc V0(伴随Hive 0.11发布)和ORC V1(伴随Hive 0.12发布)两个版本。

文件布局

文件由1个或1个以上的Stripe,一个文件页脚,一个后记组成。

文件逻辑结构图解

Orc文件的读取是反向读取,先读取文件尾部,解析出必要信息,再去读取具体数据。

文件尾部由三部分组成;

  • 文件元数据:包含stripe粒度的列统计。这些统计信息基于每个stripe评估的谓词下推启用输入拆分消除。在谓词下推的是时候会根据这些统计信息进行对stripe进行评估,来筛选数据。
  • 文件页脚:页脚部分包含文件正文的布局、类型模式信息、行数和每列的统计信息。
  • 后记(Postscript):提供解析文件其余部分的必要信息,包括文件的页脚和元数据部分的长度、文件的版本以及使用的一般压缩类型(例如 none、zlib 或 snappy)。

Stripe

文件的主体被分成stripes。 每个stripe都是独立的,可以仅使stripe被本身的字节与文件的页脚和后记相结合来读取(每个stripe可以被独立的读取)。大小一般为200+MB。

stripe包含三个部分:

  • 索引流:未加密的数据在前,加密的数据在后,行组索引由每个原始列的 ROW_INDEX 流组成,每个原始列都有一个行组条目。 行组由编写器控制,默认为 10,000 行。 每个 RowIndexEntry 给出列的每个流的位置和该行组的统计信息。因为在默认的流式传输情况下不需要读取索引。 它们仅在使用谓词下推或读取器查找特定行时加载使用。
  • 数据流:未加密的数据在前,加密的数据在后
  • stripe的页脚:包含每一列的编码和流的目录,包括它们的位置

encryptStripeId 和 encryptedLocalKeys 支持列加密。 它们设置在每个带有列加密的 ORC 文件的第一个stripe上,之后不设置。

Hive下的Parquet实验

-- 创建专用数据库
create DATABASE if not EXISTS test_fileformat COMMENT '文件格式测试的库' WITH DBPROPERTIES ('createUser'='顾栋','date'='20230609');

-- 创建无非嵌套字段表
create table test_fileformat.orc_test (
 id int,
 name string,
 d string
) stored as orc tblproperties ("orc.compress"="NONE");

 INSERT INTO TABLE test_fileformat.orc_test VALUES (1, '张三', '我是张三,呼叫李四'),(2, '李四', '我是李四,呼叫张三');
 INSERT INTO TABLE test_fileformat.orc_test VALUES (3, '王五', '我是王五,呼叫赵六'),(4, '赵六', '');
 
 Drop table test_fileformat.orc_test;
 
 -- 创建含嵌套字段表 tag是一个复杂类型 是一个struct的数组 struct里面有两个字段tagid,weight
create table test_fileformat.orc_nested_test (uid string ,tag array<struct<tagid:string,weight:string>>)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '	'
COLLECTION ITEMS TERMINATED BY ' '
MAP KEYS TERMINATED BY ':'
STORED AS orc tblproperties ("orc.compress"="NONE");

 INSERT INTO TABLE test_fileformat.orc_nested_test select '221190xxx9',array(named_struct('tagid',cast(0.30 as string),'weight',cast(0.31 as string)),named_struct('tagid',cast(0.21 as string),'weight',cast(0.11 as string))) as tag;
 
 INSERT INTO TABLE test_fileformat.orc_nested_test select '221190xxx9',array(named_struct('tagid',cast(0.30 as string),'weight',cast(0.31 as string)),named_struct('tagid',cast(0.21 as string),'weight',cast(0.11 as string))) as tag;
 
 Drop table test_fileformat.orc_nested_test;
 
 DROP DATABASE test_fileformat;

文件系统

 # hadoop dfs -ls -r /user/bigdata/hive/warehouse/test_fileformat.db/orc_test

-rwxr-xr-x   2 bigdata supergroup        502 2023-06-09 17:16 /user/bigdata/hive/warehouse/test_fileformat.db/orc_test/000000_0_copy_1
-rwxr-xr-x   2 bigdata supergroup        611 2023-06-09 17:15 /user/bigdata/hive/warehouse/test_fileformat.db/orc_test/000000_0

# hadoop dfs -ls -r /user/bigdata/hive/warehouse/test_fileformat.db/orc_nested_test

-rwxr-xr-x   2 bigdata supergroup        590 2023-06-09 17:19 /user/bigdata/hive/warehouse/test_fileformat.db/orc_nested_test/000000_0_copy_1
-rwxr-xr-x   2 bigdata supergroup        590 2023-06-09 17:19 /user/bigdata/hive/warehouse/test_fileformat.db/orc_nested_test/000000_0

ORC简单工具的使用

# 查看orc的元数据信息
java -jar orc-tools-1.8.3-uber.jar meta -p 000000_0

# 查看orc文件中的记录数
java -jar orc-tools-1.8.3-uber.jar data -n 2 000000_0

在这里插入图片描述
在这里插入图片描述

支持的组件

  • Apache Hive
  • Apache HDFS
  • Apache Doris
  • Apache iceberg
  • Apace hudi

数据存储中的编码

字典编码

位打包

增量编码

游程编码

二进制编码方式

Thrift

avro

arrow

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。