乐趣区

关于java:一篇文章带你入门HBase

本文已收录至 Github,举荐浏览 👉 Java 随想录
微信公众号:Java 随想录

HBase(Hadoop Database)是一个开源的、分布式的、面向列的 NoSQL 数据库,它是构建在 Hadoop 之上的。HBase 旨在提供牢靠的、高性能的、可扩大的存储和拜访大规模数据集的能力。

HBase 个性

以下是 HBase 的一些要害个性和概念:

  1. 分布式架构:HBase 是一个分布式数据库,它能够在一个集群中运行在多个机器上。数据以程度分片的形式散布在不同的机器上,这样能够实现数据的高可用性和横向扩展性。
  2. 列存储:HBase 是面向列的数据库,它将数据存储在表中的列族中。每个列族能够蕴含多个列,这样能够不便地存储和检索具备不同构造的数据。HBase 的列存储个性使得能够高效地读取和写入大量数据。
  3. 强一致性:HBase 提供强一致性的读写操作。当数据被写入或读取时,HBase 会确保所有相干的正本都是最新的。这使得 HBase 非常适合须要强一致性的利用场景,如金融、电信等畛域。
  4. 高可扩展性:HBase 能够轻松地扩大到大规模的数据集和集群。通过增加更多的机器和分片数据,能够线性地扩大存储容量和吞吐量。
  5. 疾速读写:HBase 是为了高性能而设计的。它应用了内存和硬盘的组合来存储数据,能够实现疾速的读写操作。此外,HBase 还反对批量写入和异步写入,进一步提高了写入性能。
  6. 灵便的数据模型:HBase 提供了灵便的数据模型,能够依据应用程序的需要设计表构造。它反对动静增加列,并且能够高效地执行范畴查问和单行读写操作。
  7. 数据一致性:HBase 通过应用 ZooKeeper 来治理集群的元数据和协调分布式操作,确保数据的一致性和可用性。
  8. 集成 Hadoop 生态系统:HBase 与 Hadoop 生态系统严密集成,能够与 Hadoop 分布式文件系统(HDFS)和 Hadoop 的计算框架(如 MapReduce)无缝配合应用。这使得 HBase 可能解决大规模的数据存储和剖析工作。

Hadoop 的限度

只管 Hadoop 是一个弱小的分布式计算框架,但它也存在一些不足之处,与 HBase 相比,以下是一些 Hadoop 的限度:

  1. 实时性:Hadoop 次要用于批处理工作,对于实时性要求较高的利用场景,如实时数据分析和流式解决,Hadoop 的提早可能会比拟高。Hadoop 的 MapReduce 模型通常不适宜解决须要即时响应的数据处理工作。
  2. 存储效率:Hadoop 在存储效率方面存在一些问题。为了提供容错性和可靠性,Hadoop 将数据复制屡次存储在不同的节点上,这会导致存储开销减少。绝对于 HBase 的列存储模型,Hadoop 的存储效率可能较低。
  3. 复杂性:Hadoop 的配置和治理绝对简单,须要专业知识和教训。搭建和保护一个 Hadoop 集群须要解决许多参数和组件,对于初学者来说可能存在肯定的学习曲线。
  4. 扩展性限度:尽管 Hadoop 具备良好的可扩展性,能够通过增加更多的节点来扩大集群的存储和计算能力,但在某些状况下,随着集群规模的减少,治理和调度节点可能变得更加艰难。
  5. 解决简单查问的限度:Hadoop 的次要计算模型是 MapReduce,它适宜解决简略的计算工作,但对于简单的查问和数据分析,如简单聚合、连贯和实时查问等,Hadoop 的性能可能不如专门设计的剖析数据库。

基本概念

NameSpace

命名空间,相似于关系型数据库的 Database 概念,每个命名空间下有多个表。

HBase 自带两个命名空间,别离是 hbasedefault,hbase 中寄存的是 HBase 内置的表,default 表是用户默认应用的命名空间,这 2 个命名空间默认是不展现的。

Table

相似于关系型数据库的表概念。不同的是,HBase 定义表时只须要申明列族即可,不须要申明具体的列。因为数据存储时稠密的,空(null)列不占用存储空间,所有往 HBase 写入数据时,字段能够动静、按需指定。因而,和关系型数据库相比,HBase 可能轻松应答字段变更的场景

RowKey

HBase 表中的每行数据都由一个 RowKey 和多个 Column(列)组成,数据是依照 RowKey 的字典顺序存储的,并且查问数据时只能依据 RowKey 进行检索,所以 RowKey 的设计非常重要

Column

HBase 中的每个列都由 Colunn Family (列族)和 Column Qualifier (列限定符)进行限定,例如 info: name, info: age。建表时,只需指明列族,而列限定符无需事后定义。

TimeStamp

用于标识数据的不同版本(version),每条数据写入时,零碎会主动为其加上该字段,其值为写入 HBase 的工夫。

Cell

由{rowkey, column Family:column Qualifier, timestamp} 惟一确定的单元,Cell 中的数据全副是字节码模式存贮

一条数据有多个版本,每个版本都是一个 Cell。

存储构造

HBase 存储构造如下:

下面的这种数据会存储为上面这样,底层存储为 Byte:

行分为 Region,列分为 Store,Region 能够放在其余机器上。

HBase 是基于 HDFS 的,而 HDFS 是不可能批改数据的,所以 HBase 其实也是不能批改数据的。HBase 应用工夫戳实现批改性能。取数据的时候取最新工夫戳的数据,取出来的就是最新的数据

