1 背景
Cloudera 在 2016 年发布了新型的分布式存储系统—— kudu,kudu 目前也是 apache 下面的开源项目。Hadoop 生态圈中的技术繁多,HDFS 作为底层数据存储的地位一直很牢固。而 HBase 作为 Google BigTable 的开源产品,一直也是 Hadoop 生态圈中的核心组件,其数据存储的底层采用了 HDFS,主要解决的是在超大数据集场景下的随机读写和更新的问题。Kudu 的设计有参考 HBase 的结构,也能够实现 HBase 擅长的快速的随机读写、更新功能。
那么同为分布式存储系统,HBase 和 Kudu 二者有何差异?两者的定位是否相同?
接下来,土哥通过分析 HBase 与 Kudu 整体结构和存储结构等方面对两者的差异进行比较。
2 整体结构
2.1 HBase的整体结构
图片
HBase的主要组件包括 Master,zookeeper 服务,RegionServer,HDFS。
Master:用来管理与监控所有的HRegionServer,也是管理HBase元数据的模块。
zookeeper:作为分布式协调服务,用于保存meta表的位置,master的位置,存储RS当前的工作状态。
RegionServer:负责维护Master分配的region,region对应着表中一段区间内的内容,直接接受客户端传来的读写请求。
HDFS:负责最终将写入的数据持久化,并通过多副本复制实现数据的高可靠性。
2.2 Kudu的整体结构
图片
Kudu 集群中存在两种主要组件:
(1)TServer,负责管理Tablet,tablet是负责一张表中某块内容的读写,接收其他TServer中leader tablet传来的同步信息。
(2)Master,集群中的管理节点,用于管理tablet的基本信息,表的信息,并监听TServer的状态。多个Master之间通过Raft协议实现数据同步和高可用。
2.3 主要区别
Kudu结构看上去跟HBase差别并不大,主要的区别包括:
- Kudu将HBase中zookeeper的功能放进了Master内,Kudu中Master的功能比HBase中的Master任务要多一些。
- Hbase将数据持久化这部分的功能交给了Hadoop中的HDFS,最终组织的数据存储在HDFS上。Kudu 自己将存储模块集成在自己的结构中,内部的数据存储模块通过 Raft 协议来保证 leader Tablet 和 replica Tablet 内数据的强一致性,和数据的高可靠性。为什么不像 HBase 一样,利用 HDFS 来实现数据存储,猜测可能是因为 HDFS 读小文件时的时延太大,所以 Kudu 自己重新完成了底层的数据存储模块,并将其集成在 TServer 中。
3 数据存储方式
3.1 HBase
HBase是一款Nosql数据库,典型的KV系统,没有固定的schema模式,建表时只需指定一个或多个列族名即可,一个列族下面可以增加任意个列限定名。一个列限定名代表了实际中的一列,HBase将同一个列族下面的所有列存储在一起,所以HBase是一种面向列族式的数据库。
图片
HBase 将每个列族中的数据分别存储,一个列族中的每行数据中,将 rowkey、列族名、列名、timestamp 组成最终存取的key值,另外为了支持修改,删除,增加了一个表征该行数据是否删除的标记。
在同一个列族中的所有数据,按照 rowkey:columnfamily:columnQulifier:timestamp 组成的 key 值大小进行升序排列,其中 rowkey、columnfamily、columnQulifier 采用的是字典顺序,其值越大,key 越大,而 timestamp 是值越大,key 越小。HBase 通过按照列族分开存储,相对于行式存储能够实现更高的压缩比,这也是其比较重要的一个特性。
HBase 对一行数据进行更新时,HBase也是相当于插入一行新数据,在读数据时HBase按照 timestamp 的大小得到经过更新过的最新数据。
图片
3.2 Kudu
Kudu是一种完全的列式存储引擎,表中的每一列数据都是存放在一起,列与列之间都是分开的。
图片
为了能够保存一部分历史数据,并实现 MVCC,Kudu 将数据分为三个部分。
- 第一部分叫做 base data,是当前的数据;
- 第二个部分叫做 UNDO records,存储的是从插入数据时到形成 base data 所进行的所有修改操作,修改操作以一定形式进行组织,实现快速查看历史数据;
- 第三个部分是 REDO records,存储的是还未 merge 到当前数据中的更新操作。下图中表示的是在 Kudu 中插入一条数据、更新数据两个操作的做法,当然做法不唯一,不唯一的原因是Kudu可以选择先不将更新操作合并到 base data 中。
图片
3.3 差异分析
(1)HBase 是面向列族式的存储,每个列族都是分别存放的,HBase 表设计时,很少使用设计多个列族,大多情况下是一个列族。这个时候的 HBase 的存储结构已经与行式存储无太大差别了。而 Kudu,实现的是一个真正的面向列的存储方式,表中的每一列都是单独存放的;所以 HBase 与 Kudu 的差异主要在于类似于行式存储的列族式存储方式与典型的面向列式的存储方式的差异。
(2)HBase 是一款 NoSQL 类型的数据库,对表的设计主要在于 rowkey 与列族的设计,列的类型可以不指定,因为 HBase 在实际存储中都会将所有的 value 字段转换成二进制的字节流。因为不需要指定类型,所以在插入数据的时候可以任意指定列名(列限定名),这样相当于可以在建表之后动态改变表的结构。
Kudu 因为选择了列式存储,为了更好的提高列式存储的效果,Kudu 要求在建表时指定每一列的类型,这样的做法是为了根据每一列的类型设置合适的编码方式,实现更高的数据压缩比,进而降低数据读入时的 IO 压力。
(3)HBase 对每一个 cell 数据中加入了 timestamp 字段,这样能够实现记录同一 rowkey 和列名的多版本数据,另外 HBase 将数据更新操作、删除操作也是作为一条数据写入,通过 timestamp 来标记更新时间,type 来区分数据是插入、更新还是删除。HBase 写入或者更新数据时可以指定 timestamp,这样的设置可以完成某些特定的操作。
Kudu 也在数据存储中加入了 timestamp 这个字段,不像 HBase 可以直接在插入或者更新数据时设置特殊的 timestamp 值,Kudu 的做法是由 Kudu 内部来控制timestamp 的写入。不过 Kudu 允许在 scan 的时候设置 timestamp 参数,使得客户端可以 scan 到历史数据。
(4)相对于 HBase 允许多版本的数据存在,Kudu 为了提高批量读取数据时的效率,要求设计表时提供一列或者多列组成一个主键,主键唯一,不允许多个相同主键的数据存在。这样的设置下,Kudu 不能像 HBase 一样将更新操作直接转换成插入一条新版本的数据,Kudu 的选择是将写入的数据,更新操作分开存储。
(5)当然还有一些其他的行式存储与列式存储之间在不同应用场景下的性能差异。
4 总结
这里主要对 Kudu和 HBase 在整体结构,数据存储结构方面进行分析和比较。
Kudu 通过完整的表结构设置,主键的设定,以列式存储作为数据在磁盘上的组织方式,更新和数据分开等技巧,使得 Kudu 能够实现像HBase一样实现数据的随机读写之外,在 HBas不太擅长的批量数据扫描(scan)具有较好的性能。