HBase:创建多个表或具有多列的单个表?

Posted

技术标签:

【中文标题】HBase:创建多个表或具有多列的单个表?【英文标题】:HBase: Create multiple tables or single table with many columns? 【发布时间】:2018-07-13 13:49:21 【问题描述】:

什么时候创建多个表而不是创建具有大量列的单个表才有意义。我知道通常表只有几个列族 (1-2),每个列族可以支持 1000 多个列。

当 HBase 似乎在单个表中可能包含大量列时表现良好时,何时创建单独的表有意义?

【问题讨论】:

这是一个常见问题解答。请在考虑发布之前阅读您的教科书和/或手册和谷歌任何错误消息或您的问题/问题/目标的许多清晰、简洁和精确的措辞,有和没有您的特定字符串/名称和站点:***.com 和标签;阅读许多答案。如果您发布问题,请使用一个短语作为标题。反映你的研究。请参阅How to Ask 和投票箭头鼠标悬停文本。 【参考方案1】:

在回答问题本身之前,让我先说明一些起作用的主要因素。我将假设正在使用的文件系统是 HDFS。

    一个表被划分为称为区域的键空间的非重叠分区。

    key-range -> 区域映射存储在一个称为 meta 的特殊单区域表中。

    一个区域的一个 HBase 列族中的数据存储在单个 HDFS 目录中。它通常是几个文件,但出于所有意图和目的,我们可以假设列族的区域数据存储在 HDFS 上称为 StoreFile / HFile 的单个文件中。

    StoreFile 本质上是一个包含 KeyValues 的排序文件。 KeyValue 在逻辑上按顺序表示以下内容:(RowLength、RowKey、FamilyLength、FamilyName、Qualifier、Timestamp、Type)。例如,如果您的区域中只有两个 KV 用于 CF,其中键相同但值在两列中,这就是 StoreFile 的样子(除了它实际上是字节编码的,并且元数据(如长度等)也是如上所述存储):

    Key1:Family1:Qualifier1:Timestamp1:Value1:Put
    
    Key1:Family1:Qualifier2:Timestamp2:Value2:Put
    

    StoreFile被划分为blocks(默认64KB),每个数据块中包含的key range通过多级索引进行索引。单个块内的随机查找可以使用索引 + 二进制搜索来完成。但是,在定位扫描所需的第一个块中的起始位置后,扫描必须连续通过特定块。

    HBase 是一个基于 LSM-tree 的数据库,这意味着它有一个内存日志(称为 Memstore),它会定期刷新到创建 StoreFiles 的文件系统。 Memstore 为特定列族的单个区域内的所有列共享。

在处理从 HBase 读取/向 HBase 写入数据时涉及到一些优化,但上面给出的信息在概念上是正确的。鉴于上述陈述,以下是多列与多表相比其他方法的优点:

单表多列

    由于一个键的所有数据都存储在一起,而不是跨表存储在多个文件中,因此由于前缀编码可以更好地进行磁盘压缩。由于数据大小更小,这也会减少磁盘活动。 元表上的负载较小,因为区域总数将变小。您将有 N 个区域用于一张表,而不是 N*M 区域用于 M 个表。这意味着更快的区域查找和元表上的低争用,这是大型集群的一个问题。 当您需要为单个行键读取多个列时,读取速度更快且 IO 放大率更低(导致磁盘活动更少)。 为单个行键写入多个列时,您可以利用行级事务、批处理和其他性能优化。

什么时候用这个

    如果您想跨多个列执行行级事务,您必须将它们放在一个表中。 即使您不需要行级事务,但您经常为同一个行键写入或查询多个列。一个好的经验法则是,如果平均而言,超过 20% 的列具有单行的值,则应尝试将它们放在一个表中。 列过多时。

多个表

    如果扫描主要只关注一列,则可以更快地扫描每个表并降低 IO 放大(请记住,扫描中的顺序查找将不必要地读取它们不需要的列)。 良好的数据逻辑分离,尤其是当您不需要跨列共享行键时。为一种类型的行键设置一个表。

何时使用

    当数据有明确的逻辑分离时。例如,如果您的行键架构在不同的列集上有所不同,请将这些列集放在单独的表中。 当只有一小部分列具有行键值时(请参阅下面的更好的方法)。 您希望为不同的列集设置不同的存储配置。例如。 TTL、压缩率、阻塞文件计数、memstore 大小等(在下面查看此用例中的更好方法)。

另一种选择:单个表中的多个 CF

从上面可以看出,这两种方法都有优点。如果您对多列具有相同的行键结构(因此,您希望共享行键以提高存储效率或需要跨列事务)但数据非常稀疏(这意味着您只写/只读),选择变得非常困难行键的列的一小部分)。 在这种情况下,您似乎需要两全其美。这就是列族的用武之地。如果您可以将列集划分为逻辑子集,在这些子集中您主要访问/读取/写入单个子集,或者您需要每个子集的存储级别配置(如 TTL、存储类、编写大量压缩计划等),然后您可以使每个子集成为列族。 由于特定列族的数据存储在单个文件(文件集)中,因此您可以在读取列子集的同时获得更好的局部性,而不会减慢扫描速度。

但是,有一个问题

不要尝试不必要地使用列族。与它们相关的成本,由于区域级写锁、监控等在 HBase 中的工作方式,HBase 在 10+ CF 上表现不佳。仅当跨 CF 的列之间存在逻辑关系但您通常不跨 CF 执行操作或需要为不同的 CF 具有不同的存储配置时才使用 CF。 如果您在它们之间共享行键模式,则仅使用包含所有列的单个 CF 是完全可以的,除非您有一个非常稀疏的数据集,在这种情况下,您可能需要不同的 CF 或基于上述要点的不同表。

【讨论】:

以上是关于HBase:创建多个表或具有多列的单个表?的主要内容,如果未能解决你的问题,请参考以下文章

自动创建具有多列的 SQL 表

HBase 与多列的一对多关系

具有多列的单个固定表与灵活的抽象表

如何同时对多个表或列操作

postgresql:具有外键的多个多列索引?

具有多列的表视图