hfile, parquet, orcfile, carbondata and indexr

目录

  • OLTP和OLAP的核心区别
  • 问题的解决思路
  • Hadoop体系下的各种存储格式的简介
  • KtSQL如何做出自己的选择
  • 附录

OLTP和OLAP的核心区别

OLTP面向小量数据的操作,包含保存、读取、更新等操作。高并发的小量数据是OLTP的场景。OLAP则面向数据信息的获取,需要大批量读取所需的数据。这两者的区别,以及现有的硬件架构,决定了OLTP使用行存格式,OLAP采用列存格式。

如果是对数据建立索引,在面向OLTP场景时,从大量数据中检索出所需的数据,索引可以起到很大的加速效果。但如果是面向OLAP,大批量数据获取则对索引的依赖性大幅度降低,IO的速度变成了瓶颈,预计算、压缩存储、并行读取、并行计算等技术才是关键。这些区别,导致一种可以
同时满足OLTP和OLAP需求的存储架构难以出现。

问题的解决思路

正是因为鱼与熊掌难以兼得,所以市面上出现了不少妥协方案。如Apache Kudu、PingCap TiKV、Peloton Pax。尝试通过一个折衷方法来满足所有场景。简单点说,既然没有一种架构能够同时满足OLTP和OLAP,那么通过寻找一种两者架构的最优交集,也算是解决方法。

PingCap TiKV和Peloton Pax的解决方案可以理解,毕竟这两者是立足于OLTP的。但是Apache Kudu,一个舍弃了应用场景,尝试去取得两者折衷效果的中立方案,这种解决思路是很怪的。

既然没法解决,就要改变解决的思路。两份拷贝甚至多份拷贝并不是问题,存储的成本一直在下降,适当付出一定的存储成本,满足性能的需求,显然是另外一种思路。Apache Druid和SnappyData就是采用这样思路的解决方案。通过把热点数据放在内存中(可使用行式格式进行保存),把历史数据通过列式格式保存起来,通过查询要求的判断,找到满足查询需求的相应存储。

简单说,通过数据拷贝,形成不同的存储格式,满足不同场景的使用需求,就是解决思路的方向。

Hadoop体系下的各种存储格式的简介

业界中,Hadoop一直被当作可靠存储、数据仓库、商业智能的解决方案来使用,在OLTP场合下的场景看起来只有HBase一支独秀,且远未能成为主流。支撑Hadoop OLTP和OLAP的,是底层的各式存储格式。

因为硬件的原因,顺序写的速度比随机写的速度要快得多,所以LSM的思路被广泛使用。同时,为了提升读速度和满足随机读,采用SSD作为底层存储也是被广泛采取的技术方案。下面的文件格式设计方式,大多采用基于现代硬件的方式进行设计,所以随机读是被应用到文件结构设计中的。

hfile

hfile作为面向HBase专用的底层存储格式,信息的组织方式以keyvalue方式组织,value以行式方式组织。

hfile v1 包含以下文件存储信息(按文件从上到下次序排列):

  • Data Block,由DATABLOCKMAGIC和若干个record组成,record以keyvalue形式保存,长度不定
  • Meta Block,由METABLOCKMAGIC和Bloom Filter信息组成
  • File Info,由MapSize和若干个key/value组成,保存HFile的基本信息,如hfile.LASTKEY, hfile.AVG_KEY_LEN, hfile.AVG_VALUE_LEN, hfile.COMPARATOR
  • Data Block Index,由INDEXBLOCKMAGIC和若干个record组成,记录了data block的位置
  • Meta Block Index,由INDEXBLOCKMAGIC和若干个record组成,记录了meta block的位置
  • Fixed File Trailer,大小固定,可以根据它查找到File Info, Block Index的起始位置

hfile v1在初始化的时候,把meta block、file info、data block index、meta block index、fixed file trailer读取到内存中,方便快速查找,但需要读取的信息多、延时大,占用内存高。

hfile v2 包含以下文件存储信息(按文件从上到下次序排列):

  • scanned block section,保存了data block和leaf index block/bloom block
  • non scanned block section, 保存了meta block
  • load on open section, 保存了root data index, fields for midkey, meta index, file info, bloom filter metadata
  • trailer, 保存了trailer fields, version

hfile v2采用分层索引的方式,减少一开始初始化时的加载成本。

parquet

parquet常被用于和orcfile来对比,相比orcfile而言,parquet因为其精妙的嵌套数据格式实现而闻名,同时也带来了相对orcfile略好的压缩性能。不过从功能上看,parquet的性能和orcfile接近,但功能却不如orcfile丰富。

