背景
日志数据入湖中的场景多样,有做日志数据归档以低成本留存,有导出数据做BI或者报表,有的是用计算引擎进行计算分析。日志服务(SLS)集成了数据入湖投递到OSS中的功能,下面根据SLS现有的能力与用户使用的场景分析下入湖不同存储格式适合的场景。
数据格式
SLS数据投递新版目前支持JSON/CSV/ORC/Parquet四种格式。可粗略分为行存(Row-based)和列存(Columnar/Column-based)两种,行存中的JSON、CSV,可读性比较好,易用性高,生态丰富,使用场景非常广泛。相比而言,列存主要应用在OLAP中,编码和扫描方式区别如下图。相对行存在以下场景中有显著的优点:
- 方便做下推(Predicate pushdown/Projection pushdown),如果只需要几列而不是全部数据或者需要根据某些列进行裁剪,那么不需要扫描所有的列,能够加快扫描速度。
- 相同类型的数据储存在一起,可以采用适合的压缩类型(Snappy/Gzip等)和编码类型(如Fixed Length,RLE,Dictionary Coding等),能够减少占用空间,在查询时也能够显著减少IO。
下面对四种格式进行一个横向的比较。
行存格式
压缩类型
在讨论数据格式之前首先比较下不同的压缩类型,我们往往将数据编码成特定的存储格式,然后对结果数据进行压缩能够显著减少IO和存储消耗。目前SLS投递OSS支持Snappy/Gzip/Zstd三种压缩方式:
Snappy
Snappy 是谷歌基于 LZ77 算法思想编写出的压缩解压缩库,其目标不是最大的压缩率或者兼容任何其他的压缩库,而是非常快的速度和合理的压缩率。相比zlib,Snappy对于大多数的输入都要快一个数量级,但是压缩文件要大20%-100%。具有以下特性
- Fast:不依赖任何汇编代码,压缩速度快
- Stable:在Google生产环境中解压缩PB级数据;Snappy的二进制格式是稳定且不会随着版本改变而修改
- Robust:Snappy的解压器设计上不会因为处理损坏数据或者恶意输入而崩溃
- Free and open source software: Snappy是BSD-type的license
Gzip
Gzip是一种无损的数据压缩工具,其基础是deflate,是LZ77与哈夫曼编码的一个组合体,有着广泛的应用。
Zstd
Zstd,全称是Zstandard,是一个快速的无损压缩算法,目标是zlib级别的实时压缩场景和更快的压缩率,提供了非常强大的压缩速度/压缩率的折中方案。Zstd还提供了多种参数,Meta中将其用于大负载、数仓、文件系统、数据库等多种场景。这几年来,许多开源项目都已经官方支持了Zstd压缩,有的在其内部一些压缩场景默认替换为了Zstd,下面是Zstd在Meta中使用中的数据,替换原有的算法带来了很多的好处,如30%的压缩率提升或者3倍以上的压缩速度。
压缩算法比较
特点 |
|
Snappy |
以笔者实测,Snappy的压缩率表现相对于其他两种一般,但压缩速度比较快。广泛应用于Google(BigTable/MapReduce)和其他开源项目(Cassandra/Hadoop/LevelDB等),在很多大数据场景中是默认采用的压缩算法 |
Gzip |
Gzip算法的压缩率较高,应用也非常广泛,常常能在网络传输,打包压缩等场景看到gzip字样或者.gz后缀;但其更偏CPU密集型,压缩时间相对其他两种较高,实测CPU占用率比Zstd高不少 |
Zstd |
Zstd 2016年后出现,各方面表现都不错,举例来说,Parquet默认的压缩类型是Snappy,看重Snappy的压缩速度,用户也可选更高压缩率算法如Gzip,而Zstd能达到Snappy类似的速度和Gzip类似的压缩率。但是如果考虑到一些兼容性,如低版本的hadoop、低版本的Parquet/ORC的reader,可能未支持Zstd。 |
JSON类型
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人阅读和编写,同时也易于解析和生成。是语言无关的;JSON常常被用于数据交换,如web端数据交互,API,配置读取等。其也可作为文档数据库数据交互的格式。
SLS数据投递中的JSON格式确切的说是JSON Lines,每行之间以'\n'分割,每行都是一个有效的JSON字符串,示例如下
{"key1":"value1-1","key2":"value1-2"} {"key1":"value2-1","key2":"value2-2"}
可使用下面的一些方式使用投递后的数据集:
- 直接使用OSS select,支持控制台或者SDK直接查询JSON数据。
- 作为Spark/Flink的数据源;以Spark为例,通过"json"方式读取,并设置multiLine参数为true即可载入作为其数据集。
- 连接MPP引擎如Presto/Trino等做交互查询;如在Presto中使用json_extract等命令读取文件数据。
CSV
逗号分隔值(Comma-Separated Values,CSV,有时成为字符分隔值)。文件以纯文本形式存储,文件的每一行都是一个数据记录。每个记录由一个或多个字段组成,用逗号分隔。使用逗号作为字段分隔符是此文件格式的名称的来源,因为分隔字符也可以不是逗号,有时也称为字符分隔值(也有些类似格式叫做Plain或者Text格式)
key1,key2 value1-1,value2-1 value2-1,value2-2 value3-1,value3-2
SLS数据投递支持配置字段,分隔符,是否投递字段名称等配置,可自由控制,如可以配置逗号作为比较标准的CSV格式,也可以如HIVE中的TEXTFILE格式中可自行配置竖线或者制表符作为分隔符
数据文件的使用方式也是多样的,通过OSS select,批流计算引擎,SQL引擎等等,还有各种数据分析,BI,科学计算等工具都可以进行读取和处理。
列存格式
ORC
ORC源自于RCFile(RecordColumnar File),支持数据压缩、索引功能、update 操作、ACID 操作、支持复杂类型等。其格式如下;ORC中的每个Stripe为一组,一组中存放着一批数据;ORC有内建的文件/Stripe/Row级别的索引,通过索引可以决定后续的数据是否需要被读取。ORC文件的压缩不同于JSON/CSV是针对整体文件进行压缩,而是针对其特点,对每部分的列数据进行压缩;而且,除了我们常见的snappy/gzip这种压缩算法,针对其以列来组织的方式,针对不同的列类型存在着不同的编码方式,比如RLE,Dictionary Encoding等,也能达到减少存储占用的效果。
SLS数据投递支持配置Parquet文件的schema,字段名称与字段类型,支持6种字段类型string、boolean、int32、int64、float、double。设置默认的Stripe的大小是128M(与HDFS块大小一致)。
ORC被比较广泛地用于各种计算和查询引擎,如Hive/Spark/Flink/Presto等
Parquet
Parquet 是 Hadoop 生态圈中主流的列式存储格式,最早是由 Twitter 和 Cloudera 合作开发。其也具有上述描述的列存的特点,其格式如下。Parquet与ORC类似,其数据组织为多个RowGroup,可类比ORC的Stripe;在Parquet的文件Footer中存着RowGroup级别的statistic(min/max等),也能达到跳过扫描行数的效果。压缩方面和ORC类似,除压缩是针对列数据,也支持RLE,Plain Dictionary,Bit packed等方式来优化存储。
SLS数据投递支持配置Parquet文件的schema,字段名称与字段类型,支持6种字段类型string、boolean、int32、int64、float、double。Parquet的基本思想是大RowGroup增加查询效率,SLS数据投递设置RowGroup为128MB。
Parquet被比较广泛地用于各种计算和查询引擎,如Hive/Spark/Flink/Presto等。
两种列存格式其差异有几点
- Parquet格式对于复杂类型支持的更好。
- ORC有多种level的index,Parquet对Page级别的Level在较新版本才支持,导致许多Parquet文件只有RowGroup级别的statistic。
- ORC支持ACID更新,Parquet格式则无法修改,但是大数据场景下的对文件修改的场景较少,这并不是一个很痛的点。
优缺点
优点 |
缺点 |
|
JSON |
|
|
CSV |
|
|
ORC |
|
|
Parquet |
|
|
Use cases
JSON
场景1:追求简单易用,心智成本低
JSON可读性好,易用性高;数据流转起来成本也比较低,不需要担心数据生产方(日志producer)随意改动日志的字段和内容,如果一个logstore中的数据字段存在较大差异,JSON的优势会更明显;
场景2:对数据使用场景多样
JSON可以说是最广泛的格式之一,开发人员容易了解,可以说JSON贯穿在研发和数据传输的各个阶段,不止如此,即使JSON存在着存储空间增大,行存扫描速度较慢的特点,其仍是许多批处理、批处理、SQL查询引擎广泛支持的类型,还可以导入到MongoDB等文档数据库之中
CSV
场景1:追求简单易用
CSV具有和JSON相似的特点,可读性好,易用性高,在字段格式比较固定的情况下,使用CSV非常方便。
场景2:对数据使用场景多样
CSV格式也是使用最广泛的格式之一。以阿里云的产品为例,其是很多数据导出和导入支持的格式,如Mysql导出,Datav导入等;也可以作为许多批处理、批处理、SQL查询引擎、科学计算,机器学期,报表等的数据源。
ORC/Parquet
场景1:数据量非常大
当数据量非常大时,由于列存每一列往往具有固定的类型和特征,通过其Encoding和压缩方式能够大大减少内存的占用,相比CSV和JSON这是非常明显的内存优势;另一方面从扫描当你需要扫描大量数据时,JSON和CSV需要整体解压,逐行扫描,而列存格式可以跳过某些Stripe或者RowGroup,加速查询计算。
场景2:数据列非常多,但是查询和计算时往往只需要某几列
这种场景下,通过列存同样需要扫描大量的数据,而列存可以只扫描特定的列,大大加快了速度。
场景3:格式开放,数据需要多处使用
Snowflake有其独有的列式格式,相比起来ORC和Parquet是开放的格式,更适合数据的流动。举例来说:投递在OSS上列式格式可以被多种处理和分析的工具读取(E-Mapreduce,DLA,自建lakehouse等),不止存储和计算分离,也做到了“存储格式和计算分离”。
场景4:ORC or Parquet?如何选择
ORC源于RC file,出现较早,Hive和Presto之前支持的较好,Presto还有专门的ORC reader;目前基本上支持ORC的都能够支持Parquet,而如Spark的场景中Parquet支持更加完善,其数据湖lakehouse delta lake,底层格式只支持了Parquet,笔者看来,Parquet事实上更接近大数据时代的数据标准,所以如果不是历史原因或者需要ORC的特性,建议优先选择Parquet。