彻底理解大数据的列式存储
Posted 明哥的IT随笔
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了彻底理解大数据的列式存储相关的知识,希望对你有一定的参考价值。
一篇文章彻底理解大数据的列式存储
大家好,我是明哥!
本片文章,跟大家一起探讨下,列式存储与数据质量的相关话题。
1. 什么是列式存储
-
所谓行式存储,指存储结构化数据时,在底层的存储介质上,数据是以行的方式来组织的,即存储完一条记录的所有字段,再存储下一条数据的所以字段,以此类推;
-
所谓列式存储,指存储结构化数据时,在底层的存储介质上,数据时以列的方式来组织的,即存储完若干条记录的首个字段后,再存储这些记录的第二个字段,然后是这些记录的第三个字段,以此类推,当这些记录的所有字段存储完毕后,再按照这种方式,组织存储下一批若干条记录的所有字段;
比如需要存储以下记录:
姓名 | 年龄 | 工资 |
---|---|---|
小张 | 18 | 10000 |
小王 | 19 | 11000 |
小李 | 20 | 12000 |
小赵 | 21 | 13000 |
小周 | 22 | 14000 |
小吴 | 23 | 15000 |
小郑 | 24 | 16000 |
小杨 | 25 | 17000 |
则行存储格式,底层是这样组织的:(示意图)
而列存储格式,底层是这样组织的:(示意图)
说明:
- 以上只是示意图,实际存储时,往往还会存储一些元数据,比如校验信息,统计信息等等;(列式存储往往会存储更多的元数据信息,以便于检索/查询/统计等)
- 行存储与列存储,存储的都是结构化数据;(非结构化数据,就无所谓行和列了)
- 行存储和列存储,描述的是底层存储介质上,数据的组织形式,而存储介质可以是磁盘,也可以是内存;存储介质的上次建筑,可以是文件系统,也可以是对象存储;
- 很多数据库,在底层即支持行存储,也支持列存储,这两者有时是同时混合使用的;(比如 oracle12c,就引入了对 In-Memory Column Store 的支持;再比如 TiDB, 底层同时支持行存储引擎 TiKV 和 列存储引擎 TiFlash);
2. 深入了解列式存储 - 以 Parquet 为例
说到当下最流行的开源列式存储引擎,就非 apache parquet 莫属了。
- 绝大多数计算引擎,比如 apache spark/presto/impala/flink, 都将 parquet 作为首选的列示存储引擎;
- orc 是 apache hive 社区推出的,另一款开源列示存储引擎;
- apache hive 在早期只支持 orc 列示存储的基础上,有扩展了对 parquet 的支持;
有鉴于此,我们有必要以 parquet 为例,来深入了解下列示存储。
以下是 parquet 底层,数据的组织格式:
相关述语解释如下:
-
Block (hdfs block): 指 hdfs 文件系统的 block,parquet 是在 hdfs 文件之上的数据组织格式;(当然现在很多对象存储系统,比如 S3,也支持 parquet 存储格式);
-
File: 指 hdfs 文件,使用 parquet 格式时,每个 hdfs 文件底层必须包括 parquet 元数据 - 事实上该文件底层可以不包含数据,但必须包含元数据;(当然现在很多对象存储系统,比如 S3,也支持 parquet 存储格式);
-
Row group: 文件底层的所有数据,在逻辑上被水平切割,每个 row group 存储的都是一份这些被水平切割后的数据。(row group 是对数据的水平切,分比如上图中,就显示了 row group 0 和 row group1 两个 row group);
-
Column chunk: 每个 row group 底层都包含一系列 column chunk,每个colum 都有一个对应的 column chunk;(数据有多少列,水平切割的每个 row group底层,就有多少个 column chunk);
-
Page: Column chunks 被进一步切分为若干个 page 页,page 是压缩和编码的最小单位;(比如上如,row group0 的 Column a,就显示了两个 page: page a 和 page b);
概括如下:
- 每个文件由一个或多个 row group 构成,每个 row group 包含了多个 column chunk (column chunk 和 column 是 一一对应的),Column chunks 包含了一个或多个 page;
- MR 等计算作业,其并行操作的单位是 File/Row Group;
- IO 的单位是 Column chunk;
- 编码和压缩的单位是 Page;
3. 行式存储与列式存储的优劣势都有哪些
通过上述对底层数据组织格式的对比,不难发现,行存储有以下特点:
- 行存储将每条数据的所有列连续存储在一起,一条记录接着一条记录;
- 行存储中数据写入的成本较低,适合数据有频繁更新的场景;
- 通过使用索引,能大幅提高行存储的数据查询速度;
- 行存储是传统的数据组织形式,更适合传统的 OLTP 系统;(OLTP 数据库表的设计强调范式,底层一般有多张有关联关系的窄表)
而列存储有以下特点:
- 列存储将多行记录的列连续存储在一起,一列接着一列;
- 由于连续存储在一起的列的数据类型都一样,所以数据压缩率更高,更省存储空间;
- 列存储中数据查询的成本较低,特别适合分析时只查询部分列的场景,因为不需要扫描/读取不需要查询的列;
- 列存储由于数据更新成本较高,一般适合读多写少的场景;(但是不代表不能更新!)
- 列存储是新型数据组织形式,更适合 OLAP 分析型系统;(OLAP数据库表的设计强调反范式,底层一般是星型模式的若干张事实表和维度表,倾向使用大宽表)
4. 列式存储与数据质量
各厂各司在其数据仓库或数据湖泊的数据规范部分,大都强调要尽可能地使用列式存储格式,而尽可能不要使用行式存储格式。
究其原因,一方面是列式存储相比行式存储,其压缩率更高读写效率更快;另一方面是因为,相比行式存储,列式存储的数据质量更高!
之所以说列示存储能提高数据质量,更准确地说,是有些行式存储会降低数据质量,比如 csv/tsv 等 TextFile。
比如,以我们熟悉的 HIVE 为例,一个常见的 DDL 语句如下:
CREATE TABLE student_text_test(
id double,
name string,
sex double,
age double,
class double,
address string)
ROW FORMAT DELIMITED FIELDS TERMINATED BY ',' LINES TERMINATED BY '\\n'
STORED AS TEXTFILE;
- 大家可以看到,TextFile 的 DDL 语句中,需要通过 xx terminated by xx 来指定列分隔符和行分隔符(列分隔符默认是’\\001’,行分隔符默认是’\\n’);
- 而上游rdbms表中某些字段,可能会包含特殊字符比如换行符,此时就可能会跟上述textfile 表的 DDL 语句中指定的列分隔符或分隔符冲突;
- 后续通过数据同步工具比如 sqoop/datax/spark sql 等同步上游数据到 hive中 textfile 格式的表时,同步作业可以执行成功;
- 但是后续再查询该同步的 textfile 格式的表时,因为大数据schema on read 的特性,在读数据时按就会按照 DDL 中指定的行和列分隔符来切割数据,此时就可能会因为行分隔符造成数据切割混乱,从而查出大量 NULL 字段的错乱数据;
- 不幸的是,上游 RDBMS 中的数据往往不受大数据部门管控,会出现各种各样的奇怪数据;
- 更不幸的是,目前hive的textfile只支持\\n作为行分隔符(验证了cdh6.3,也就是hive2.1)
所以,正式出于数据质量的原因,各厂各司在其数据仓库或数据湖泊的数据规范部分,大都强调:
- 要尽可能地使用列式存储格式比如orc/parquet,而尽可能不要使用行式存储格式textfile;
- 数仓中各层,包括 ods/dwd/dws/ads ,以及业务临时表与技术临时表,都尽量不要使用textfile;
- 除非是选用的数据同步工具不支持orc/parquet(比如sqoop),且此时也仅仅只能在 ods层的落地表中使用textfile, 后面 dws等各层需要切换使用列示存储 orc/parquet;
- 必要时,对数据同步工具重新进行技术选型,比如抛弃不支持列示存储的 sqoop, 而选用支持列示存储的 spark sql/datax 等。
以上是关于彻底理解大数据的列式存储的主要内容,如果未能解决你的问题,请参考以下文章