orcfile

orcfile是列式存储,单个orcfile由多个area组成,每个area包含:

  • header, file head info
  • body, 包含多个stripe,每个strip包含index data, row data, stripe footer. index data包含min, max和column index. stripe footer是数据的编码,文件所在位置等信息
  • footer, metadata, file footer, postscript. metadata包含stripe级别的统计信息, file footer含stripe list, column count in stripe, data type, min, max, sum等信息,postscript则是file’s Footer和Metadata sections长度, file version和压缩类型等信息

orcfile通过记录column变化历史表,记录了ACID操作历史。当读取的时候,由读取程序读出最新的数据记录。为了实现实时写入,orcfile包含以下功能:

  • additional footers,旧的footers会被作废,新的footer被写入
  • side file named “*_flush_length”,用于记录preliminary footer的位置

orcfile的更新性能不佳,建议采取批量更新的方式对数据进行操作。

carbondata

carbondata是华为提供的列式存储方案,成为了apache顶级项目,也算是开源里面的国产骄傲。

carbondata出彩的功能点:

  • 支持update & delete,整体思路和orcfile类似
  • datamap,相当于二次索引或预计算,可用于预聚合或时间序列的优化,相当于子表
  • 引入主键作为segment的划分依据

从功能上来说,carbondata比orcfile要强。如果能融合indexr的实时和离线节点设计,就更进一步了。

indexr

indexr是广州舜飞提供的数据存储方案,文件按以下格式组织:

  • IndexR 在HDFS存储的一个文件是一个Segment,一个Segment保存一个表的部分行,包含所有的列。以列式的方式组织。
  • File, 包含文件版本和SegmentMeta描述。Segment的元数据包括:行数,列数,每列的MAX和MIN值,每列的name, type,每列的各种索引的偏移量等。
  • Column, 包含DataPackNodes, PackRSIndexs, PackExtIndexs, DataPacks
    • DataPackNode是Pack元数据信息,包括索引文件的大小和偏移等;
    • PackRSIndex是Pack的Rough Set索引,RSIndex通过对Pack中列的max/min记录,可以快速判断某个值是否在某个Pack中。
    • PackExtIndex是Pack的内索引,包括equal,in, greater, between, like 5种。支持字典和bitmap两种实现方式。
    • DataPack是实际的数据,以列式存储的方法,对不同column独立处理
  • outerIndex, outerIndex是Pack级别的倒排索引,主要用于Pack之间的精准过滤。倒排索引按需创建。

indexr采取实时节点和离线节点分开的方式进行数据管理,数据一开始写入内存,并定期写入到文件中。indexr支持子表,当需要聚合结果的时候,通过sql查询的路由处理,则可以更快获得结果。

个人总结

通过上述存储格式的介绍,可以看到,一些出彩的功能点:

  • 支持update & delete,ACID
  • 支持实时节点和离线节点
  • 预计算的支持
  • 丰富使用可配置的索引机制

KtSQL如何做出自己的选择

KtSQL主要面向的场景是OLTP,所以优先满足的还是OLTP的需求。采用HBase作为底层的存储,是与这一点不违背的。KtSQL提供了二次索引的能力,对于从大范围数据中获取小量数据的场景,也是满足需求的。对于不可避免需要满足的统计式查询,HBase并不擅长。这可以通过mini cube提前聚合(参考carbondata、indexr)的方式来处理,即通过二次索引机制实现自定义cube,对KtSQL进行扩展。

采用apache kudu作为底层存储

Kudu 底层数据文件并没有存储在 HDFS 这样的分布式文件系统上,而是基于 Raft 算法实现了一套副本同步机制,保障数据不丢失及高可用性。为满足统计的需求,Kudu 的磁盘存储对字段采用列式存储,据说很多地方直接复用了 Parquet 的代码。这样的处理导致难以实现高效的更新操作。Kudu 为了提供实时写入功能,采用了在不可变的基线数据上,叠加后续更新数据的方法。

假如对HBase创建二次索引,以及因为行式存储带来的scan io成本为原来的3倍(如果太多列则远不止3倍),那么kudu对大量scan的情况,还是要远优于HBase的。不过如果更新很频繁,且更新的量很大,Kudu就会引发数据频繁的compact操作。不过对于更新操作,业务上可以绕过去。

总的来说,如果要同时满足OLTP和OLAP,Kudu是更好的选择,后续可根据发展需要增加Kudu的支持。

附录