关于KUDU

时间:2024-04-09 22:21:24

Kudu是什么?有什么特性?它和Hadoop生态的关系是什么?有了HDFS和HBase,为什么还要用kudu?

目录

 

Kudu产生的背景

Kudu是什么

kudu的使用场景

Kudu设计优势

kudu使用时的优势

kudu使用时的劣势

kudu基本原理

表与schema

kudu的底层数据模型

Tablet的发现过程

kudu的写流程

kudu的读流程

 kudu的更新流程

Kudu在使用过程中的各种限制 

有了HBase为什么还要Kudu?

使用注意点(踩坑记录)

如何入门和实战


Kudu产生的背景

在KUDU之前,大数据主要以两种方式存储;

(1)静态数据:

以 HDFS 引擎作为存储引擎,适用于高吞吐量的离线大数据分析场景。这类存储的局限性是数据无法进行随机的读写。

(2)动态数据:

以 HBase、Cassandra 作为存储引擎,适用于大数据随机读写场景。这类存储的局限性是批量读取吞吐量远不如 HDFS,不适用于批量数据分析的场景。

这两种数据在存储方式上完全不同,导致使用场景完全不同。在真实的场景中,面对既需要随机读写,又需要批量分析的大数据场景,该如何选择呢?显然单种存储引擎无法满足业务需求,我们需要通过多种大数据工具组合来满足这一需求。

例如,Online Report采用HDFS/Parquet on Impala的架构,数据每隔一小时通过MapReduce从生产db增量同步到HDFS,再通过HIVE/MAPREDUCE增量MERGE到数仓中,最终通过IMPALA查询在报表展示。类似这样:

关于KUDU

如图所示,数据实时写入 HBase,实时的数据更新也在 HBase 完成,为了应对 OLAP 需求,我们定时(通常是 T+1 或者 T+H)将 HBase 数据写成静态的文件(如:Parquet)导入到 OLAP 引擎(如:HDFS)。这一架构能满足既需要随机读写,又可以支持 OLAP 分析的场景,但他有如下缺点:

(1)架构复杂。从架构上看,数据在HBase、消息队列、HDFS 间流转,涉及环节太多,运维成本很高。并且每个环节需要保证高可用,都需要维护多个副本,存储空间也有一定的浪费。最后数据在多个系统上,对数据安全策略、监控等都提出了挑战。

(2)时效性低。数据从HBase导出成静态文件是周期性的,一般这个周期是一天(或一小时),在时效性上不是很高。

(3)难以应对后续的更新。真实场景中,总会有数据是延迟到达的。如果这些数据之前已经从HBase导出到HDFS,新到的变更数据就难以处理了,一个方案是把原有数据应用上新的变更后重写一遍,但这代价又很高。

这种架构可以支持超大数据集的查询和分析,但是数据更新执行过程很久,效率较差。而Apache Kudu结合了Hbase和HDFS的优势,可以同时提供高效的随机访问以及数据扫描能力,支持数据的实时插入和分析,为Online Report提供了另外一种选择。

Kudu是什么

KUDU的定位是Fast Analytics on Fast Data,把analytics 和 online两个应用场景进行了整合,是一个既支持随机读写、又支持 OLAP 分析的大数据存储引擎。

关于KUDU

Kudu---This new open source complement to HDFS and Apache HBase is designed to fill gaps in Hadoop’s storage layer that have given rise to stitched-together, hybrid architectures.

(一个弥补HDFS和HBase之间的缺口的新型的存储,它能够更有效的利用现代硬件的CPU和IO资源,既能够支持分析,又能够支持更新、删除和实时查询。)

可以看出,KUDU 是一个折中的产品,在 HDFS 和 HBase 这两个偏科生中平衡了随机读写和批量分析的性能。

Kudu集HDFS的顺序读和HBASE的随机读于一身,同时具备高性能的随机写,以及很强大的可用性(单行事务,一致性协议),支持Impala spark计算引擎。是一个融合HDFS和HBase的功能的新组件,具备介于两者之间的新存储组件。可以同时提供低延迟的随机读写和高效的数据分析能力,支持水平扩展,高可用,与Cloudera Impala和Apache Spark等当前流行的大数据查询和分析工具结合紧密。

 

kudu的使用场景

