kudu是什么?

apache kudu是Cloudera开源的存储引擎,可以同时提供低延迟的随机读写和高效的数据分析能力。

kudu支持水平扩展,使用Raft协议进行一致性保证,并且与impala、spark紧密结合

基于HDFS的存储技术,具有高吞吐连续读取数据的能力;而HBase等技术用于低延迟的随机读写场景。

kudu提供了一种折中的选择,kudu不但提供了行级别的插入、更新、删除api,同时也提供了接近parquet性能的批量扫描操作,使用同一份存储,既可以进行随机读写,也可以满足数据分析的需求。

Table和Schema

kudu是一种存储结构化数据表的存储系统

在一个kudu集群中可以定义任意数量的table,每个table都要事先定义好schema

每个表的列数的确定的,每一列都要有名字和类型。

每个表中可以把一列或多列定义为主键

读写操作

用户可以使用Insert、Update、Delete API对表进行读写

不论使用哪种API,都必须指定主键

批量的删除更新操作要依赖更高层次的组件(Impala、Spark)

Kudu目前还不支持多行事务

在读操作方面,kudu只提供了Scan操作来获取数据

用户可以通过指定过滤条件来获取想要的数据。目前只提供了两种类型的过滤条件:主键范围、列值与常数的比较。

kudu在硬盘中的数据采用列式存储,只扫描需要的列将极大的提高读取性能。

一致性模型

kudu提供了两种一致性模型。

默认的一致性模型是snapshot consistency。这种一致性模型保证用户每次读取出来的都是一个可用的快照,但是这种一致性模型只能保证单个client看到的数据一致,不能保证多个client读取出一样的数据。

另一种一致性模型是external consistency,可以在多个client间保证每次读取到的都是相同数据

Kudu的架构

在这里插入图片描述

kudu使用单个的master节点,用来管理集群的元数据,并且使用任意数量的Tablet Server节点来存储实际数据。

kudu的master节点负责整个集群的元数据管理和服务协调。它承担着以下功能:

  • 作为catalog manager,master节点管理者集群汇总所有的table和tablet的schema及一些其他的元数据
  • 作为cluster coordinator,master追踪者所有的server节点是否存活,并且当 server节点挂掉后协调数据的重新分布
  • 作为tablet directory,master跟踪每个tablet的位置

Catalog Manager

kudu的master节点持有一个单tablet的table——catalog table,用户不能直接访问

master将内部的catalog写入该tablet,并将整个catalog的信息缓存到内存中

元数据信息占用的空间不大,所以master不易存在性能瓶颈

catalog table保存了所有table的schema版本以及table的状态

Cluster Coordinator

kudu集群中的每个tablet server都要配置master的主机名列表

当集群启动时,tablet server会向master注册,并发送所有的tablet信息

tablet server第一次向master发送信息时,会发送所有的tablet全量信息;后续每次发送只会发送增量信息,仅包含新创建、删除或修改的tablet的信息。

作为Cluster Coordinator,master只是集群状态的观察者。对于tablet server中tablet的副本位置、raft配置和schema版本等信息的控制和修改由tablet server自身完成。master只需要下发命令,tablet server执行成功后会自动上报处理的结果。

Tablet Directory

master上缓存了集群的元数据,所以client读写数据时,要通过master才能获取到tablet的位置信息

但是如果每次读写都要通过master节点的话,那master就会变成这个集群的性能瓶颈,所以client会在本地缓存一份它需要访问的tablet的位置信息,这样就不用每次读写都从master中获取。

因为tablet的位置可能也会发生变化(比如某个tablet server节点crash掉了),所以当tablet的位置发生变化的时候,client会收到相应的通知,然后再去master上获取一份新的元数据信息。

Tablet存储

tablet存储主要实现目标为:

  • 快速的列扫描
  • 低延迟的随机读写
  • 一致性的性能

RowSets

在kudu中,tablet被细分为更小的单元,叫做RowSets

一些RowSet仅存储在内存中,被称为MemRowSets,另一些则同时使用内存和硬盘,被称为DiskRowSets。

任何一行未被删除的数据都只能存在于一个RowSet中。

无论任何时候,一个tablet仅有一个MemRowSet来保存插入的数据,并且有一个后台线程会定时将内存中的数据flush到硬盘上。

当一个MemRowSet被flush到硬盘上后,一个新的MemRowSet会替代它。而原有的MemRowSet会变成一个到多个DiskRowSet。

flush操作是完全同步进行的,在进行flush时,client同样可以进行读写操作

MemRowSet

MemRowSet是一个可以被并发访问并进行过锁优化的B-tree,主要是基于MassTree来设计的,但存在几点不同。

kudu并不支持直接的删除操作,使用了MVCC,所以在kudu中删除操作其实是插入一条标志着删除的数据,这样就可以推迟删除操作。

kudu不适合极高的随机读写的场景,与kudu的其他模块的数据结构不同,MemRowSet的数据结构使用行式存储。因为数据都存在内存中,所以性能是可以接收的,而且kudu对在MemRowSet中的数据结构进行了一定的优化。

DiskRowSet

当MemRowSet被flush到磁盘上就形成了DiskRowSet。当MemRowSet被flush到磁盘时,每32M就会形成一个新的DiskRowSet,这是为了保证每个DiskRowSet都不会太大,便于后续的增量compact操作。

