HBase总结

HBase

1. HBase核心概念

HBase 的作用

HBase 主要用于存储和管理超大规模的结构化或半结构化数据(如 PB 级),特点包括:

  • 高扩展性:通过分布式架构横向扩展,支持数千台服务器
  • 高吞吐量:适合实时随机读写(如用户行为日志、实时分析)
  • 强一致性:保证同一行数据的原子性操作
  • 灵活的数据模型:支持动态列和稀疏存储

典型应用场景

  • 互联网公司的用户行为日志存储(如点击流数据)
  • 社交媒体的实时消息存储
  • 物联网设备时序数据管理

Region(区域)
  • 定义:HBase 表中数据的物理分片,按 RowKey 范围划分

  • 特点:

    • 每个表初始时由一个 Region 构成,随着数据增长自动分裂
    • Region 由 RegionServer 管理,分布在集群的不同节点上,实现负载均衡
    • 每个 Region 默认大小为 10GB(可配置),超出后触发分裂
  • 示例:

    假设用户表的 RowKey 是用户 ID(如user001user999),Region 可能按 ID 范围划分:

    • Region 1:user001user500
    • Region 2:user501user999

RowKey(行键)
  • 定义:每行数据的唯一标识符,类似关系数据库的主键,但设计更灵活
  • 设计原则:
    1. 唯一性:确保不同行的 RowKey 不重复
    2. 散列分布:避免热点问题(如使用哈希前缀:MD5(userID)_timestamp
    3. 有序性:按字典序排序,影响数据在 Region 中的分布
  • 常见模式:
    • 时间戳反转reverse_timestamp + uniqueID(避免时间顺序导致热点)
    • 组合键userID_actionType_timestamp(支持多维查询)

示例
存储用户订单数据时,RowKey 可设计为:
userId_反转时间戳_orderIduser123_87654321_oder456


列族(Column Family)
  • 定义:逻辑上相关的列的集合,物理存储的基本单元

  • 特点:

    • 列族在表创建时定义,后续无法修改
    • 每个列族独立存储(对应 HDFS 上的 StoreFile),支持不同的压缩、版本控制策略
    • 建议列族不超过 3 个(避免性能下降)
  • 设计示例:

    用户信息表可定义两个列族:

    BASHuser_info:
      - 列族1:basic(基础信息,如 name, age)
      - 列族2:extend(扩展信息,如 address, preferences)
    

核心概念关系图
TEXTHBase Table
├── RowKey (主键)
├── Region (水平分片)
│   └── RegionServer (物理节点管理)
└── Column Family (逻辑列分组)
    ├── Column Qualifier 1 (动态列)
    └── Column Qualifier 2

最佳实践
  1. RowKey 设计:避免使用连续值(如时间戳),优先使用散列或随机前缀
  2. 列族优化:高频查询的列放在同一列族,减少 IO 开销
  3. Region 监控:通过 HBase Shell 或 Web UI 监控 Region 大小和分布
  4. 压缩配置:对历史数据列族启用 Snappy 或 GZIP 压缩

通过合理设计 RowKey 和列族,结合 Region 的动态分裂机制,HBase 能够高效支持海量数据存储与实时查询

HBase Region的分割

HBase中的表格可以根据行键水平分割为一个或几个region。

每个region中包含了一段处于某一起始键值和终止键值之间的连续的行键。
每一个region的默认大小为10GB
相应的Region server负责向客户端提供访问某一region中的数据的服务
每一个Region server能够管理大约1000个region(这些region可能来自同一个表格,也可能来自不同的表格)

每一个表格最初都对应于一个region。随着region中数据量的增加,region会被分割成两个子region。每一个子region中存储原来一半的数据。同时Region server会通知HMaster这一分割。出于负载均衡的原因,HMaster可能会将新产生的region分配给其他的Region server管理(这也就导致了Region server服务远端数据的情况的产生)

什么是列式存储&列式存储和行式存储的区别

列存储不同于传统的关系型数据库,其数据在表中是按行存储的,列方式所带来的重要好处之一就是,由于查询中的选择规则是通过列来定义的,因此整个数据库是自动索引化的
按列存储每个字段的数据聚集存储,在查询只需要少数几个字段的时候,能大大减少读取的数据量,一个字段的数据聚集存储,那就更容易为这种聚集存储设计更好的压缩/解压算法

传统的行存储和列存储的区别:
关系性数据库
1、数据是按行存储的
2、没有索引的查询使用大量I/O
3、建立索引和物化视图需要花费大量时间和资源
4、面对查询的需求,数据库必须被大量膨胀才能满足性能需求

列式数据库
1、数据按列存储–每一列单独存放
2、数据即是索引
3、只访问查询涉及的列–大量降低系统IO
4、每一列由一个线索来处理–查询的并发处理
5、数据类型一致,数据特征相似–高效压缩

2.HBase核心原理

HBase合并

合并原因
HBase不停的刷写,导致存储目录中有过多的数据文件,文件太多会导致维护困难、降低数据查询性能和效率。对一堆的文件进行I/O操作,耗时太多。所以HBase定期会对这些琐碎的文件进行整理,即合并Compaction。

合并原理
分为三步:排序文件、合并文件、代替原文件服务。
HBase首先从待合并的文件中读出HFile中的key-value,再按照由小到大的顺序写入一个新文件(storeFile)中。这个新文件将代替所有之前的文件,对外提供服务。

合并类别
1.Minor Compaction(小合并)
小合并是指将相邻的StoreFile合并为更大的StoreFile。

2.Major Compaction(大合并)
大合并是将多个StoreFile合并为一个StoreFile。

HBase合并时,清空以下三种数据
1.标记为删除的数据。
当我们删除数据时,HBase并没有把这些数据立即删除,而是将这些数据打了一个个标记,称为“墓碑”标记。在HBase合并时,会将这些带有墓碑标记的数据删除。

2.TTL过期数据
TTL(time to live)指数据包在网络中的时间。如果列族中设置了TTL过期时间,则在合并的过程中,发现过期的数据将被删除。

3.版本合并
若版本号超过了列族中预先设定的版本号,则将最早的一条数据删除。
如:列族设置版本号是5,当此列族第六次保存数据时,会将最早一次数据删除。

触发时机
MEMStore Flush
内存中的数据flush刷写到硬盘上以后,会对当前Store中的文件进行判断,当数量达到阈值,则会触发Compaction。Compaction是以Store为单位进行合并的。当Flush刷写完成后,整个Region的所有Store都会执行Flush。

后台线程周期性的检查
Compaction Checker线程定期检查是否触发Compaction,Checker会优先检查文件数量是否大于阈值,再判断是否满足major Compaction的条件的时间范围内,如果满足,则触发一次大合并Major Compaction。
大合并条件:[7-70.5,7+7 0.5](单位:天) 通过参数hbase.hregion.majorcompaction和hbase.hregion.majorcompaction.jitter设置合并条件,分别对应7和0.5

手动触发
由于很多业务担心MajorCompaction影响读写性能,所以选择在低峰期手动触发合并。
当用户修改表结构后,希望立刻生效,则手动触发合并。
运维人员发现硬盘空间不够,则会手动触发合并,因为删除了过期数据,腾出空间。

MajorCompaction持续时间比较长,整个过程将消耗大量的系统资源,对上传业务有比较大的影响,
所以线上业务通过将自动触发MajorCompaction改为业务低峰期的手动触发,来优化Hbase系统。

HBase删除原理(HBase删除数据是立即删除吗)
HBase 的删除操作并不会立即将数据从磁盘上删除,这主要是因为 HBase 的数据通常被保存在 HDFS 之中,而 HDFS 只允许新增或者追加数据文件,所以删除操作主要是对要被删除的数据打上标记。

当执行删除操作时,HBase 新插入一条相同的 KeyValue 数据,但是使 keytype=Delete,这便意味着数据被删除了,直到发生 Major_compaction 操作时,数据才会被真正的从磁盘上删除。

hbase中通过row和columns确定的为一个存贮单元称为cell,每个 cell都保存着同一份数据的多个版本。在默认的情况下,HBase会存储三个版本的历史数据。

删除时机
1.flush: 删除未被刷写的被覆盖或有删除标记的数据。删除标记不会删,因为不知道hdfs中是否有被此删除标记作用的数据。

2.majorCompact: 多个文件加载到内存,将一个 Store 下的所有的 HFile 合并成一个大 HFile,并且会清理掉过期和有删除标记的数据。数据删除完毕后再删除删除标记。

Time To Live (TTL)
ColumnFamilies可以设置TTL长度(以秒为单位),HBase将在到期时间后自动删除行。这适用于行的所有版本,包括当前版本。
当Minor compaction操作时,仅删除包含过期行的存储文件。设置hbase.store.delete.expired.storefile为false禁用此功能。将最小版本数设置为0以外也会禁用此功能。
HBase还支持按每个单元格设置生存时间(TTL)。

Cell TTL处理和ColumnFamily TTL之间存在两个显着差异:
• Cell TTL以毫秒而不是秒为单位表示。
• Cell TTL的TTL不能超过ColumnFamily 的TTL。

Keeping Deleted Cells
设置 KEEP_DELETED_CELLS 为 True 的目的在于防止数据被物理删除。
KEEP_DELETED_CELLS 的作用就是在major compaction发生的时候,决定要不要清理旧数据。这里需要注意一点,即便 KEEP_DELETED_CELLS 设置为True,数据仍然会因为过期而被清理(HBsae表中的TTL属性)。
数据恢复的前提数据没有被物理删除,你只需要在查询(Scan)的时候,指定raw模式来搜索数据,就能看到被删除的数据,之后你要做就是把数据再写入一次。

即便不设置RAW,也可以通过时间搜索到被删数据。比如数据写入时间是T,delete时间是T+2,那么查找[0, T+1]的话就能看见数据。前提是设置了 KEEP_DELETED_CELLS=TRUE,如果你后续写入重复的Key,那你必须指定好TIMERANGE,不然你可能看到的不是原先删除的keyVlaue。
delete操作默认的时间不是当前server的时间,也不是构造Delete对象的时间,而是被删除的这个keyValue的写入时间。如果你的Delete 标记的时间和数据的时间一样,那只能通过RAW看到。

数据恢复完,建议关闭KEEP_DELETED_CELLS,节省空间,提高查询效率。
ZooKeeper在HBase中的作用

HBase利用ZooKeeper维护集群中服务器的状态并协调分布式系统的工作,其功能如下:
1、维护服务器是否存活,是否可访问的状态并提供服务器故障/宕机的通知
2、使用一致性算法来保证服务器之间的同步
3、负责Master选举的工作(集群中的服务器数目必须是奇数)

组件间的协作
ZooKeeper用来协调分布式系统的成员之间共享的状态信息。Region Server及HMaster也与ZooKeeper连接。ZooKeeper通过心跳信息为活跃的连接维持相应的ephemeral node(可查看易知网面试题 zk的节点类型)

每一个Region server都在ZooKeeper中创建相应的ephemeral node。HMaster通过监控这些ephemeral node的状态来发现正常工作的或发生故障下线的Region server

HMaster之间通过互相竞争创建ephemeral node进行Master选举。ZooKeeper会选出区中第一个创建成功的作为唯一一个活跃的HMaster。活跃的HMaster向ZooKeeper发送心跳信息来表明自己在线的状态。不活跃的HMaster则监听活跃HMaster的状态,并在活跃HMaster发生故障下线之后重新选举,从而实现了HBase的高可用性

如果Region server或者HMaster不能成功向ZooKeeper发送心跳信息,则其与ZooKeeper的连接超时之后与之相应的ephemeral node就会被删除。监听ZooKeeper状态的其他节点就会得到相应node不存在的信息,从而进行相应的处理。活跃的HMaster监听Region Server的信息,并在其下线后重新分配Region server来恢复相应的服务。不活跃的HMaster监听活跃HMaster的信息,并在起下线后重新选出活跃的HMaster进行服务

3.HBase基本操作

HBase导入数据有几种方式

HBase作为Hadoop DataBase,除了使用put进行数据导入之外,还有以下几种导入数据的方式:
1、使用importTsv功能将csv文件导入HBase;
使用importTsv功能可以将csv格式的文件导入到HBase中,格式:
hbase [类] [分隔符] [行键,列族] [表] [导入文件]

步骤:
1、创建f.csv文件 /user/f.csv
2、将文件上传至HDFS,并修改相应的读写权限
3、创建HBase表 test
4、执行MapReduce操作(将/user/f.csv文件导入到test表)
hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.separator=“,” -Dimporttsv.columns=HBASE_ROW_KEY,f1 test /user/f.csv

2、使用import功能,将数据导入HBase;
使用import功能进行数据导入,导入的文件必须是sequence文件。与import相对的还有export功能,export功能导出的文件为sequence文件。

步骤:
1、首先使用export功能将HBase表中的数据导出(导出到hdfs的/user/hbase/output目录)
hbase org.apache.hadoop.hbase.mapreduce.Export test /user/hbase/output

2、创建一个新的HBase表,新表的列族需跟导出表的列族一致

3、执行import的MapReduce(将/user/hbase/output目录下的文件导入到test2表)
hbase org.apache.hadoop.hbase.mapreduce.Import test2 /user/hbase/output

3、使用BulkLoad功能将数据导入HBase。
HBase支持BulkLoad的导入方式,它是利用HBase的数据信息按照特定格式存储在hdfs内这一原理,直接在HDFS中生成持久化的HFile数据格式文件,然后上传至适当位置,即完成巨量数据快速入库的办法。配合MapReduce完成,不占用region资源,在大数据量写入时,能极大的提高写入效率,并降低对HBase节点的写入压力。
通过使用先生成HFile,然后再BulkLoad到HBase的方式来导入数据有如下的好处:
1、消除了对HBase集群的插入压力;
2、提高了Job的运行速度,降低了Job的执行时间。

步骤:
1、通过importTsv生成HFile文件,此过程会主动创建HBase表和HDFS上对应的目录
hbase [类] [分隔符] [输出存储路径] [行键,列族] [表] [导入原始数据文件]

2、通过completebulkload将数据导入到HBase表
hadoop jar [hbase server jar包] completebulkload [HFile文件路径] [表名]

HBase查询方式

1、通过 get 方式,指定 RowKey 获取唯一一条记录
2、通过 scan 方式,设置 startRow 和 stopRow参数进行范围匹配
3、全表扫描,即直接扫描整张表中所有行记录

4.HBase表设计问题

HBase Rowkey设计

  • rowkey不宜过长,建议不超过16个字节,rowkey过长,memorystore会将部分数据存入内存降低内存利用率,降低检索效率,hfile进行数据持久化也会极大影响存储效率

  • rowkey散列,尽可能的做到rowkey散列,均匀分布到每个RegionServer,实现负载均衡,避免出现热点问题【随机数加盐 | rowkey_hash取值保证唯一性 | rowkey反转】

  • rowkey唯一性,rowkey是按照字典顺序存储的,避免将经常读取的数据存储在同一个regionserver

  • 尽量避免使用递增行键/时序数据,没办法做到负载均衡

  • rowkey长度越小越好,列名越小越好

  • long类型替代字符串类型存储

rowkey唯一及热点问题:

  • salt加盐,增加了写操作的吞吐量,但是增加了读操作多个region的开销

  • hash或mod,hash的方式比如MD5后取前四位做前缀

  • reverse反转,固定长度的rowkey反转存储,比如手机号可以反转存储

5.HBase热点问题及解决办法

热点现象
某个小的时段内,对HBase的读写请求集中到极少数的Region上,导致这些region所在的RegionServer处理请求量骤增,负载量明显偏大,而其他的RgionServer明显空闲。

热点现象出现的原因
HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。然而糟糕的rowkey设计是热点的源头。

热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。

热点现象解决办法
为了避免写热点,设计rowkey使得不同行在同一个region,但是在更多数据情况下,数据应该被写入集群的多个region,而不是一个。常见的方法有以下这些:

  • 加盐:在rowkey的前面增加随机数,使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。
  • 哈希:哈希可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据
  • 反转:第三种防止热点的方法时反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。反转rowkey的例子以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题
  • 时间戳反转:一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到key的末尾,例如[key][reverse_timestamp],[key]的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。
    比如需要保存一个用户的操作记录,按照操作时间倒序排序,在设计rowkey的时候,可以这样设计[userId反转] [Long.Max_Value - timestamp],在查询用户的所有操作记录数据的时候,直接指定反转后的userId,startRow是[userId反转][000000000000],stopRow是[userId反转][Long.Max_Value - timestamp]
    如果需要查询某段时间的操作记录,startRow是[user反转][Long.Max_Value - 起始时间],stopRow是[userId反转][Long.Max_Value - 结束时间]
  • HBase建表预分区:创建HBase表时,就预先根据可能的RowKey划分出多个region而不是默认的一个,从而可以将后续的读写操作负载均衡到不同的region上,避免热点现象。

6.HBase二级索引

索引 是一种数据结构,为了加速查询
HBase中的一级索引指,数据在写入region时,会根据rowkey进行排序后写入,之后regionserver在加载region时,会自动为当前region的rowkey创建一个LSM树的索引 !方便对当前region,rowkey的查询!

为什么需要HBase二级索引
HBase里面只有rowkey作为一级索引, 如果要对库里的非rowkey字段进行数据检索和查询, 往往要通过MapReduce/Spark等分布式计算框架进行, 硬件资源消耗和时间延迟都会比较高。
为了HBase的数据查询更高效、适应更多的场景, 诸如使用非rowkey字段检索也能做到秒级响应, 或者支持各个字段进行模糊查询和多字段组合查询等, 因此需要在HBase上面构建二级索引, 以满足现实中更复杂多样的业务需求。

二级索引
如果要固定查询一个hbase表中的某些列,可以针对这些列的数据,创建索引,可以在查询指定的数据时,快速定位到列的位置!

Phoenix二级索引特点:
1、Covered Indexes(覆盖索引) :把关注的数据字段也附在索引表上,只需要通过索引表就能返回所要查询的数据(列), 所以索引的列必须包含所需查询的列(SELECT的列和WHERE的列)。

2、Functional indexes(函数索引): 索引不局限于列,支持任意的表达式来创建索引。

3、Global indexes(全局索引):适用于读多写少场景。通过维护全局索引表,所有的更新和写操作都会引起索引的更新,写入性能受到影响。 在读数据时,Phoenix SQL会基于索引字段,执行快速查询。

4、Local indexes(本地索引):适用于写多读少场景。 在数据写入时,索引数据和表数据都会存储在本地。在数据读取时, 由于无法预先确定region的位置,所以在读取数据时需要检查每个region(以找到索引数据),会带来一定性能(网络)开销

你可能感兴趣的:(HBase总结)