官方表述:

  • Strong performance for both scan and random access to help customers simplify complex hybrid architectures(适用于那些既有随机访问,也有批量数据扫描的复合场景)
  • High CPU efficiency in order to maximize the return on investment that our customers are making in modern processors(高计算量的场景)
  • High IO efficiency in order to leverage modern persistent storage(使用了高性能的存储设备,包括使用更多的内存)
  • The ability to update data in place, to avoid extraneous processing and data movement(支持数据更新,避免数据反复迁移)
  • The ability to support active-active replicated clusters that span multiple data centers in geographically distant locations(支持跨地域的实时数据备份和查询)

  更贴近业务的适用场景解读:

  • Streaming Input with Near Real Time Availability(具有近实时可用性的流输入)

       刚刚到达的数据就马上要被终端用户使用访问到。

       数据分析中的一个共同挑战就是新数据快速而不断地到达,同样的数据需要靠近实时的读取,扫描和更新。Kudu 通过高效的列式扫描提供了快速插入和更新的强大组合,从而在单个存储层上实现了实时分析用例。

  • Time-series application with widely varying access patterns(具有广泛变化的访问模式的时间序列应用)

              需要同时支持:

    根据海量历史数据查询。
    必须非常快地返回关于单个实体的细粒度查询。

              time-series(时间序列)模式是根据其发生时间组织和键入数据点的模式。这可以用于随着时间的推移调查指标的性能,或者根据过去的数据尝试预测未来的行为。例如,时间序列的客户数据可以用于存储购买点击流历史并预测未来的购买,或由客户支持代表使用。虽然这些不同类型的分析正在发生,插入和更换也可能单独和批量地发生,并且立即可用于读取工作负载。Kudu 可以用 scalable (可扩展)和 efficient (高效的)方式同时处理所有这些访问模式。由于一些原因,Kudu 非常适合时间序列的工作负载。随着 Kudu 对基于 hash 的分区的支持,结合其对复合 row keys(行键)的本地支持,将许多服务器上的表设置成很简单,而不会在使用范围分区时通常观察到“hotspotting(热点)”的风险。Kudu 的列式存储引擎在这种情况下也是有益的,因为许多时间序列工作负载只读取了几列,而不是整行。 过去,您可能需要使用多个数据存储来处理不同的数据访问模式。这种做法增加了应用程序和操作的复杂性,并重复了数据,使所需存储量增加了一倍(或更糟)。Kudu 可以本地和高效地处理所有这些访问模式,而无需将工作卸载到其他数据存储。

  • Predictive Modeling(预测建模)

支持根据所有历史数据周期地更新模型。

数据科学家经常从大量数据中开发预测学习模型。模型和数据可能需要在学习发生时或随着建模情况的变化而经常更新或修改。此外,科学家可能想改变模型中的一个或多个因素,看看随着时间的推移会发生什么。在 HDFS 中更新存储在文件中的大量数据是资源密集型的,因为每个文件需要被完全重写。在 Kudu,更新发生在近乎实时。科学家可以调整值,重新运行查询,并以秒或分钟而不是几小时或几天刷新图形。此外,批处理或增量算法可以随时在数据上运行,具有接近实时的结果。

  • Combining Data In Kudu With Legacy Systems(结合 Kudu 与遗留系统的数据)

  公司从多个来源生成数据并将其存储在各种系统和格式中。例如,您的一些数据可能存储在 Kudu,一些在传统的 RDBMS 中,一些在 HDFS 中的文件中。您可以使用 Impala 访问和查询所有这些源和格式,而无需更改旧版系统。

  有关这些和其他方案的更多信息,请参阅 Example Use Cases。

      国内使用的kudu一些案例可以查看《构建近实时分析系统.pdf》文档。

 

Kudu设计优势

  • OLAP工作流的快速处理

    能够快速更新/获取新数据,并进行在线查询分析

  • 列式存储结构
  • 集成Hadoop生态组件

    Spark/Flume/MapReduce/Impala等

  • 服务的高可用性

    TabletServer以及Master都使用*Raft*一致性算法。该算法确保只要可用副本数为总数的一半以上,Tablet就可以进行读写操作。即使在leader发生故障的情况下,follower也可以为程序提供只读服务。

  • 结构化数据模型

    类关系型数据库,强字段类型,主键索引,表分区设计

  • NOSQL APIs

    支持 C++, Java and Python (INSERT/UPDATE/SCAN/DELETE等)

kudu使用时的优势

1)一个table由多个tablet组成,对分区查看、扩容和数据高可用支持非常好
2)支持update和upsert操作。
3)与imapla集成或spark集成后(dataframe)可通过标准的sql操作,使用起来很方便
4)可与spark系统集成

kudu使用时的劣势

1)只有主键可以设置range分区,且只能由一个主键,也就是一个表只能有一个字段range分区,且该字段必须是主键。
2)如果是pyspark连接kudu,则不能对kudu进行额外的操作;而scala的spark可以调用kudu本身的库,支持kudu的各种语法。
3)kudu的shell客户端不提供表schema查看。如果你不通过imapla连接kudu,且想要查看表的元数据信息,需要用spark加载数据为dataframe,通过查看dataframe的schema查看表的元数据信息。
3)kudu的shell客户端不提供表内容查看。如果你想要表的据信息,要么自己写脚本,要么通过spark、imapla查看。
4)如果使用range 分区需要手动添加分区。假设id为分区字段,需要手动设置第一个分区为1-30.第二个分区为30-60等等
5)时间格式是utc类型,需要将时间戳转化为utc类型,注意8个小时时差。

 

kudu基本原理

表与schema

Kudu设计是面向结构化存储的,因此,Kudu的表需要用户在建表时定义它的Schema信息,这些Schema信息包含:列定义(含类型),Primary Key定义(用户指定的若干个列的有序组合)。数据的唯一性,依赖于用户所提供的Primary Key中的Column组合的值的唯一性。 Kudu提供了Alter命令来增删列,但位于Primary Key中的列是不允许删除的。 Kudu当前并不支持二级索引。 从用户角度来看,Kudu是一种存储结构化数据表的存储系统。在一个Kudu集群中可以定义任意数量的table,每个table都需要预先定义好schema。每个table的列数是确定的,每一列都需要有名字和类型,每个表中可以把其中一列或多列定义为主键。这么看来,Kudu更像关系型数据库,而不是像HBase、Cassandra和MongoDB这些NoSQL数据库。不过Kudu目前还不能像关系型数据一样支持二级索引。