HBase 数据拜访模式

HBase 数据拜访能够通过以下几种模式进行:

  1. 单行读写(Get 和 Put):应用 HBase 提供的 API,能够通过指定行键(Row Key)来读取和写入单行数据。Get 操作能够依据行键从表中获取特定行的数据,而 Put 操作能够将数据写入表的指定行。
  2. 批量读写(Scan 和 Batch Put):HBase 反对批量读写操作,能够一次性读取或写入多行数据。Scan 操作能够依照肯定的条件扫描表中的多行数据,而 Batch Put 操作能够一次性写入多行数据。
  3. 全表扫描(Scan):通过 Scan 操作,能够遍历整个表的数据,依照指定的条件进行过滤和筛选。能够设置起始行键和完结行键,还能够应用过滤器(Filter)进行更准确的数据查问。
  4. 列族范畴扫描(Scan):HBase 中的数据以列族(Column Family)为单位进行存储,能够通过 Scan 操作对指定列族的数据进行范畴扫描。这种形式能够进步数据查问的效率,只获取所需列族的数据,而不用读取整个表的数据。
  5. 过滤器(Filter):HBase 反对多种过滤器来进行数据的准确查问和过滤。能够应用行键过滤器(Row Filter)依照行键的条件进行数据过滤,还能够应用列族过滤器(Family Filter)、列限定符过滤器(Qualifier Filter)和值过滤器(Value Filter)等进行更细粒度的数据过滤。
  6. 原子性操作(Check-and-Put 和 Check-and-Delete):HBase 反对原子性操作,例如 Check-and-Put 和 Check-and-Delete。这些操作容许在写入数据之前进行查看,只有在满足指定条件的状况下才执行写入操作。

以上模式提供了不同的数据拜访形式,能够依据具体的需要和查问条件抉择适宜的形式来拜访和操作 HBase 中的数据。

架构体系

HBase 的架构体系是基于分布式存储和解决的设计。它蕴含了以下几个重要的组成部分:

  1. HMaster:HMaster 是 HBase 集群的主节点,负责管理整个集群的元数据和协调各个 RegionServer 的工作。它保护了表的构造信息、分片规定、RegionServer 的负载平衡等,并协调分布式操作,如 Region 的决裂和合并。
  2. RegionServer:RegionServer 是 HBase 集群中的工作节点,负责存储和解决数据。每个 RegionServer 治理多个 Region,每个 Region 负责存储表中的一部分数据。RegionServer 解决客户端的读写申请,负责数据的存储、读取和写入操作。
  3. ZooKeeper:ZooKeeper 是一个分布式协调服务,被 HBase 用于治理集群的元数据和协调分布式操作。HBase 应用 ZooKeeper 来进行主节点的选举、故障检测、集群配置的同步等工作。
  4. HDFS(Hadoop Distributed File System):HBase 应用 HDFS 作为底层的分布式文件系统,用于存储数据。HDFS 将数据宰割成块并散布在不同的节点上,提供高可靠性和可扩展性的存储。
  5. HBase 客户端:HBase 客户端是与 HBase 交互的应用程序或工具,用于发送读写申请和接管查问后果。客户端能够通过 HBase 的 Java API 或者命令行工具(如 HBase shell)来拜访和操作 HBase 表。
  6. 表和列族:HBase 数据模型是基于表的,表由一个或多个列族(Column Family)组成。每个列族能够蕴含多个列(Column),列存储着理论的数据。表被宰割成多个 Region 存储在不同的 RegionServer 上,每个 Region 负责存储一部分行数据。

这些组成部分独特形成了 HBase 的架构体系,实现了分布式存储和解决大规模数据集的能力。HMaster 负责管理元数据和协调工作,RegionServer 存储和解决数据,ZooKeeper 提供分布式协调服务,HDFS 提供底层的分布式文件存储,而 HBase 客户端用于与 HBase 进行交互。表和列族的概念提供了数据的组织和存储形式。

HBase 组件

  1. MemStore:每个 RegionServer 都有一个 MemStore,它是位于内存中的长期数据存储区域。当客户端写入数据时,数据首先被写入到 MemStore 中,以提供疾速的写入性能
  2. WAL(Write-Ahead-Log):WAL 是 HBase 的日志文件,用于记录所有的写操作。当数据被写入到 MemStore 时,相应的写操作也会被写入 WAL 中,以保证数据的持久性和故障恢复能力。
  3. StoreFile:当 MemStore 中的数据达到肯定大小阈值后,会被刷新到磁盘上的 StoreFile 中。StoreFile 是 HBase 中理论长久化存储数据的文件模式,它蕴含了曾经写入的数据和相应的索引
  4. HFile:HFile 是 StoreFile 的底层存储格局,采纳了块索引和工夫范畴索引的形式,提供了高效的数据查找和扫描能力。HFile 应用块(Block)来组织数据,并采纳压缩和编码技术来减小存储空间。

MemStore 提供了长期的内存存储,StoreFile 提供了长久化的磁盘存储,WAL 用于保证数据的持久性。这种架构设计使得 HBase 可能提供高可用性、高性能和可扩展性的分布式存储和解决能力。

HBase 读写流程