kudu通过将数据分为base data和delta data,来实现数据的更新操作

kudu会将数据按列存储,数据被切分成多个page,并使用B-Tree进行索引。

除了用户写入的数据,kudu还会将主键索引存入一个列中,并提供布隆过滤器来进行高效查找。

Compaction

为了提供查询性能,kudu会定期记性compact操作,合并delta data和base data,对标记了删除的数据进行删除,并且会合并一些DiskRowSet

分区

kudu提供了较为灵活的分区方式。

当用户创建一个table时,可以同时制定table的partition schema,partition schema将primary key映射为partition key。一个partition schema包括0个到多个hash-partitioning规则和一个range-partitioning规则。通过灵活的组合各种partition规则,用户可以创建适用自己业务场景的分区方式。

kudu的应用

进行实时的数据写入和分析。

kudu和HBase比较

https://bigdata.163.com/product/article/15

HBase架构

在这里插入图片描述

HBase的主要组件包括Master、Zookeeper、RegionServer、HDFS

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

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

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

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

kudu架构

在这里插入图片描述

kudu的主要组件包括TServer和TMaster

TServer:负责管理tablet,tablet负责一张表中某块内容的读写,接受其他TServer中leader tablet传来的同步信息。

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

主要区别
  • kudu将HBase中Zookeeper的功能放进了TMaster中,kudu中TMaster功能比HBase的Master任务多一些。
  • HBase将数据持久化到HDFS中;kudu将存储模块集成到自己的结构中
数据存储方式
HBase

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

在这里插入图片描述

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

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

kudu

kudu是完全的列式存储引擎,表中的每一列数据都是存放在一起,列与列之间都是分开的。
在这里插入图片描述

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

在这里插入图片描述

差异区别

(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的选择是将写入的数据,更新操作分开存储;

写入和读取过程

HBase

Hbase是典型的LSM结构的分布式存储系统。

真正接收客户端读写请求的RegionServer的结构如下所示:

在这里插入图片描述

HBase的几个关键点
  • HBase中,充当写入缓存的结构是Memstore,另外会将写入操作顺序写入HLOG(WAL)中以保证数据不丢失
  • 为了提高读的性能,HBase在内存中设置了blockcache,blockcache按照LRU策略将最近使用的数据块放在内存中。
  • 作为分布式存储系统,为保证数据不因为集群中机器出故障而导致的数据丢失,HBase将实际数据存放在HDFS中,包括storeFile和HLOG。HBase与HDFS低耦合,HBase作为HDFS的客户端向HDFS读取数据
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的数量。

HBase读数据

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

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

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

kudu

在这里插入图片描述

kudu的tablet是负责表中一块内容的读写工作。tablet由一个或多个RowSet组成。

kudu写过程

kudu写入分为:插入一条新数据、对一条已插入数据的更新

插入一条新数据:

  1. 客户端连接TMaster获取表的相关信息,包括分区信息,表中所有的tablet信息
  2. 客户端找到负责读写请求的tablet所负责的TServer。kudu接收客户端的请求,检查请求是否符合要求
  3. kudu在tablet的所有rowset中查找,看是否存在于带插入数据主键相同的数据,如果存在就返回错误,否则继续。
  4. kudu在MemRowSet中写入一行新数据,在MemRowSet数据达到一定大小时,MemRowSet数据落盘,并生成一个新的DiskRowSet用于持久化数据,还生成一个MemRowSet继续接受新数据的请求。

对原有数据的更新:

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

  2. 客户端找到负责读写请求的tablet所负责的TServer。kudu接收客户端的请求,检查请求是否符合要求

  3. 因为数据可能位于MemRowSet中,也可能已经flush到磁盘上形成DiskRowSet,因此根据数据所处位置不同,有不同做法:

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

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

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

kudu读过程
  1. 客户端连接TMaster获取表的相关信息,包括分区信息,表中所有的tablet信息
  2. 客户端找到要读取数据的tablet所在的TServer,kudu接收请求,并记录时间戳信息,如果没有显示指定,那么表示使用当前信息。
  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通过要求完整的表结构设置,主键的设定,以列式存储作为数据在磁盘上的存储方式,使得kudu能够实现像HBase一样随机读写之外,在HBase不太擅长的批量数据扫描方面有较好的性能。正如kudu官网所描述的,kudu既可以实现数据的快速插入和实时更新,也可以实现数据的快速分析。

kudu的定位不是取代HBase,而是以降低写的性能为代价,提高了批量读的性能,使其能够实现快速在线分析。

LSM

LSM树核心思想的核心就是放弃部分读能力,换取写入的最大化能力。LSM Tree ,这个概念就是结构化合并树的意思,它的核心思路其实非常简单,就是假定内存足够大,因此不需要每次有数据更新就必须将数据写入到磁盘中,而可以先将最新的数据驻留在内存中,等到积累到足够多之后,再使用归并排序的方式将内存内的数据合并追加到磁盘队尾(因为所有待排序的树都是有序的,可以通过合并排序的方式快速合并到一起)。

https://blog.csdn.net/ifenggege/article/details/107461003
https://juejin.cn/post/7067815605465776158
https://zhuanlan.zhihu.com/p/426901943

作者:admin  创建时间:2023-06-27 23:06
最后编辑:admin  更新时间:2024-04-07 15:40