Kudu使用确定的列类型,而不是类似于NoSQL的"everything is byte"。这可以带来两点好处: 确定的列类型使Kudu可以进行类型特有的编码。 可以提供 SQL-like 元数据给其他上层查询工具,比如BI工具。

kudu的底层数据模型

Kudu的底层数据文件的存储,未采用HDFS这样的较高抽象层次的分布式文件系统,而是自行开发了一套可基于

Table/Tablet/Replica视图级别的底层存储系统。

这套实现基于如下的几个设计目标:

• 可提供快速的列式查询

• 可支持快速的随机更新

• 可提供更为稳定的查询性能保障

关于KUDU

一张表会分成若干个tablet,每个tablet包括MetaData元信息及若干个RowSet,RowSet包含一个MemRowSet及若干个DiskRowSet,DiskRowSet中包含一个BloomFile、Ad_hoc Index、BaseData、DeltaMem及若干个RedoFile和UndoFile(UndoFile一般情况下只有一个)。

MemRowSet:用于新数据insert及已在MemRowSet中的数据的更新,一个MemRowSet写满后会将数据刷到磁盘形成若干个DiskRowSet。每次到达32M生成一个DiskRowSet。

DiskRowSet:用于老数据的变更(mutation),后台定期对DiskRowSet做compaction,以删除没用的数据及合并历史数据,减少查询过程中的IO开销。

BloomFile:根据一个DiskRowSet中的key生成一个bloom filter,用于快速模糊定位某个key是否在DiskRowSet中存在。

Ad_hocIndex:是主键的索引,用于定位key在DiskRowSet中的具体哪个偏移位置。

BaseData是MemRowSet flush下来的数据,按列存储,按主键有序。

UndoFile是基于BaseData之前时间的历史数据,通过在BaseData上apply UndoFile中的记录,可以获得历史数据。

RedoFile是基于BaseData之后时间的变更(mutation)记录,通过在BaseData上apply RedoFile中的记录,可获得较新的数据。

DeltaMem用于DiskRowSet中数据的变更mutation,先写到内存中,写满后flush到磁盘形成RedoFile。

 

MemRowSets可以对比理解成HBase中的MemStore, 而DiskRowSets可理解成HBase中的HFile。MemRowSets中的数据按照行试图进行存储,数据结构为B-Tree。

MemRowSets中的数据被Flush到磁盘之后,形成DiskRowSets。

DisRowSets中的数据,按照32MB大小为单位,按序划分为一个个的DiskRowSet。 DiskRowSet中的数据按照Column进行组织,与Parquet类似。

这是Kudu可支持一些分析性查询的基础。每一个Column的数据被存储在一个相邻的数据区域,而这个数据区域进一步被细分成一个个的小的Page单元,与HBase File中的Block类似,对每一个Column Page可采用一些Encoding算法,以及一些通用的Compression算法。 既然可对Column Page可采用Encoding以及Compression算法,那么,对单条记录的更改就会比较困难了。

前面提到了Kudu可支持单条记录级别的更新/删除,是如何做到的?

   

与HBase类似,也是通过增加一条新的记录来描述这次更新/删除操作的。DiskRowSet是不可修改了,那么 KUDU 要如何应对数据的更新呢?在KUDU中,把DiskRowSet分为了两部分:base data、delta stores。base data 负责存储基础数据,delta stores负责存储 base data 中的变更数据.

关于KUDU

如上图所示,数据从 MemRowSet 刷到磁盘后就形成了一份 DiskRowSet(只包含 base data),每份 DiskRowSet 在内存中都会有一个对应的DeltaMemStore,负责记录此 DiskRowSet 后续的数据变更(更新、删除)。DeltaMemStore 内部维护一个 B-树索引,映射到每个 row_offset 对应的数据变更。DeltaMemStore 数据增长到一定程度后转化成二进制文件存储到磁盘,形成一个 DeltaFile,随着 base data 对应数据的不断变更,DeltaFile 逐渐增长。

Tablet的发现过程

当创建Kudu客户端时,其会从主服务器上获取tablet位置信息,然后直接与服务于该tablet的服务器进行交谈。

为了优化读取和写入路径,客户端将保留该信息的本地缓存,以防止他们在每个请求时需要查询主机的tablet位置信息。

随着时间的推移,客户端的缓存可能会变得过时,并且当写入被发送到不再是tablet领导者的tablet服务器时,则将被拒绝。然后客户端将通过查询主服务器发现新领导者的位置来更新其缓存。

关于KUDU

kudu的写流程

关于KUDU

如上图,当 Client 请求写数据时,先根据主键从Master Server中获取要访问的目标 Tablets,然后到依次对应的Tablet获取数据。