读流程

  1. 客户端发送读取申请:客户端向 HBase 集群发送读取申请,包含所需的表名、行键(Row Key)以及其余可选的参数(如列族、列限定符等)。
  2. 定位 RegionServer 和 Region:HBase 的客户端会与 ZooKeeper 进行通信,获取到存储有所需数据的 Region 所在的 RegionServer 的信息。
  3. RegionServer 解决申请:客户端发送的读取申请达到对应的 RegionServer,RegionServer 会依据申请的行键定位到蕴含所需数据的 Region。
  4. 数据读取:RegionServer 首先会从 MemStore 中查找数据,如果数据在 MemStore 中找到,则间接返回给客户端。如果数据不在 MemStore 中,RegionServer 会在磁盘上的 StoreFile 中进行查找,依据索引定位到所需的数据块,并将数据块读取到内存中进行解决
  5. 数据返回给客户端:RegionServer 将读取到的数据返回给客户端,客户端能够依据须要对数据进行进一步的解决和剖析。

写流程

  1. 客户端发送写入申请:客户端向 HBase 集群发送写入申请,包含表名、行键、列族、列限定符和对应的值等信息。
  2. 定位 RegionServer 和 Region:客户端与 ZooKeeper 通信,获取存储指标数据的 Region 所在的 RegionServer 的信息。
  3. RegionServer 解决申请:客户端发送的写入申请达到对应的 RegionServer,RegionServer 依据行键定位到指标 Region。
  4. 写入到 MemStore:RegionServer 将写入申请中的数据写入到指标 Region 对应的内存中的 MemStore。写入到 MemStore 是一个追加操作,将数据追加到内存中的 MemStore 中,并不间接写入磁盘
  5. WAL 日志记录:同时,RegionServer 将写入申请中的操作写入 WAL(Write-Ahead-Log)日志文件,确保数据的持久性和故障恢复能力
  6. MemStore 刷新到磁盘:当 MemStore 中的数据达到肯定的大小阈值时,RegionServer 会将 MemStore 中的数据刷新到磁盘上的 StoreFile 中。刷新过程将内存中的数据写入到磁盘上的 StoreFile,并生成相应的索引。
  7. 数据返回给客户端:写入实现后,RegionServer 向客户端发送写入胜利的响应,示意数据已胜利写入。

MemStore Flush

在 HBase 中,MemStore Flush 是将内存中的数据刷新到磁盘上的 StoreFile 的过程。当 MemStore 中的数据达到肯定大小阈值时,或者达到了肯定的工夫限度,HBase 会触发 MemStore Flush 操作,以将数据长久化到磁盘,确保数据的持久性和可靠性

上面是 MemStore Flush 的根本过程:

  1. MemStore Flush 触发:当 MemStore 中的数据量达到肯定的阈值(由配置参数管制)或者达到了肯定的工夫限度时,HBase 会触发 MemStore Flush 操作。这个阈值和工夫限度能够依据需要进行配置,以均衡写入性能和数据持久性的要求。
  2. 写入内存快照:在触发 Flush 操作时,HBase 会先将 MemStore 中的数据做一个内存快照(Snapshot),以保障在 Flush 期间持续接管新的写入申请。
  3. 刷写到磁盘:内存快照实现后,HBase 会将内存中的数据依照列族的维度划分为多个 KeyValue,而后将这些 KeyValue 写入磁盘上的 StoreFile。StoreFile 采纳 HFile 格局,用于长久化存储数据。
  4. 更新 Region 元数据:实现刷写到磁盘后,HBase 会更新 Region 的元数据,包含最新的 StoreFile 列表和相应的工夫戳等信息。
  5. MemStore 清空:一旦数据刷写到磁盘上的 StoreFile,HBase 会清空相应的 MemStore,以开释内存空间用于接管新的写入申请。

通过 MemStore Flush 操作,HBase 能够将内存中的数据长久化到磁盘,以确保数据的持久性和可靠性。Flush 操作的频率和老本能够通过配置参数进行调整,以适应不同的利用场景和性能需求。频繁的 Flush 操作可能会影响写入性能,而较长的 Flush 距离可能会减少数据失落的危险。因而,依据理论状况,须要正当设置 Flush 操作的参数,以均衡数据的持久性和写入性能的要求。

参数阐明

MemStore Flush 在 HBase 中由以下几个参数进行管制,它们的含意如下:

  1. hbase.hregion.memstore.flush.size:该参数指定了 MemStore 的大小阈值。当 MemStore 中的数据量达到或超过这个阈值时,将触发 MemStore Flush 操作。该参数的默认值为 128MB。这个参数在 HBase 0.98 版本及更高版本中失效。在旧版本中,相似的参数名为 hbase.hregion.memstore.flush.size.upper,但其含意和作用雷同。
  2. hbase.hregion.memstore.block.multiplier:该参数是用来设置 MemStore 大小阈值的倍数。当 MemStore 的大小超过 hbase.hregion.memstore.flush.size 乘以 hbase.hregion.memstore.block.multiplier 时,将触发 MemStore Flush 操作。默认值为 2。这个参数在 HBase 0.98 版本及更高版本中失效。
  3. hbase.hregion.memstore.flush.size.lower.limit:该参数定义了 MemStore 大小的上限限度。当 MemStore 中的数据量小于此上限时,不会触发 MemStore Flush 操作。该参数的默认值为 0。在 HBase 2.0 版本及更高版本中失效。
  4. hbase.hregion.memstore.flush.size.upper.limit:该参数定义了 MemStore 大小的下限限度。当 MemStore 中的数据量超过此下限时,将强制触发 MemStore Flush 操作。该参数的默认值为 Long.MAX_VALUE。在 HBase 2.0 版本及更高版本中失效。

