表与schema

Kudu设计是面向结构化存储的,因此Kudu的表需要用户在建表时定义它的Schema信息,这些Schema信息包含:

  1. 列定义(含类型)
  2. Primary Key定义(用户指定的若干个列的有序组合)

数据的唯一性,依赖于用户所提供的Primary Key中的Coumn组合的值的唯一性。
Kudu提供了Ater命令来增删列,但位于Primary Key中的列是不允许删除的。
Kudu当前并不支持二级索引。
从用户角度来看,Kudu是一种存储结构化数据表的存储系统。
在一个Kudu集群中可以定义任意数量的tabe,每个tabe都需要预先定义好schema。每个tabe的列数是确定的,每一列都需要有名字和类型,每个表中可以把其中一列或多列定义为主键。这么看来,Kudu更像关系型数据库,而不是像HBase、Cassandra和MongoDB这些NoSQL数据库。不过Kudu目前还不能像关系型数据一样支持二级索引。Kudu使用确定的列类型,而不是类似于NoSQL的“everything is byte”(一切都是字节)。这可以带来两点好处: 确定的列类型使Kudu可以进行类型特有的编码 。可以提供 SQL-ike 元数据给其他上层查询工具使用,比如BI工具。

kudu的底层数据模型

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

  • 一个table其中tablet的数量是根据hash或者range设置的
  • 一个tablet包含metaData信息和多个rowSet信息组成,其中metaData保存的是block和block所在的data所在的位置
  • 一个rowSet包含一个memRowSet和多个diskRowSet,其中memRowSet是存储inset和update的数据,写满后刷新到磁盘中(diskRowSet),默认1G刷新一次或者2min
  • diskRowSet用于存储数据的变化,比如数据的更新,后台定期对diskRowSet的数据进行更新合并,删除历史数据以减少网络开销。它主要包括:Bloom filter:快速定义一个key是否存在;Ad_hoc Index主键索引用于定位diskRowSet中的具体位置;Basedata: 是memRowSet flush下来的数据按照列来存储,按照主键排序;Undo file:基于memStoreFile之前的数据值flush之前的历史数据,历史mysql获取历史之前的数据;UndoFile:是基于BaseData 之后的变更记录,可以获取最新的数据类似mysql的重做日志。DeltaMem:存储DiskRowSet中数据的更新,写满后flush到磁盘形成deltafile文件。

image.png

Kudu的读写原理