因为KUDU表存在主键约束,所以需要进行主键是否已经存在的判断,这里就涉及到之前说的索引结构对读写的优化了。一个Tablet中存在很多个RowSets,为了提升性能,我们要尽可能地减少要扫描的RowSets数量。

首先,我们先通过每个 RowSet 中记录的主键的(最大最小)范围,过滤掉一批不存在目标主键的RowSets,然后在根据RowSet中的布隆过滤器,过滤掉确定不存在目标主键的 RowSets,最后再通过RowSets中的 B-树索引,精确定位目标主键是否存在。

如果主键已经存在,则报错(主键重复),否则就进行写数据(写 MemRowSet)。

kudu的读流程

关于KUDU

如上图,数据读取过程大致如下:先根据要扫描数据的主键范围,定位到目标的Tablets,然后读取Tablets 中的RowSets。

在读取每个RowSet时,先根据主键过滤要scan范围,然后加载范围内的base data,再找到对应的delta stores,应用所有变更,最后union上MemRowSet中的内容,返回数据给Client。

 kudu的更新流程

关于KUDU

数据更新的核心是定位到待更新数据的位置,这块与写入的时候类似,就不展开了,等定位到具体位置后,然后将变更写到对应的delta store 中。


 

Kudu在使用过程中的各种限制 

主键

  • 表创建后,主键不能修改。必须删除重建表指定新的主键。
  • 主键列必须在非主键列之前
  • 主键列的值不能使用UPDATE函数修改。如果要修改主键的值只能删除该行重新插入。
  • DOUBLE、FLOAT或BOOL类型的列不能作为主键,此外,主键列必须为NOT NULL。
  • 不支持自动生成的主键(如自增列)
  • 组合主键的所有列在编码后,大小不能大于16K

  • 不支持CHAR、VARCHAR、DATE和数组等复杂类型。
  • 不能通过ALTER TABLE更改现有列的类型和是否可为空属性
  • DECIMAL类型列的精度和规模不能通过ALTER TABLE进行修改(Kudu 1.7+才支持decimal类型)
  • 表最多可以有300列。

  • 表的副本数必须为奇数,最多为7。
  • 副本数在建表时指定,之后无法更改。

单元格

  • 单个单元格的值在编码或压缩前不能大于64KB。

其他使用限制

  • kudu主要是为分析用例设计的,如果单行中包含多个千字节的单元格,则可能会遇到问题。
  • 不支持二级索引。
  • 不支持多行事物。
  • 不支持外键等关系特性。
  • 表名和列名必须为有效的UTF-8字符串,最大长度为256个字符。
  • 删除列并不会立即释放空间,compaction运行后才会释放。
  • 无法手动执行compaction,但是删表会立即释放空间。

分区限制

  • 表必须使用简单或复合主键手动分区。目前不支持自动分区。
  • Range 分区可以在建表后添加或删除。
  • 表中已有的数据无法重新自动分区。有一种解决方案,创建一个新表并制定新的分区,然后将旧表的数据插入新表。
  • 失去大部分副本的tablet(比如三个副本中的一个)需要人工干预才能恢复。

集群管理

  • 不支持机架感知。
  • 不支持多数据中心。
  • 不支持滚动重启。

 

服务器管理

  • 生产部署时应该为tablet server分配至少4G内存,在接近数据和tablet的限制时,最好配置大于16G的内存。
  • 预写日志(WAL)只能存储在磁盘上。
  • 磁盘故障是无法容忍的,一旦检测到磁盘故障,tablet server就会崩溃。
  • 无法恢复数据的磁盘需要格式化该tablet server的所有Kudu数据,然后才能重新启动。
  • 无法添加或删除数据目录,必须对所有目录进行重新格式化以更改目录集。
  • Tablet servers不能优雅地退役。
  • 不能更改tablet servers的地址和端口。
  • Kudu对时钟同步有严格的要求,一旦时钟不同步,Kudu的master和tablet server就会崩溃。
  • Kudu发行版只在NTP中进行过测试,其他时钟同步程序不保证能正常工作。

 

规模

  • 建议tablet server最多为100台。
  • 建议master最多为3个。
  • 建议的最大数据存储量为,复制和压缩后,每个tablet server 8TB。
  • 建议每个tablet server管理的tablet为2000,包含tablet的副本。
  • 在创建表时,每个Tablet server的每个表的最大tablet数为60。
    基于以上限制,可以推测出一下内容:
    Kudu中存储的总数据量建议为:tablet server总数
    单个tablet server的数据量=100*8TB=800TB,即Kudu不适用于存储PB级数据。
    单个tablet的数据量建议为:单个tablet server的数据量/每个tablet server中tablet的总数=8TB/2000=4G。Kudu支持的压缩方式有LZ4, Snappy,或zlib。鉴于各种压缩算法的压缩比一般不超过50%,则每个tablet中的数据量在压缩前的大小建议应小于2G。

复制和备份

  • kudu目前没有任何内置备份和恢复功能。建议用户在必要的时候使用spark或impala等工具导入或导出表。

 