上述的 1 和 2,满足任一条件都会触发 MemStore Flush 操作

这些参数须要依据具体的利用场景和性能要求进行正当的设置。较小的 Flush 阈值能够进步数据的持久性,但可能会减少 Flush 的频率和写入的开销;较大的 Flush 阈值能够缩小 Flush 的频率和开销,但可能会减少数据失落的危险。因而,须要依据利用的读写特色和数据的重要性,抉择适合的参数值。

StoreFile Compaction

StoreFile Compaction(文件合并)是 HBase 中的一个重要操作,它用于合并和优化存储在磁盘上的数据文件(StoreFile)。StoreFile Compaction 能够帮忙缩小磁盘空间占用、进步读取性能,并且在某些状况下能够进步写入性能。

StoreFile Compaction 的根本过程如下:

  1. Compact Selection(抉择合并):在进行 Compaction 之前,HBase 首先进行选择性合并。它会依据肯定的策略,如大小、工夫戳等,抉择一组须要合并的 StoreFile。这样能够限度合并的数据量,防止一次合并过多数据。
  2. Minor Compaction(小规模合并):Minor Compaction 次要合并较少数量的 StoreFile。它通过创立一个新的 StoreFile,并从多个旧的 StoreFile 中抉择合并的数据,将其合并到新的文件中。这个过程中,旧的 StoreFile 不会被删除,新的 StoreFile 会被创立并写入新的数据
  3. Major Compaction(大规模合并):Major Compaction 是一种更为综合和耗时的合并操作。它会合并一个或多个 HBase 表的所有 StoreFile。Major Compaction 将会创立一个新的 StoreFile,并将所有旧的 StoreFile 中的数据合并到新的文件中。与 Minor Compaction 不同,Major Compaction 还会删除旧的 StoreFile,从而开释磁盘空间
  4. Compaction Policy(合并策略):HBase 提供了不同的合并策略,能够依据数据特点和利用需要进行抉择。常见的合并策略包含 SizeTieredCompactionPolicy(按大小合并)和 DateTieredCompactionPolicy(按工夫戳合并)等。

通过 StoreFile Compaction,HBase 能够缩小磁盘上的存储空间占用,进步读取性能,同时合并操作还能够优化数据布局,减速数据的拜访。适合的合并策略的抉择能够依据数据的拜访模式和利用需要,以达到最佳的性能和存储效率。

参数阐明

StoreFile Compaction 过程中波及到的一些相干参数及其含意如下:

  1. hbase.hstore.compaction.min:指定了进行 Minor Compaction 的最小文件数。当 StoreFile 的数量达到或超过该值时,才会触发 Minor Compaction。默认值为 3。
  2. hbase.hstore.compaction.max:指定了进行 Major Compaction 的最大文件数。当 StoreFile 的数量超过该值时,将触发 Major Compaction。默认值为 10。
  3. hbase.hstore.compaction.ratio:指定了触发 Major Compaction 的比率。当一个 Region 中的 StoreFile 的总大小超过其最大文件大小的比率时,将触发 Major Compaction。默认值为 1.2。
  4. hbase.hstore.compaction.min.size:指定了进行 Compaction 的最小文件大小。当一个 StoreFile 的大小小于该值时,将不会参加 Compaction。默认值为 1 KB。
  5. hbase.hstore.compaction.max.size:指定了进行 Compaction 的最大文件大小。当一个 StoreFile 的大小超过该值时,将不会参加 Compaction。默认值为 Long.MAX_VALUE,即无限度。
  6. hbase.hstore.compaction.enabled:指定了是否启用 Compaction。如果设置为 false,则不会触发任何 Compaction 操作。默认值为 true。
  7. hbase.hstore.compaction.checker.interval.multiplier:指定了进行 Compaction 查看的工夫距离。理论查看的工夫距离为 hbase.hstore.compaction.checker.interval.multiplier 乘以 StoreFile 的均匀大小。默认值为 1.0。

这些参数能够在 HBase 的配置文件(hbase-site.xml)中进行设置。通过调整这些参数的值,能够依据数据量、存储需要和性能要求来优化 Compaction 操作的触发条件和行为。

触发过程

以下是判断是否触发 Compaction 的过程:

  1. 判断是否满足进行 Minor Compaction 的条件:

    • 查看 StoreFile 的数量是否达到或超过 hbase.hstore.compaction.min。如果是,则满足触发 Minor Compaction 的条件。
  2. 判断是否满足进行 Major Compaction 的条件:

    • 查看 StoreFile 的数量是否超过 hbase.hstore.compaction.max。如果是,则满足触发 Major Compaction 的条件。

    或者

    • 计算 StoreFile 的总大小与最大文件大小之间的比率。如果超过 hbase.hstore.compaction.ratio,即 StoreFile 的总大小超过最大文件大小的比率,那么满足触发 Major Compaction 的条件。
  3. 对于行将进行 Compaction 的 StoreFile:

    • 查看 StoreFile 的大小是否在 hbase.hstore.compaction.min.size 和 hbase.hstore.compaction.max.size 之间。如果不在这个范畴内,则该文件将不会参加 Compaction。
  4. 查看是否启用 Compaction:

    • 查看 hbase.hstore.compaction.enabled 的值是否为 true。如果为 false,则不会触发任何 Compaction 操作。
  5. 判断触发 Compaction 的工夫距离:

    • 依据 hbase.hstore.compaction.checker.interval.multiplier 乘以 StoreFile 的均匀大小,得出理论的查看工夫距离。