image.png

  • 每个kudu table按照hash或range分区为多个tablet;每个tablet中包含一个MemRowSet以及多个DiskRowSet;
  • 每个DiskRowSet包含BaseData以及DeltaStores;
  • DeltaStores由多个DeltaFile和一个DeltaMemStore组成;
  • insert请求的新增数据以及对MemRowSet中数据的update操作(新增的数据还没有来得及触发compaction操作再次进行更新操作的新数据)会先进入到MemRowSet
  • 当触发flush条件时将新增数据真正的持久化到磁盘的DiskRowSet内;
  • 对老数据的update和delete操作是提交到内存中的DeltaMemStore;
  • 当触发flush条件时会将更新和删除操作持久化到磁盘DIskRowSet中的DeltaFile内,此时老数据还在BaseData内(逻辑删除),新数据已在DeltaFile内;
  • 当触发compaction条件时,将DeltaFile和BaseData进行合并,DiskRowSet进行合并,此时老数据才真正的从磁盘内消失掉(物理删除),只留下更新后的数据记录;

    kudu读写流程

    image.png
  1. 客户端向Kudu Master请求tablet所在位置Kudu Master返回tablet所在位置
  2. 为了优化读取和写入,客户端将元数据进行缓存
  3. 根据主键范围过滤目标tablet,请求Tablet Follower
  4. 根据主键过滤scan范围,定位DataRowSets
  5. 加载BaseData,并与DeltaStores合并,得到老数据的最新结果
  6. 拼接第6步骤得到的老数据与MemRowSet数据 得到所需数据
  7. 将数据返回给客户端

    kudu的写流程

    image.png

  8. 客户端向Kudu Master请求tablet所在位置;Kudu Master返回tablet所在位置;

  9. 为了优化读取和写入,客户端将元数据进行缓存;
  10. 根据分区策略,路由到对应Tablet,请求Tablet Leader;
  11. 根据RowSet记录的主键范围过滤掉不包含新增数据主键的RowSet;
  12. 根据RowSet 布隆过滤器再进行一次过滤,过滤掉不包含新数据主键的RowSet;
  13. 查询RowSet中的B树索引判断是否命中新数据主键,若命中则报错主键冲突,否则新数据写入MemRowSet;
  14. 返回响应给客户端;

    kudu更新流程

    image.png

  15. 更新删除流程与写入流程类似,区别就是最后判断是否存在主键时候的操作,若存在才能更新,不存在才能插入新数据。客户端向Kudu Master请求tablet所在位置

  16. Kudu Master返回tablet所在位置
  17. 为了优化读取和写入,客户端将元数据进行缓存
  18. 根据分区策略,路由到对应Tablet,请求Tablet Leader
  19. 根据RowSet记录的主键范围过滤掉不包含修改的数据主键的RowSet
  20. 根据RowSet 布隆过滤器再进行一次过滤,过滤掉不包含修改的数据主键的RowSet
  21. 查询RowSet中的B树索引判断是否命中修改的数据主键,若命中则修改至DeltaStores,否则报错数据不存在
  22. 返回响应给客户端

    kudu优化

    TabletServer 在开始拒绝所有传入的写入之前可以消耗的最大内存量:memory_limit_h
    ard_bytes=1073741824
    分配给 Kudu Tablet Server 块缓存的最大内存量:block_cache_capacity_mb=512

    kudu使用限制

    主键

    创建表后,不能更改主键。必须删除并重新创建表以选择新的主键。创建表的时候,主键必须放在最前边。
    主键不能通过 update 更新,如果要修改主键就必须先删除行,然后重新插入。这种操作不是原子性的。(kudu的删除和插入操作无法事务)
    不支持自动生成主键,可以通过内置的 uuid 函数表示为主键值。
    联合主键由 kudu 编码后,大小不能超过 16KB。

    Cells

    在编码或压缩之前,任何单个单元都不得大于 64KB
    在 Kudu 完成内部复合键编码之后,组成复合键的单元格总共限制为 16KB
    如果插入不符合这些限制的行时会报错误并返回给客户端

    字段

  23. 默认情况下,Kudu 不允许创建超过 300 列的表。官方建议使用较少列的 Schema 设计以获得最佳性能。不支持 CHAR、VARCHAR、DATE 和数组等复杂类型。

  24. 现有列的类型和是否允许为空,一旦设置后,是不可修改的。
  25. Decimal 类型的精度不可修改。也不允许通过更改表来更改 Decimal 列的精度和小数位数
  26. 删除列不会立即回收空间。首先必须运行压缩。

    表中的副本数必须为奇数,最多为 7
    复制因子(在表创建时设置)不能更改
    无法手动运行压缩,但是删除表将立即回收空间

    其他限制

    不支持二级索引。不支持多行事务。
    不支持外键。
    列名和表名之类的标识符仅限于有效的 UTF-8 字符串并且其最大长度为 256 个字符。

    分区限制

  27. 表必须根据一个主键 or 联合主键被预先切成 tablet,不支持自动切。表被创建后不支持修改分区字段,支持添加和删除 range 分区(意思分区表,分区字段需提前定义好,kudu 不会自动分)

  28. 已经存在的表不支持自动重新分区,只能创建新表时指定
  29. 丢失副本时,必须通过手动修复方式来恢复

    扩展建议和限制

  30. 建议 TabletServer 最多为 100 台。建议 Master 最多 3 台。

  31. 建议每个 TabletServer 最大数据为 8T(压缩后)。
  32. 建议每台 TabletServer 的 tablet 数为 1000,最多 2000。
  33. 创建表的时候,建议在每个 Tablet Server 上,每个表的 Tablet 数最大为 60,也就是 3 节点的话,3 副本,创表分区最大 60,这样每个单 TabletServer 上该表的 Tablets 也就为 60
  34. 建议每个 Tablet 最大为 50GB,超出后可能导致压缩和启动有问题。
  35. 建议单 Tablet 的大小<10GB。

    守护进程

  36. 部署至少 4G 内存,理想情况下应超过 16GB。预写日志(WAL)只能存储在一个磁盘上。

  37. 不能直接删除数据目录,必须使用重新格式化数据目录的方式来达到删除目的。
  38. TabletServer 不能修改 IP 和 PORT。
  39. Kudu 对 NTP 有严格要求,如果时间不同步时,Kudu 的 Master 和 TabletServer 会崩溃。
  40. Kudu 仅使用 NTP 进行了测试,不支持其他时间同步工具。

    集群管理限制

  41. 不支持滚动重启。建议 Kudu 集群中的最大点对点延迟为 20 毫秒。推荐的最小点对点带宽是 10GB。

  42. 如果要使用位置感知功能将平板服务器放置在不同的位置,官方建议先测量服务器之间的带宽和延迟,以确保它们符合上述指导原则。
  43. 首次启动群集时,必须同时启动所有 Master 服务。

    复制和备份限制

    Kudu 当前不支持任何用于备份和还原的内置功能。鼓励用户根据需要使用 Spark 或 Impala之类的工具导出或导入表。

    Impala集成限制

  44. 创建 Kudu 表时,建表语句中的主键字段必须在最前面。 Impala 无法更新主键列中的值。

  45. Impala 无法使用以下命令创建 Kudu 表 VARCHAR 或嵌套类型的列。
  46. 名称包含大写字母或非 ASCII 字符的 Kudu 表在 Impala 中用作外部表时,必须分配一个备用名称。
  47. 列名包含大写字母或非 ASCII 字符的 Kudu 表不能用作 Impala 中的外部表。可以在 Kudu 中重命名列以解决此问题。
  48. !=和 like 谓词不会下推到 Kudu,而是由 Impala 扫描节点评估。相对于其他类型的谓语,这会导致降低性能。
  49. 使用 Impala 进行更新,插入和删除是非事务性的。如果查询在部分途中失败,则其部分效果不会回滚。
  50. 单个查询的最大并行度受限于 Table 中 Tablet 的数量。为了获得良好的分析性能,每位主机目标为 10 片或更多 tablets。
  51. Impala 的关键字(PARTITIONED、LOCATION、ROWFORMAT)不适用于在创建 Kudu 表时使用。

    Spark集成限制

    必须使用 JDK8,自 Kudu-1.5.0 起,Spark 2.2 是默认的依赖项版本。
    Kudu 表只能在 Spark SQL 中注册为临时表。
    无法使用 HiveContext 查询 Kudu 表。