其他已知的问题

  • 如果Kudu master配置了-log_force_fsync_all选项、 tablet servers和客户端将经常超时,集群可能变得不可用。
  • 如果一个tablet server 有大量的tablet,启动可能需要几分钟。建议将每台服务器的tablet数量限制在100或更少。在预拆分表时,请考虑这个限制。如果您注意到启动时间较慢,您可以在web UI中监视每个服务器的tablet数量。
  • Kerberos身份验证在主机名中包含大写字母的主机上不能正常运行。
  • 如果在krb5.conf中配置rdns = false,Kerberos身份验证将不能正常工作。

有了HBase为什么还要Kudu?Kudu与HBase的简要分析与对比

有了HBase为什么还要Kudu

kudu和Hbase类似也是一个分布式数据库,据官方给它的定位是提供”fast analytics on fast data”(在更新更及时的数据上做更快的分析),希望支撑起同时需要高吞吐率的顺序和随机读写的应用场景(可能的场景,比如时间序列数据分析,日志数据实时监控分析),提供一个介于HDFS和HBase的性能特点之间的一个系统,在随机读写和批量扫描之间找到一个平衡点,并保障稳定可预测的响应延迟。

 Kudu的数据模型和磁盘存储都与Hbase不同。

0.作为Hadoop生态圈的一部分,对接生态系统的其他组件方便,会有官方提供的接口

1.kudu自己存储数据不依赖与HDFS存储;不依赖于zookeeper,将它的功能集成进了自身的TMaster;

2.和HBase类似的LSM结构,用于支持数据的随机读写。

3.有表结构,需定义Schema信息

- 必须定义唯一键,这就造成了写数据时多了一个判断唯一的过程。

需在写入数据前定义好每一列的类型(方便做类似于parquet的列式存储)

可以像关系型数据库一样用Alter增删列,不具有HBase动态列的特性

4.支持行级别的ACID

5.目前不支持二级索引

6.目前不支持BloomFilter优化join

7.核心模块用的C++来实现,没有full gc的风险

kudu解决了什么问题?

HDFS和HBase是大数据最常用的两种存储方式,它们的优缺点非常明显:

HDFS,使用列式存储格式Apache Parquet,Apache ORC,适合离线分析,不支持单条记录级别的update操作,随机读写性能差。这个就不多说了,用过HDFS的同学应该都知道这个特点。

HBase,可以进行高效随机读写,却并不适用于基于SQL的数据分析方向,大批量数据获取时的性能较差

那为什么HBase不适合做分析呢?

因为分析需要批量获取数据,而HBase本身的设计并不适合批量获取数据

1)都说HBase是列式数据库,其实从底层存储的角度来说它并不是列式的,获取指定列数据时是会读到其他列数据的。相对而言Parquet格式针对分析场景就做了很多优化。

2)HBase是LSM-Tree架构的数据库。LSM 的中心思想就是将随机写转换为顺序写来大幅提高写入操作的性能,但是牺牲了部分读的性能。这导致了HBase读取数据路径比较长,从内存到磁盘,可能还需要读多个HFile文件做版本合并。

随机读写是指对任意一个位置的读和写,磁盘随机读写慢是因为需要寻道,倒带才可以访问到指定存储点,而内存不需要,可以任意指定存储点。

为了让数据平台同时具备随机读写和批量分析能力,传统的做法是采用混合架构(hybrid architecture),也就是我们常说的T+1的方式,数据实时更新在HBase,第二天凌晨同步到HDFS做离线分析。这样的缺点很明显,时效性差,数据链路长,过程复杂,开发成本高。

Kudu不及HDFS批处理快,也不及HBase随机读写能力强,但是反过来它比HBase批处理快(适用于OLAP的分析场景),而且比HDFS随机读写能力强(适用于实时写入或者更新的场景),这就是它能解决的问题。

Kudu与HBase的简要分析与对比

转自:https://www.cnblogs.com/163yun/p/9646008.html

Hadoop生态圈中的技术繁多,HDFS作为底层数据存储的地位一直很牢固。而HBase作为Google BigTable的开源产品,一直也是Hadoop生态圈中的核心组件,其数据存储的底层采用了HDFS,主要解决的是在超大数据集场景下的随机读写和更新的问题。Kudu的设计有参考HBase的结构,也能够实现HBase擅长的快速的随机读写、更新功能。那么同为分布式存储系统,HBase和Kudu二者有何差异?两者的定位是否相同?我们通过分析HBase与Kudu整体结构和存储结构等方面对两者的差异进行比较。

HBase的整体结构

关于KUDU

HBase的主要组件包括Master,zookeeper服务,RegionServer,HDFS。

Master:用来管理与监控所有的HRegionServer,也是管理HBase元数据的模块。

zookeeper:作为分布式协调服务,用于保存meta表的位置,master的位置,存储RS当前的工作状态。

RegionServer:负责维护Master分配的region,region对应着表中一段区间内的内容,直接接受客户端传来的读写请求。

HDFS:负责最终将写入的数据持久化,并通过多副本复制实现数据的高可靠性。

Kudu的整体结构

关于KUDU

Kudu集群中存在两种主要组件:

(1)TServer,负责管理Tablet,tablet是负责一张表中某块内容的读写,接收其他TServer中leader tablet传来的同步信息。

(2)Master,集群中的管理节点,用于管理tablet的基本信息,表的信息,并监听TServer的状态。多个Master之间通过Raft协议实现数据同步和高可用。