依据以上判断过程,HBase 在每个 RegionServer 上的每个 Store(列族)会依据配置参数进行定期的 Compaction 查看。一旦满足触发 Compaction 的条件,相应的 Minor Compaction 或 Major Compaction 将被触发,合并和优化存储的数据文件。这样能够进步读取性能、节俭磁盘空间,并且在某些状况下能够进步写入性能。

Region Split

Region Split(区域分割)是 HBase 中的一个重要操作,它用于在数据增长过程中,将一个较大的 HBase 表的 Region(区域)划分成更小的子区域,以进步读写性能和负载平衡。

当一个 Region 的大小达到了事后配置的阈值时,HBase 将触发 Region Split 操作。Region Split 的根本过程如下:

  1. Split Policy(宰割策略):HBase 提供了多种宰割策略,用于决定何时触发 Region Split。常见的宰割策略包含按大小宰割(Size-based Split)和按行数宰割(Row-count-based Split)。这些策略能够依据数据特点和利用需要进行抉择。
  2. Split Selection(抉择宰割点):在触发宰割之前,HBase 首先抉择一个适当的宰割点。宰割点是指一个 RowKey,它将成为宰割后的两个子区域的边界。抉择宰割点的策略能够是依据大小、行数或其余自定义逻辑进行抉择。
  3. Region Split(区域分割):一旦抉择了宰割点,HBase 将通过创立两个新的子区域来执行宰割操作。原始的 Region 将被拆分成两个子区域,每个子区域负责存储宰割点两侧的数据。同时,HBase 会为新的子区域生成新的 Region ID,并更新元数据信息。

常见的区域分割形式包含:

  1. 平均宰割(Even Split):将一个 Region 平均地划分为两个子区域。宰割点依据数据大小或行数进行抉择,以放弃两个子区域的大小相近。
  2. 预分区(Pre-splitting):在创立表时,能够提前定义多个宰割点,将表划分为多个初始的子区域。这样能够在表创立之初就实现数据的平衡散布,防止后续的动静宰割
  3. 自定义宰割(Custom Split):依据具体的业务需要和数据特点,能够通过自定义逻辑来抉择宰割点,实现更灵便的宰割形式。

通过正当地应用区域分割,能够充分利用集群资源,进步读写性能和负载平衡能力。不同的宰割策略和宰割形式能够依据数据规模、拜访模式和利用需要进行抉择,以满足不同场景下的需要。

预分区

在 HBase 中进行预分区能够通过 HBase Shell 或 HBase API 进行操作。以下是应用 HBase Shell 进行预分区的示例:

  1. 关上 HBase Shell:

    $ hbase shell
  2. 创立表并指定分区:

    hbase(main):001:0> create 'my_table', 'cf', {SPLITS => ['a', 'b', 'c']}

    上述命令创立了一个名为 my_table 的表,并指定了三个分区点:’a’、’b’ 和 ‘c’。这将创立四个初始的子区域。

  3. 查看表的分区状况:

    hbase(main):002:0> describe 'my_table'

    这将显示表的详细信息,包含分区信息。

通过上述步骤,你能够在创立表时事后定义分区点,从而实现预分区。每个分区点将成为一个子区域的边界,确保数据在表创立时就能散布在多个子区域中,从而实现负载平衡和性能优化。

请留神,上述示例是应用 HBase Shell 进行预分区的简略示例。如果须要在编程中进行预分区,能够应用 HBase API,例如 Java API,通过在创立表时设置 SPLITS 参数来指定分区点。

以下是应用 HBase Java API 进行预分区的示例代码:

import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;

public class PreSplitExample {public static void main(String[] args) throws IOException {
        // 创立 HBase 配置
        org.apache.hadoop.conf.Configuration config = HBaseConfiguration.create();

        // 创立 HBase 连贯
        try (Connection connection = ConnectionFactory.createConnection(config)) {
            // 创立 HBase 管理器
            try (Admin admin = connection.getAdmin()) {
                // 定义表名
                TableName tableName = TableName.valueOf("my_table");

                // 定义分区点
                byte[][] splitKeys = {Bytes.toBytes("a"),
                        Bytes.toBytes("b"),
                        Bytes.toBytes("c")
                };

                // 创立表并指定分区
                admin.createTable(TableDescriptorBuilder.newBuilder(tableName)
                        .addColumnFamily(ColumnFamilyDescriptorBuilder.of("cf"))
                        .setSplitKeys(splitKeys)
                        .build());
            }
        }
    }
}

上述代码通过 HBase Java API 创立了一个名为 my_table 的表,并指定了三个分区点:’a’、’b’ 和 ‘c’。这将创立四个初始的子区域。

请留神,在应用 Java API 进行预分区时,须要先建设与 HBase 的连贯,并通过 HBase 管理器(Admin)执行表的创立操作,并设置 setSplitKeys(splitKeys) 办法来指定分区点。

通过上述示例代码,你能够在编程中应用 HBase Java API 实现预分区性能。

HBase 优化

查问优化

设置 Scan 缓存

在 HBase 中,能够通过设置 Scan 对象的 setCaching() 办法来调整 Scan 缓存的大小。Scan缓存用于指定每次扫描操作从 RegionServer 返回给客户端的行数。通过调整缓存大小,能够在肯定水平上控制数据的读取性能和网络传输的开销。

