3-HBase体系结构
结构体系
部署架构
一个HBase集群由一个Master(也可以把两个Master做成 High Available)和多个RegionServer组成。
HBase有两种服务器:Master服务器和RegionServer服务器。
-
Master服务器负责维护表结构信息
-
RegionServer服务器负责存储实际的数据,他们直接存储在Hadoop的HDFS上
- RegionServer非常依赖ZooKeeper服务
- ZooKeeper管理了HBase所有RegionServer的信息,包括具体的数据段存放在哪个RegionServer上。
体系架构
HBase中的组件包括Client、Zookeeper、HMaster、HRegionServer、HRegion、Store、MemStore、StoreFile、HFile、HLog等
Client
当客户端从ZooKeeper获取RegionServer的地址后,它会直接从RegionServer获取或操作(插入、删除)数据,而不经过Master。
Client 访问用户数据前需要首先访问 ZooKeeper,找到-ROOT-表的 Region 所在的位置,然后访问-ROOT-表,接着访问.META.表,最后才能找到用户数据的位置去访问。
-
.META.:记录了用户所有表拆分出来的的 Region 映射信息,.META.可以有多个 Regoin
-
-ROOT-:记录了.META.表的 Region 信息,-ROOT-只有一个 Region,无论如何不会分裂
寻址方式
三层查询(HBase-0.96 版本以前)
- 用户通过查找zk(ZooKeeper) 的/hbase/root-regionserver节点来知道-ROOT-表在什么RegionServer上
- Client 请求-ROOT-所在的 RegionServer 地址,获取.META.表的地址,Client 会将-ROOT-的相关信息 cache 下来,以便下一次快速访问
- Client 请求.META.表的 RegionServer 地址,获取访问数据所在 RegionServer 的地址, Client 会将.META.的相关信息 cache 下来,以便下一次快速访问
- Client 请求访问数据所在 RegionServer 的地址,获取对应的数据
总结:用户需要 3 次请求才能直到用户 Table 真正的位置,这在一定程序带来了性能的下降。
二层查询(HBase-0.96 版本以后)
使用 3 层设计的主要原因是考虑到元数据可能需要很大。
每行 METADATA 数据存储大小为 1KB 左右,如果按照一个 Region 为 128M 的计算,3 层设计可以支持的 Region 个数为 2^34 个,采用 2 层设计可以支持 2^17(131072)。使用 2 层设计的情况下,一个集群可以存储 4P 的数据。这仅仅是一个 Region 只有 128M 的情况下。 2 层设计就可以满足集群的需求,因此在 0.96 版本以后就去掉 了-ROOT-表了。
- 客户端先通过ZooKeeper的/hbase/meta-region-server节点查询到哪台RegionServer上有hbase:meta表。
- 客户端连接含有hbase:meta表的RegionServer。 hbase:meta表存储了所有Region的行键范围信息, 通过这个表就可以查询出你要存取的rowkey属于哪个Region的范围里面, 以及这个Region又是属于哪个RegionServer。
- 获取这些信息后, 客户端就可以直连其中一台拥有你要存取的rowkey的RegionServer, 并直接对其操作。
- 客户端会把meta信息缓存起来,下次操作就不需要进行以上加载hbase:meta的步骤了。
Zookeeper
元数据表hbase:meata的位置存储在ZooKeeper
HDFS
真正承载数据的载体
Master
Master只负责各种协调工作,如建表、删表、移动Region、合并等操作。
RegionServer
RegionServer就是存放Region的容器,直观上说就是服务器上的一个服务。RegionServer上有一个或者多个Region,数据就存储在Region上。
RegionServer的内部架构图
-
WAL(Write-Ahead Log):预写日志。当操作到达Region的时候,HBase先把操作写到WAL里面。 HBase会先把数据放到基于内存实现的Memstore里,等数据达到一定的数量时才刷写(flush)到最终存储的HFile内。
-
多个Region:Region相当于一个数据分片,每一个Region都有起始rowkey和结束rowkey,代表了它所存储的row范围。
Region
Region就是一段数据的集合。HBase中的表一般拥有一个到多个Region。
Region有以下特性:
- Region不能跨服务器,一个RegionServer上有一个或者多个Region。
- 数据量小的时候,一个Region足以存储所有数据;但是,当数据量大的时候,HBase会拆分Region。
- 当HBase在进行负载均衡的时候,也有可能会从一台RegionServer上把Region移动到另一台RegionServer上。
- Region是基于HDFS的,它的所有数据存取操作都是调用了HDFS的客户端接口来实现的。
一个Region就是多个行的集合。在Region中行的排序按照行键(rowkey)字典排序。
Region 内部结构图
一个Region包含有:
- 多个Store:每一个Region内都包含有多个Store实例。一个Store对应一个列族的数据,如果一个表有两个列族,那么在一个Region里面就有两个Store。在最右边的单个Store的解剖图上,我们可以看到Store内部有MemStore和HFile这两个组成部分。
Store
Store中有两个重要组成部分:
-
MemStore:每个Store中有一个MemStore实例。 数据写入WAL之后就会被放入MemStore。 MemStore是内存的存储对象, 只有当MemStore满了的时候才会将数据刷写(flush) 到HFile中
-
HFile:在Store中有多个HFile。 当MemStore满了之后HBase就会在HDFS上生成一个新的HFile, 然后把MemStore中的内容写到这个HFile中。HFile直接跟HDFS打交道,它是数据的存储实体。
Store内部结构图
MemStore
Memstore是存储在内存中,Memstore存在的意义是维持数据按照rowkey顺序排列,而不是做一个缓存
使用内存先把数据整理成顺序存放,然后再一起写入硬盘。
数据被写入WAL之后就会被加载到MemStore中去。 MemStore的大小增加到超过一定阀值的时候就会被刷写到HDFS上, 以HFile的形式被持久化起来。
HFile(StoreFile)
HFile是数据存储的实际载体,我们创建的所有表、列等数据都存储在HFile里面。HFile是由一个一个的块组成的。 在HBase中一个块的大小默认为64KB, 由列族上的BLOCKSIZE属性定义。
MemStore刷写而成的文件叫HFile, StoreFile就是HFile的抽象类
HFile的组成
- Data (数据块) :每个HFile有多个Data块。存储HBase表中的数据。可选的
- Meta(元数据块):Meta块是可选的,Meta块只有在文件关闭的时候才会写入。Meta块存储了该HFile文件的元数据信息
- FileInfo(文件信息):只有在文件关闭的时候写入。存储的是这个文件的信息,比如最后一个Key(Last
Key) ,平均的Key长度(Avg Key Len)等。必选的 - DataIndex(存储Data块索引信息的块文件):索引的信息也就是Data块的偏移值(offset)。可选的
- MetaIndex(存储Meta块索引信息的块文件):可选的
- Trailer:存储了FileInfo、 DataIndex、 MetaIndex块的偏移值。必选的
Data数据块
Data数据块的第一位存储的是块的类型, 后面存储的是多个KeyValue键值对, 也就是单元格(Cell)的实现类。 Cell是一个接口,KeyValue是它的实现类。
- BlockType(块类型)
- DATA
- ENCODED_DATA
- LEAF_INDEX
- BLOOM_CHUNK
- META
- INTERMEDIATE_INDEX
- ROOT_INDEX
- FILE_INFO
- GENERAL_BLOOM_META
- DELETE_FAMILY_BLOOM_META
- TRAILER
- INDEX_V1
KeyValue类
一个KeyValue类里面最后一个部分是存储数据的Value,而前面的部分都是存储跟该单元格相关的元数据信息。如果你存储的value很小,那么这个单元格的绝大部分空间就都是rowkey、columnfamily、column等的元数据,所以大家的列族和列的名字如果很长,大部分的空间就都被拿来存储这些数据了。
采用适当的压缩算法就可以极大地节省存储列族、 列等信息的空间。压缩和解压必然带来性能损耗
KeyValue类结构图
数据存储顺序
WAL是存储在HDFS上的, Memstore是存储在内存中的, HFile也是存储在HDFS上的。
数据是先写入WAL, 再被放入Memstore, 最后被持久化到HFile中。
预写日志(Log)
预写日志(Write-ahead log,WAL) 就是设计来解决宕机之后的操作恢复问题的。数据到达Region的时候是先写入WAL,然后再被加载到Memstore的。WAL的数据是存储在HDFS。
关闭或打开WAL特性
WAL是默认开启的
Mutation.setDurability(Durability.SKIP_WAL)
-- 方法来设置和获取字段的值
Mutation.getDurability()
- 在HBase的Java API中,指定写入时不使用WAL log
Put put = new Put(rowKey);
put.setWriteToWAL(false);
- 在HBase shell中,关闭一张表的WAL log
disable 'TABLE_NAME'
alter 'TABLE_NAME', DURABILITY => 'SKIP_WAL'
enable 'TABLE_NAME'
延迟(异步)同步写入WAL
Mutation.setDurability(Durability.ASYNC_WAL)
DURABILITY可选的值有ASYNC_WAL, FSYNC_WAL, SKIP_WAL, SYNC_WAL(默认值), USE_DEFAULT等
- ASYNC_WAL
Write the Mutation to the WAL asynchronously - FSYNC_WAL
Write the Mutation to the WAL synchronously and force the entries to disk. - SKIP_WAL
Do not write the Mutation to the WAL - SYNC_WAL
Write the Mutation to the WAL synchronously. - USE_DEFAULT
If this is for tables durability, use HBase's global default value (SYNC_WAL).
WAL 滚动
WAL的检查间隔由hbase.regionserver.logroll.period定义, 默认值为1小时。检查的内容是把当前WAL中的操作跟实际持久化到HDFS上的操作比较,看哪些操作已经被持久化了,被持久化的操作就会被移动到.oldlogs文件夹内
一个WAL实例包含有多个WAL文件。 WAL文件的最大数量通过hbase.regionserver.maxlogs(默认是32) 参数来定义
触发滚动条件
- 当WAL文件所在的块(Block) 快要满了
- 当WAL所占的空间大于或者等于某个阀值,该阀值的计算公式:
hbase.regionserver.hlog.blocksize * hbase.regionserver.logroll.multiplier
- hbase.regionserver.hlog.blocksize: 存储系统的块大小,若基于HDFS,则设定为HDFS的块大小
- hbase.regionserver.logroll.multiplier:默认值0.95。即当WAL文件所占的空间大于或者等于95%的块大小, 则这个WAL文件就会被归档到.oldlogs文件夹内
WAL归档
WAL文件被创建出来后会放在/hbase/.log,一旦WAL文件被判定为要归档,则会被移动到/hbase/.oldlogs文件夹。
当这个WAL不需要作为用来恢复数据的备份时,Master会负责定期地去清理.oldlogs文件夹。
引用WAL文件的场景:
- TTL进程: 该进程会保证WAL文件一直存活直到达到hbase.master.logcleaner.ttl定义的超时时间(默认10分钟) 为止
- 备份(replication) 机制: 如果你开启了HBase的备份机制,那么HBase要保证备份集群已经完全不需要这个WAL文件了,才会删除这个WAL文件。
只有当该WAL文件没有被以上两种情况引用时, 才会被系统彻底地删除掉。
数据存储架构
结构图
表结构图
行结构
Namespace(表命名空间)
表命名空间的作用是把多个属于相同业务领域的表分成一个组。
在建表时可以添加上命名空间(
保留表空间
HBase中预定义2个保留表空间
- HBase: 系统表空间,用于HBase内部表
- default: 那些没有定义表空间的表都被自动分配到这个表空间下
Table(表)
一个表由一个或者多个列族组成
Row(行)
一个行包含了多个列,这些列通过列族来分类。一行中的数据可以分布在不同的服务器上。
Row key
rowkey行键可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),最好是16。在 HBase 内部,rowkey 保存为字节数组。每个行(row)都拥有唯一的行键(row key)来确定其唯一性。rowkey就是决定row存储顺序和位置的唯一凭证。
HBase中无法根据某个column来排序,只能根据rowkey来排序(字典顺序)
column
最基本的存储单位是列(column),一个列或者多个列形成一行(row)。
在HBase中,行跟行的列可以完全不一样,这个行的数据跟另外一个行的数据也可以存储在不同的机器上,甚至同一行内的列也可以存储在完全不同的机器上。
Hbase只支持3中查询方式
1、基于Rowkey的单行查询
2、基于Rowkey的范围扫描
3、全表扫描
Column Family
若干列可以组成列族(Column Family)。Hbase通过列族划分数据的存储,列族下面可以包含任意多的列,实现灵活的数据存取。
Hbase表的创建的时候就必须指定列族,不需要指定列。
Hbase的列族不是越多越好,官方推荐的是列族最好小于或者等于3。我们使用的场景一般是1个列族。
表属性(如:过期时间,数据块缓存,是否压缩等属性)都是定义在列簇上。
在HBase中一个列的名称前面总是带着他所属的列族,它的格式是( 列族:列名 )。
TimeStamp(时间戳/版本号)
在Hbase中使用不同的timestame来标识相同rowkey行对应的不同版本的数据。用来标定同一个列中多个单元格的版本号。
版本通过时间戳来索引。时间戳的类型是 64 位整型。时间戳可以由hbase(在数据写入时自动)赋值,它是精确到毫秒的当前系统时间。时间戳也可以由用户显式赋值。每个 cell 中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
多版本数据管理
hbase 提供了两种数据版本回收方式
- 保存数据的最后 N个版本
- 保存最近一段时间内的版本(设置数据的生命周期 TTL)
Cell(单元格)
一个列上可以存储多个版本的值,每个版本就称为单元格(Cell)。多个版本的值被存储到多个单元格中,多个版本之间用timestame(Version)来区分。因此,由行键:列族:列:版本号(rowkey:column family:column:version)来唯一确定一条结果。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)