主要区别  

Kudu结构看上去跟HBase差别并不大,主要的区别包括:

1、Kudu将HBase中zookeeper的功能放进了Master内,Kudu中Master的功能比HBase中的Master任务要多一些。

2、Hbase将数据持久化这部分的功能交给了Hadoop中的HDFS,最终组织的数据存储在HDFS上。Kudu自己将存储模块集成在自己的结构中,内部的数据存储模块通过Raft协议来保证leader Tablet和replica Tablet内数据的强一致性,和数据的高可靠性。为什么不像HBase一样,利用HDFS来实现数据存储,猜测可能是因为HDFS读小文件时的时延太大,所以Kudu自己重新完成了底层的数据存储模块,并将其集成在TServer中。

数据存储方式  

HBase  

HBase是一款Nosql数据库,典型的KV系统,没有固定的schema模式,建表时只需指定一个或多个列族名即可,一个列族下面可以增加任意个列限定名。一个列限定名代表了实际中的一列,HBase将同一个列族下面的所有列存储在一起,所以HBase是一种面向列族式的数据库。  

关于KUDU

HBase将每个列族中的数据分别存储,一个列族中的每行数据中,将rowkey、列族名、列名、timestamp组成最终存取的key值,另外为了支持修改,删除,增加了一个表征该行数据是否删除的标记。在同一个列族中的所有数据,按照rowkey:columnfamily:columnQulifier:timestamp组成的key值大小进行升序排列,其中rowkey、columnfamily、columnQulifier采用的是字典顺序,其值越大,key越大,而timestamp是值越大,key越小。HBase通过按照列族分开存储,相对于行式存储能够实现更高的压缩比,这也是其比较重要的一个特性。

 HBase对一行数据进行更新时,HBase也是相当于插入一行新数据,在读数据时HBase按照timestamp的大小得到经过更新过的最新数据。

关于KUDU

Kudu

Kudu是一种完全的列式存储引擎,表中的每一列数据都是存放在一起,列与列之间都是分开的。

为了能够保存一部分历史数据,并实现MVCC,Kudu将数据分为三个部分。一个部分叫做base data,是当前的数据;第二个部分叫做UNDO records,存储的是从插入数据时到形成base data所进行的所有修改操作,修改操作以一定形式进行组织,实现快速查看历史数据;第三个部分是REDO records,存储的是还未merge到当前数据中的更新操作。下图中表示的是在Kudu中插入一条数据、更新数据两个操作的做法,当然做法不唯一,不唯一的原因是Kudu可以选择先不将更新操作合并到base data中。

关于KUDU

差异分析

(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)当然还有一些其他的行式存储与列式存储之间在不同应用场景下的性能差异。

写入和读取过程

一、HBase 

HBase作为一种非常典型的LSM结构的分布式存储系统,是Google bigtable的apache开源版本。经过近10年的发展,HBase已经成为了一个成熟的项目,在处理OLTP型的应用如消息日志,历史订单等应用较适用。在HBase中真正接受客户端读写请求的RegionServer的结构如下图所示:

关于KUDU

关于HBase的几个关键点:

(1)在HBase中,充当写入缓存的这个结构叫做Memstore,另外会将写入操作顺序写入HLOG(WAL)中以保证数据不丢失。

(2)为了提高读的性能,HBase在内存中设置了blockcache,blockcache采用LRU策略将最近使用的数据块放在内存中。

(3)作为分布式存储系统,为保证数据不因为集群中机器出现故障而导致数据丢失,HBase将实际数据存放在HDFS上,包括storefile与HLOG。HBase与HDFS低耦合,HBase作为HDFS的客户端,向HDFS读写数据。

 

1、HBase写过程

(1)客户端通过客户端上保存的RS信息缓存或者通过访问zk得到需要读写的region所在的RS信息;

(2)RS接受客户端写入请求,先将写入的操作写入WAL,然后写入Memstore,这时HBase向客户端确认写入成功;

(3)HBase在一定情况下将Memstore中的数据flush成storefile(可能是Memstore大小达到一定阈值或者region占用的内存超过一定阈值或者手动flush之类的),storefile以HFile的形式存放在HDFS上;

(4)HBase会按照一定的合并策略对HDFS上的storefile进行合并操作,减少storefile的数量。

2、HBase读过程

HBase读数据的过程比较麻烦,原因包括:

(1)HBase采用了LSM-tree的多组件算法作为数据组织方式,这种算法会导致一个region中有多个storefile;

(2)HBase中采用了非原地更新的方式,将更新操作和删除操作转换成插入一条新数据的形式,虽然这样能够较快的实现更新与删除,但是将导致满足指定rowkey,列族、列名要求的数据有多个,并且可能分布在不同的storefile中;

(3)HBase中允许设置插入和删除数据行的timestamp属性,这样导致按顺序落盘的storefile内数据的timestamp可能不是递增的。

  下面介绍从HBase中读取一条指定(rowkey,column family,column)

(1)读过程与HBase客户端写过程第一步一样,先尝试获取需要读的region所在的RS相关信息;