以下是设置 Scan 缓存的示例代码:

Scan scan = new Scan();
scan.setCaching(500); // 设置缓存大小为 500 行

ResultScanner scanner = table.getScanner(scan);
for (Result result : scanner) {// 解决扫描后果}
scanner.close();

在上述示例中,setCaching()办法将缓存大小设置为 500 行。能够依据理论需要调整这个值,须要依据数据大小、网络带宽和性能要求进行衡量。较大的缓存大小能够缩小客户端与 RegionServer 之间的通信次数,进步读取性能,但同时也会减少内存耗费。较小的缓存大小能够缩小内存耗费,但可能会减少通信次数和网络传输开销。

须要留神的是,setCaching()办法设置的是每次扫描的缓存大小,并不是全局的设置。如果须要对整个表的扫描操作失效,须要在每次扫描时都设置缓存大小。

此外,还能够通过调整 HBase 的配置参数来全局设置缓存大小。在 hbase-site.xml 配置文件中增加以下参数能够设置默认的缓存大小:

<property>
  <name>hbase.client.scanner.caching</name>
  <value>500</value> <!-- 设置默认的缓存大小为 500 行 -->
</property>

以上是通过代码和配置文件来设置 Scan 缓存大小的办法,依据具体的利用场景和需要,能够抉择适当的形式进行设置。

显示指定列

当应用 Scan 或者 GET 获取大量的行时,最好指定所须要的列,因为服务端通过网络传输到客户端,数据量太大可能是瓶颈。如果能无效过滤局部数据,能很大水平的缩小网络 I / O 的破费。

在 HBase 中,能够应用 ScanGet操作来显示指定的列。上面别离介绍两种形式的用法:

  1. 应用 Scan 操作显示指定列:
Scan scan = new Scan();
scan.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col1")); // 指定列族 (cf) 和列(col1)

ResultScanner scanner = table.getScanner(scan);
for (Result result : scanner) {byte[] value = result.getValue(Bytes.toBytes("cf"), Bytes.toBytes("col1"));
    // 解决列 (col1) 的值
}
scanner.close();

在上述示例中,应用 scan.addColumn() 办法来指定要显示的列族和列。在 for 循环中,通过 result.getValue() 办法获取指定列的值。

  1. 应用 Get 操作显示指定列:
Get get = new Get(Bytes.toBytes("row1")); // 指定行键(row1)
get.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col1")); // 指定列族 (cf) 和列(col1)

Result result = table.get(get);
byte[] value = result.getValue(Bytes.toBytes("cf"), Bytes.toBytes("col1"));
// 解决列 (col1) 的值

在上述示例中,应用 get.addColumn() 办法来指定要显示的列族和列。通过 table.get() 办法获取行数据,并通过 result.getValue() 办法获取指定列的值。

无论是应用 Scan 还是 Get,都能够通过addColumn() 办法来指定要显示的列族和列。能够依据具体的需要,屡次调用 addColumn() 办法来显示多个列。

须要留神的是,HBase 中的列是以字节数组(byte[])模式示意的,因而在应用 addColumn()getValue()办法时,须要将列族和列名转换为字节数组。

禁用块缓存

如果批量进行全表扫描,默认是有缓存的,如果此时有缓存,会升高扫描的效率。

在 HBase 中,能够通过设置 Scan 对象的 setCacheBlocks() 办法来禁用块缓存。块缓存是 HBase 中的一种缓存机制,用于放慢数据的读取操作。然而,在某些状况下,禁用块缓存可能是无益的,例如对于某些热点数据或者须要立刻获取最新数据的场景。

以下是禁用 Scan 块缓存的示例代码:

Scan scan = new Scan();
scan.setCacheBlocks(false); // 禁用块缓存

ResultScanner scanner = table.getScanner(scan);
for (Result result : scanner) {// 解决扫描后果}
scanner.close();

在上述示例中,setCacheBlocks(false)办法将禁用 Scan 操作的块缓存。

须要留神的是,禁用块缓存可能会减少对 HBase 存储的理论磁盘读取次数,并且在一些场景下可能导致性能降落。因而,在禁用块缓存之前,倡议认真评估利用需要和场景,确保禁用块缓存的决策是正当的。

对于常常读到的数据,倡议应用默认值,开启块缓存。

写入优化

设置 AutoFlush

Htable 有一个属性是 AutoFlush,该属性用于反对客户端的批量更新,默认是 true,当客户端每收到一条数据,立即发送到服务端,如果设置为 false,当客户端提交 put 申请时候,先将该申请在客户端缓存,达到阈值的时候或者执行 hbase.flushcommits(),才向 RegionServer 提交申请。

在 HBase 中,能够通过设置 Table 对象的 setAutoFlush() 办法来管制主动刷新(AutoFlush)行为。AutoFlush 决定了在何时将数据从客户端发送到 RegionServer 并写入到存储中。

以下是设置 AutoFlush 的示例代码:

// 创立 HBase 配置对象
Configuration conf = HBaseConfiguration.create();

// 创立 HBase 连贯
Connection connection = ConnectionFactory.createConnection(conf);

// 获取表对象
TableName tableName = TableName.valueOf("your_table_name");
Table table = connection.getTable(tableName);

// 设置 AutoFlush
table.setAutoFlush(false);  // 敞开 AutoFlush

// 执行写入操作
Put put = new Put(Bytes.toBytes("row1"));
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col1"), Bytes.toBytes("value1"));
table.put(put);

// 手动刷新数据
table.flushCommits();  // 手动刷新数据到 RegionServer

// 敞开表和连贯
table.close();
connection.close();

在上述示例中,table.setAutoFlush(false)办法将敞开 AutoFlush。这意味着在执行写操作时,数据不会立刻被刷新到 RegionServer 和存储中,而是先缓存在客户端的内存中。只有当调用 table.flushCommits() 办法时,数据才会被手动刷新到 RegionServer。

须要留神的是,敞开 AutoFlush 能够进步写入性能,尤其是在批量写入或者频繁写入的场景中。然而,敞开 AutoFlush 也会减少数据在客户端内存中的暂存工夫,并减少了数据失落的危险。因而,在敞开 AutoFlush 时,须要在适当的机会手动调用 flushCommits() 办法来确保数据的持久性。

同时,还能够通过设置 table.setWriteBufferSize() 办法来指定客户端写缓冲区的大小。这能够帮忙在缓存中存储更多的数据,缩小刷新到 RegionServer 的次数,进步写入性能。例如:

table.setWriteBufferSize(1024 * 1024); // 设置写缓冲区大小为 1MB

在上述示例中,将写缓冲区大小设置为 1MB。

总之,通过设置 table.setAutoFlush(false)table.setWriteBufferSize()办法,能够管制 AutoFlush 行为和客户端写缓冲区大小,以优化写入性能和数据刷新的策略。依据具体的利用需要和场景,能够进行适当的配置调整。

参数优化

Zookeeper 会话超时工夫

属性:zookeeper.session.timeout

解释:默认值为 90000 毫秒(90s)。当某个 RegionServer 挂掉,90s 之后 Master 能力察觉到。可适当减小此值,尽可能快地检测 regionserver 故障,可调整至 20-30s。看你能有都能忍受超时,同时能够调整重试工夫和重试次数

hbase.client.pause(默认值 100ms)

hbase.client.retries.number(默认 15 次)

设置 RPC 监听数量

属性:hbase.regionserver.handler.count

解释:默认值为 30,用于指定 RPC 监听的数量,能够依据客户端的申请数进行调整,读写申请较多时,减少此值。

手动管制 Major Compaction

属性:hbase.hregion.majorcompaction

解释:默认值:604800000 秒(7 天),Major Compaction 的周期,若敞开主动 Major Compaction,可将其设为 0。如果敞开肯定记得本人手动合并,因为大合并十分有意义。

优化 HStore 文件大小

属性:hbase.hregion.max.filesize

解释:默认值 10737418240(10GB),如果须要运行 HBase 的 MR 工作,能够减小此值,因为一个 region 对应一个 map 工作,如果单个 region 过大,会导致 map 工作执行工夫。过长。该值的意思就是,如果 HFile 的大小达到这个数值,则这个 region 会被切分为两个 Hfile。

优化 HBase 客户端缓存

属性:hbase.client.write.buffer

解释:默认值 2097152bytes(2M)用于指定 HBase 客户端缓存,增大该值能够缩小 RPC 调用次数,然而会耗费更多内存,反之则反之。个别咱们须要设定肯定的缓存大小,以达到缩小 RPC 次数的目标。

指定 scan.next 扫描 HBase 所获取的行数

属性:hbase.client.scanner.caching

解释:用于指定 scan.next 办法获取的默认行数,值越大,耗费内存越大。

SpringBoot 中应用 HBase

增加 Maven 依赖:

<!-- HBase 2.4.3 依赖 -->
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>2.4.3</version>
</dependency>

配置 HBase 连贯:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;

@Configuration
public class HBaseConfig {
    @Bean
    public Connection hbaseConnection() throws IOException {Configuration config = HBaseConfiguration.create();
        config.set("hbase.zookeeper.quorum", "localhost");  // HBase ZooKeeper 地址
        config.set("hbase.zookeeper.property.clientPort", "2181");  // HBase ZooKeeper 端口
        return ConnectionFactory.createConnection(config);
    }
}

编写增删改查代码:

import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class HBaseService {

    @Autowired
    private Connection hbaseConnection;

    // 增加数据
    public void putData(String tableName, String rowKey, String columnFamily, String column, String value) throws IOException {Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
        Put put = new Put(Bytes.toBytes(rowKey));
        put.addColumn(Bytes.toBytes(columnFamily), Bytes.toBytes(column), Bytes.toBytes(value));
        table.put(put);
        table.close();}

    // 删除数据
    public void deleteData(String tableName, String rowKey) throws IOException {Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
        Delete delete = new Delete(Bytes.toBytes(rowKey));
        table.delete(delete);
        table.close();}

    // 获取数据
    public String getData(String tableName, String rowKey, String columnFamily, String column) throws IOException {Table table = hbaseConnection.getTable(TableName.valueOf(tableName));
        Get get = new Get(Bytes.toBytes(rowKey));
        Result result = table.get(get);
        byte[] valueBytes = result.getValue(Bytes.toBytes(columnFamily), Bytes.toBytes(column));
        table.close();
        return Bytes.toString(valueBytes);
    }
}

在上述代码中,HBaseConfig 类配置了 HBase 连贯,通过 hbaseConnection() 办法创立 HBase 连贯。HBaseService 类提供了 putData()deleteData()getData() 办法,别离用于插入数据、删除数据和获取数据。

Scan

以下是应用 Scan 操作的示例代码:

import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;

public class HBaseScanExample {public static void main(String[] args) throws IOException {
        // 创立 HBase 配置对象
        Configuration conf = HBaseConfiguration.create();

        // 创立 HBase 连贯
        Connection connection = ConnectionFactory.createConnection(conf);

        // 获取表对象
        TableName tableName = TableName.valueOf("your_table_name");
        Table table = connection.getTable(tableName);

        // 创立 Scan 对象
        Scan scan = new Scan();
        scan.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("col1")); // 指定要查问的列族和列

        // 执行 Scan 操作
        ResultScanner scanner = table.getScanner(scan);
        for (Result result : scanner) {
            // 解决每一行数据
            byte[] row = result.getRow();
            byte[] value = result.getValue(Bytes.toBytes("cf"), Bytes.toBytes("col1"));
            System.out.println("Row key:" + Bytes.toString(row) + ", Value:" + Bytes.toString(value));
        }

        // 敞开资源
        scanner.close();
        table.close();
        connection.close();}
}

在上述代码中,首先创立 HBase 配置对象 Configuration,而后通过 ConnectionFactory 创立 HBase 连贯 Connection。接下来,通过连贯获取表对象 Table,指定要进行 Scan 操作的表名。而后创立 Scan 对象,并应用 addColumn 办法指定要查问的列族和列。最初,应用 getScanner 办法执行 Scan 操作,并遍历 ResultScanner 获取每一行的数据,并进行解决。

Phoenix

Phoenix 是一个开源的基于 Apache HBase 的关系型数据库引擎,它提供了 SQL 接口来拜访 HBase 中存储的数据。它在 HBase 的根底上增加了 SQL 查问和事务性能,使得应用 HBase 的开发者能够应用相熟的 SQL 语言进行数据操作和查问

Phoenix 在 HBase 中的主要用途包含:

  1. SQL 查问:Phoenix 容许开发者应用规范的 SQL 语句来查问和操作 HBase 中的数据,无需编写简单的 HBase API 代码。这简化了开发过程,升高了应用 HBase 进行数据拜访的门槛。
  2. 索引反对:Phoenix 提供了对 HBase 数据的二级索引反对,开发者能够应用 SQL 语句创立索引,从而放慢查问速度。索引在数据查问和过滤中起到重要的作用,进步了数据的检索效率。
  3. 事务反对:Phoenix 引入了基于 MVCC(多版本并发管制)的事务机制,使得在 HBase 中进行简单的事务操作成为可能。开发者能够通过 Phoenix 的事务性能来保证数据的一致性和可靠性。
  4. SQL 函数和聚合:Phoenix 反对各种内置的 SQL 函数和聚合函数,如 SUM、COUNT、MAX、MIN 等,使得在 HBase 上进行数据统计和剖析变得更加不便。

要在 HBase 中应用 Phoenix,须要先装置并配置好 Phoenix。以下是一个在 HBase 中应用 Phoenix 的示例代码:

  1. 增加 Maven 依赖:在 Maven 我的项目的 pom.xml 文件中增加以下依赖:
<!-- Phoenix 依赖 -->
<dependency>
    <groupId>org.apache.phoenix</groupId>
    <artifactId>phoenix-core</artifactId>
    <version>4.16.0-HBase-2.4</version>
</dependency>
  1. 创立 Phoenix 表:在 HBase 中创立 Phoenix 表。能够应用 Phoenix 提供的 SQL 语法创立表和定义模式。例如,创立一个名为 users 的表:
CREATE TABLE users (
    id BIGINT PRIMARY KEY,
    name VARCHAR,
    age INTEGER
);
  1. 应用 Phoenix 进行操作:在 Java 代码中,能够应用 Phoenix 提供的 PhoenixConnectionPhoenixStatement 来执行 SQL 操作。
import java.sql.*;

public class PhoenixExample {public static void main(String[] args) throws SQLException {
        // 创立 Phoenix 连贯
        String url = "jdbc:phoenix:<HBase ZooKeeper Quorum>:<HBase ZooKeeper Port>";
        Connection connection = DriverManager.getConnection(url);

        // 执行 SQL 查问
        String query = "SELECT * FROM users";
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery(query);

        // 解决查问后果
        while (resultSet.next()) {long id = resultSet.getLong("ID");
            String name = resultSet.getString("NAME");
            int age = resultSet.getInt("AGE");
            System.out.println("ID:" + id + ", Name:" + name + ", Age:" + age);
        }

        // 敞开资源
        resultSet.close();
        statement.close();
        connection.close();}
}

在上述代码中,须要将 <HBase ZooKeeper Quorum><HBase ZooKeeper Port> 替换为你的 HBase ZooKeeper 地址和端口。

通过创立 PhoenixConnection 并传递正确的 JDBC URL,能够取得连贯对象。接下来,能够应用 createStatement() 办法创立 PhoenixStatement 对象,并应用 executeQuery() 办法执行 SQL 查问。

而后,能够应用 ResultSet 对象遍历查问后果,并提取所需的字段。在此示例中,遍历了 users 表的后果,并打印了每行的 ID、Name 和 Age。


本篇文章就到这里,感激浏览,如果本篇博客有任何谬误和倡议,欢送给我留言斧正。文章继续更新,能够关注公众号第一工夫浏览。

退出移动版