(2)RS接收读请求,因为HBase中支持多版本数据(允许存在rowkey、列族名、列名相同的数据,不同版本的数据通过timestamp进行区分),另外更新与删除数据都是通过插入一条新数据实现的。所以要准确的读到数据,需要找到所有可能存储有该条数据的位置,包括在内存中未flush的memstore,已经flush到HDFS上的storefile,所以需要在1 memstore +N storefile中查找;

(3)在找到的所有数据中通过判断timestamp值得到最终的数据。

二、Kudu

关于KUDU

(1)Kudu中的Tablet是负责表中一块内容的读写工作,Tablet由一个或多个Rowset组成。其中有一个Rowset处于内存中,叫做Memrowset,Memrowset主要负责处理新的数据写入请求。DiskRowSet是MemRowset达到一定程序刷入磁盘后生成的,实质上是由一个CFile(Base Data)、多个DeltaFile(UNDO records &REDO records)和位于内存的DeltaMemStore组成。Base data、UNDO records、和REDO records都是不可修改的,DeltaMemStore达到一定大小后会将数据刷入磁盘生成新的REDO records。Kudu后台会有一个类似HBase的compaction线程按照一定的compaction 策略对tablet进行合并处理:

 a、将多个DeltaFile(REDO records)合并成一个大的DeltaFile;

 b、将多个REDO reccords文件与Base data进行合并,并生成新的UNDO records;

 c、将多个DiskRowset之间进行合并,减少DiskRowset的数量。

(2)Kudu将最终的数据存储在本地磁盘上,为了保证数据可靠性,Kudu为一个tablet设置了多个副本(一般为3或5个)。所以一个tablet会由多个TServer负责维护,其中有个副本称为leader tablet,写入的请求只能通过leader tablet来处理,副本之间通过Raft协议保证其他副本与leader tablet的强一致性。

 

1、Kudu写过程

Kudu与HBase不同,Kudu将写入操作分为两种,一种是插入一条新数据,一种是对一条已插入数据的更新。

1、Kudu插入一条新数据

(1)客户端连接Master获取表的相关信息,包括分区信息,表中所有tablet的信息;

(2)客户端找到负责处理读写请求的tablet所负责维护的TServer。Kudu接受客户端的请求,检查请求是否符合要求(表结构);

(3)Kudu在Tablet中的所有rowset(memrowset,diskrowset)中进行查找,看是否存在与待插入数据相同主键的数据,如果存在就返回错误,否则继续;

(4)Kudu在MemRowset中写入一行新数据,在MemRowset数据达到一定大小时,MemRowset将数据落盘,并生成一个diskrowset用于持久化数据,还生成一个memrowset继续接收新数据的请求。

 2、Kudu对原有数据的更新

(1)客户端连接Master获取表的相关信息,包括分区信息,表中所有tablet的信息;

(2)Kudu接受请求,检查请求是否符合要求;

(3)因为待更新数据可能位于memrowset中,也可能已经flush到磁盘上,形成diskrowset。因此根据待更新数据所处位置不同,kudu有不同的做法:

当待更新数据位于memrowset时

a、找到待更新数据所在行,然后将更新操作记录在所在行中一个mutation链表中;在memrowset将数据落盘时,Kudu会将更新合并到base data,并生成UNDO records用于查看历史版本的数据和MVCC,UNDO records实际上也是以DeltaFile的形式存放;

当待更新数据位于DiskRowset中

b、找到待更新数据所在的DiskRowset,每个DiskRowset都会在内存中设置一个DeltaMemStore,将更新操作记录在DeltaMemStore中,在DeltaMemStore达到一定大小时,flush在磁盘,形成Delta并存在方DeltaFile中;

实际上Kudu提交更新时会使用Raft协议将更新同步到其他replica上去,当然如果在memrowset和diskrowset中都没有找到这条数据,那么返回错误给客户端;另外当DiskRowset中的deltafile太多时,Kudu会采用一定的策略对一组deltafile进行合并。

 

2、Kudu读过程

1、客户端连接Master获取表的相关信息,包括分区信息,表中所有tablet的信息;

2、客户端找到需要读取的数据的tablet所在的TServer,Kudu接受读请求,并记录timestamp信息,如果没有显式指定,那么表示使用当前时间;

3、Kudu找到待读数据的所有相关信息,当目标数据处于memrowset时,根据读取操作中包含的timestamp信息将该timestamp前提交的更新操作合并到base data中,这个更新操作记录在该行数据对应的mutation链表中;

4、当读取的目标数据位于diskrowset中,在所有DeltaFile中找到所有目标数据相关的UNDO record和REDO records,REDO records可能位于多个DeltaFile中,根据读操作中包含的timestamp信息判断是否需要将base data进行回滚或者利用REDO records将base data进行合并更新。

三、Kudu与HBase在读写上过程中的差异

1、写过程

(1)HBase写的时候,不管是新插入一条数据还是更新数据,都当作插入一条新数据来进行;而Kudu将插入新数据与更新操作分别看待。

(2)Kudu表结构中必须设置一个唯一键,插入数据的时候必须判断一些该数据的主键是否唯一,所以插入的时候其实有一个读的过程;而HBase没有太多限制,待插入数据将直接写进memstore。

(3)HBase实现数据可靠性是通过将落盘的数据写入HDFS来实现,而Kudu是通过将数据写入和更新操作同步在其他副本上实现数据可靠性。

结合以上几点,可以看出Kudu在写的性能上相对HBase有一定的劣势。

2、读过程

(1)在HBase中,读取的数据可能有多个版本,所以需要结合多个storefile进行查询;Kudu数据只可能存在于一个DiskRowset或者MemRowset中,但是因为可能存在还未合并进原数据的更新,所以Kudu也需要结合多个DeltaFile进行查询。

(2)HBase写入或者更新时可以指定timestamp,导致storefile之间timestamp范围的规律性降低,增加了实际查询storefile的数量;Kudu不允许人为指定写入或者更新时的timestamp值,DeltaFile之间timestamp连续,可以更快的找到需要的DeltaFile。

(3)HBase通过timestamp值可以直接取出数据;而Kudu实现多版本是通过保留UNDO records(已经合并过的操作)和REDO records(未合并过的操作)完成的,在一些情况下Kudu需要将base data结合UNDO records进行回滚或者结合REDO records进行合并然后才能得到真正所需要的数据。

 结合以上三点可以得出,不管是HBase还是Kudu,在读取一条数据时都需要从多个文件中搜寻相关信息。相对于HBase,Kudu选择将插入数据和更新操作分开,一条数据只可能存在于一个DiskRowset或者memRowset中,只需要搜寻到一个rowset中存在指定数据就不用继续往下找了,用户不能设置更新和插入时的timestamp值,减少了在rowset中DeltaFile的读取数量。这样在scan的情况下可以结合列式存储的优点实现较高的读性能,特别是在更新数量较少的情况下能够有效提高scan性能。

另外,本文在描述HBase读写过程中没有考虑读写中使用的优化技术如Bloomfilter、timestamp range等。其实Kudu中也有使用类似的优化技术来提高读写性能,本文只是简单的分析,因此就不再详细讨论读写过程。如有需要了解HBase的详细读写过程,可以参考范欣欣的    HBase - 数据写入流程解析等一系列HBase相关文章。

 

其他差异

HBase:使用的java,内存的释放通过GC来完成,在内存比较紧张时可能引发full GC进而导致服务不稳定;

Kudu:核心模块用的C++来实现,没有full gc的风险。

总结

以上在整体结构,数据存储结构还有读写过程等方面上对HBase和Kudu这两款分布式存储系统进行大体上的比较。Kudu通过要求完整的表结构设置,主键的设定,以列式存储作为数据在磁盘上的组织方式,更新和数据分开等技巧,使得Kudu能够实现像HBase一样实现数据的随机读写之外,在HBase不太擅长的批量数据扫描(scan)具有较好的性能。而批量读数据正是olap型应用所关注的重点,正如Kudu官网主页上描述的,Kudu实现的是既可以实现数据的快速插入与实时更新,也可以实现数据的快速分析。Kudu的定位不是取代HBase,而是以降低写的性能为代价,提高了批量读的性能,使其能够实现快速在线分析。

 

使用注意点(踩坑记录)

1、Kudu创建表时,PK字段一定要写在表结构的最前面。

2、Hash分区只能在创建表时修改,而Range分区可以在使用过程中动态改变。

3、Range分区在动态改变时,通过python api新增一个Range分区

4、一次性将大量数据读入Kudu,会报Server timeout错误,解决方案2个(可以同时使用):

(1)增加memory_limit_hard_bytes配置的值

(2)将大量数据分批写入Kudu表

5、Kudu的Python api需要注意版本选择,目前部门用的cdh5.13,kudu1.4,但是python api用的1.4(没有匹配的1.5版本,安装python api时要注意)

6、通过impala使用kudu表,既可以通过impala直接创建kudu表(store as kudu),也可以先单独创建kudu表,再通过通过impala做外部表映射(create external table)。

7、通过cdh的clouder manager重启kudu服务后,

通过命令:kudu cluster ksck <master_addresses> 查看kudu各个服务和节点状态时,开始一段时间各个kudu表会不可用,需要等待一段时间才会正常(重启之后,kudu内部需要自动做各种处理工作),这时不要慌,耐心等待即可。

8、通过python的os.system提交命令imapal sql时,注意字段名称是否含有特殊字段,若有则需要做转义,否则impala-shell运行时会根据特殊字段切分字段名称,导致报错。

9、读取Kudu表时,需要根据分区字段读取,尽可能少加载数据,切记一次性读取全量表(数据表很大时,读取性能会有很大问题)。

10、通过python api写入数据时 ,需要控制flush数据量,否则会内存问题,建议每间隔N条记录(根据实际情况选择)刷新一次。

11、kudu表的现有限制(未来版本限制或许会移除),举几个需要重点注意的栗子:

字段数目不要超过300
PK的类型选择,不能float和bool等
单个表在单个tablet server上的最大tablets数目为60,单个tablet server上最大tablet数目为2000(在设计表分区的时候需要重点考虑) 

如何入门和实战

循着一下主题去探索。当探索完这些主题的时候,相信你已经掌握了Kudu。

关于KUDU

关于KUDU