1 Hbase 概述
1.1 Hbase 定义
HBase 是一种 分布式、可扩展、支持海量数据存储 的 NoSQL 数据库,支持对大数据进行随机、实时的读/写访问。
NoSQL数据库(非关系型数据库)是一种不同于传统关系型数据库的数据库管理系统。它们使用灵活的数据模型,不遵循传统的表格关系模式,而是采用键值对(如Redis)、文档型(如MongoDB)、列族存储(如HBase)、图形数据库(如Neo4j)等各种数据模型。非关系型数据库主要用于存储和处理大量分散的数据,具有高性能、高可扩展性和高可用性的特点。
Hadoop中的 HDFS 是不支持随机修改数据的,但可以追加写。HBase 底层基于Hadoop,为什么就可以支持对数据的随机读写了呢?让我们继续往下学习
1.2 HBase 数据模型
逻辑上,HBase 的数据模型同关系型数据库很类似,数据存储在一张表中,有行有列。但从 HBase 的底层物理存储结构(K-V)来看,HBase更像是一个 multi-dimensional map。
1.2.1 HBase 逻辑结构
一个 Region 的数据是存在一个机器上的,一个 Region 的多个 store 是存储在不同的文件中的。
1.2.2 HBase 物理存储结构
对于Row Key 、Column Family 和 Column Qualifier 相同的数据,可以根据最新的时间戳来确定最终的值。
这对于前面的问题就有了答案。
为啥 HBase 支持对数据的随机修改? 其实 HBase 底层基于 Hadoop,是不支持随机修改数据的。想要修改数据时,HBase 会对新数据进行末尾追加操作,并不会真正修改原始的旧数据,HBase 通过时间戳来标识这几个版本的数据,以最新的时间戳为最终的数据值。对于旧数据,HBase 会在合适的时机进行清理。
1.2.3 数据模型
(1)Name Space
命名空间,类似于关系型数据库的 database 概念,每个命名空间下有多个表。HBase有两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是HBase内置的表,default 表是用户默认使用的命名空间。
(2)Table
类似于关系型数据库的表概念。不同的是,HBase 定义表时只需要声明列族即可,不需要声明具体的列。这意味着,往HBase写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase能够轻松应对字段变更的场景。
(3)Row
HBase表中的每行数据都由一个 RowKey 和多个Column(列)组成,数据是按照RowKey 的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以RowKey 的设计十分重要。
(4)Column
HBase中的每个列都由Column Family(列族)和Column Qualifier(列限定符)进行限定,例如info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。
(5)Time Stamp
用于标识数据的不同版本(version),每条数据写入时,系统会自动为其加上该字段,其值为写入HBase的时间。
(6)Cell
由 {rowkey, column Family:column Qualifier, time Stamp}
唯一确定的单元。cell 中的数据全部是 字节码 形式存贮。
1.3 HBase 基本架构
架构角色:
(1)Region Server
Region Server为 Region 的管理者,其实现类为 HRegionServer,主要作用如下:
- 对于数据的操作:get, put, delete;
- 对于Region的操作:splitRegion、compactRegion。
(3)Master
Master是所有 Region Server 的管理者,其实现类为 HMaster,主要作用如下:
- 对于表的操作:create, delete, alter
- 对于 RegionServer 的操作:分配 regions 到每个 RegionServer,监控每个 RegionServer 的状态,负载均衡和故障转移。
(4)Zookeeper
HBase通过Zookeeper来做 master 的高可用、RegionServer 的监控、元数据的入口以及集群配置的维护等工作。
(5)HDFS
HDFS为Hbase提供最终的底层数据存储服务,同时为HBase提供高可用的支持。
2 HBase Shell 操作
2.1 基本操作
(1)进入HBase 客户端命令行
[huwei@hadoop102 hbase-2.0.5]$ bin/hbase shell
(2)查看帮助命令
hbase(main):001:0> help
hbase(main):001:0> help 'create_namespace'
2.2 namespace 操作
(1)查看当前 Hbase 中有哪些 namespace
hbase(main):002:0> list_namespace
(2)创建 namespace
hbase(main):003:0> create_namespace "test"
hbase(main):004:0> create_namespace "test01", {"author"=>"huwei", "create_time"=>"2024-03-6 17:38:08"}
(3)查看 namespace
hbase(main):005:0> describe_namespace "test01"
(4)修改 namespace 的信息(添加或者修改属性)
hbase(main):007:0> alter_namespace "test01", {METHOD => 'set', 'author' => 'hw'}
添加或者修改属性:
alter_namespace 'ns1', {METHOD => 'set', 'PROPERTY_NAME' => 'PROPERTY_VALUE'}
删除属性:
alter_namespace 'ns1', {METHOD => 'unset', NAME => ' PROPERTY_NAME '}
(5)删除 namespace
hbase(main):008:0> drop_namespace "test01"
注意: 要删除的namespace必须是空的,其下没有表。
2.3 表操作
(1)查看当前数据库中有哪些表
hbase(main):009:0> list
(2)创建表
create 'student','info'
如果不指定命名空间,新建表则默认在 default 下
在 已存在 的命名空间中新建表
hbase(main):013:0> create 'test:tab','f1','f2'
二者等价
hbase(main):015:0> create 'test:tab1', {NAME => 'f1'}, {NAME => 'f2'}, {NAME => 'f3'}
(3)插入数据到表
hbase(main):017:0> put 'student','1001','info:sex','male'
hbase(main):018:0> put 'student','1001','info:age','18'
hbase(main):019:0> put 'student','1002','info:name','Janna'
hbase(main):020:0> put 'student','1002','info:sex','female'
hbase(main):021:0> put 'student','1002','info:age','20'
(4)变更表信息
将 info 列族中的数据存放3个版本:
hbase(main):022:0> alter 'student',{NAME=>'info',VERSIONS=>3}
(5)扫描查看表数据
hbase(main):023:0> scan 'student'
hbase(main):024:0> scan 'student',{STARTROW => '1001', STOPROW => '1001'}
hbase(main):025:0> scan 'student',{STARTROW => '1001'}
(6)查看表结构
hbase(main):026:0> describe 'student'
(7)更新指定字段的数据
hbase(main):027:0> put 'student','1001','info:name','Nick'
hbase(main):028:0> put 'student','1001','info:age','100'
(8)查看“指定行”或“指定列族:列”的数据
hbase(main):030:0> get 'student','1001'
hbase(main):031:0> get 'student','1001','info:name'
(9)统计表数据行数
hbase(main):033:0> count 'student'
(10)删除数据
删除某 rowkey 的全部数据:
hbase(main):034:0> deleteall 'student','1001'
删除某 rowkey 的某一列数据:
hbase(main):035:0> delete 'student','1002','info:sex'
(11)清空表数据
hbase(main):036:0> truncate 'student'
注意:清空表的操作顺序为先disable(不用自己操作),然后再truncate。
(12)删除表
首先需要先让该表为disable状态:
hbase(main):037:0> disable 'student'
然后才能drop这个表:
hbase(main):038:0> drop 'student'
注意:如果直接drop表,会报错:ERROR: Table student is enabled. Disable it first.
3 HBase 原理深入
3.1 RegionServer 架构
WAL 和 StoreFile 都存储在 HDFS 中。
(1)StoreFile
保存实际数据的物理文件,StoreFile 以 Hfile 的形式存储在 HDFS 上。每个Store会有一个或多个StoreFile(HFile),数据在每个 StoreFile 中都是有序的。
(2)MemStore
写缓存,由于 HFile 中的数据要求是有序的,所以数据是先存储在 MemStore 中,排好序后,等到达刷写时机才会刷写到 HFile,每次刷写都会形成一个新的 HFile。
(3)WAL
由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile 的文件中,然后再写入 MemStore 中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
(4)BlockCache
读缓存,每次查询出的数据会缓存在 BlockCache 中,方便下次查询。
3.2 HBase 写流程
想知道
hbase:meta
表 由哪一个 Region Server 维护,问 Zookeeper。
想知道 其他表 由哪一个 Region Server 维护,问hbase:meta
表。
写流程:
(1)Client 先访问 zookeeper ,获取 hbase:meta
表位于哪个Region Server。
(2)访问对应的 Region Server,获取 hbase:meta
表,根据读请求的 namespace:table/rowkey,查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的 meta cache,方便下次访问。
(3)与目标 Region Server 进行通讯;
(4)将数据顺序写入(追加)到 WAL;
(5)将数据写入对应的 MemStore,数据会在 MemStore 进行排序;
(6)向客户端发送 ack;
(7)等达到 MemStore 的刷写时机后,将数据刷写到HFile。
3.3 MemStore Flush
MemStore 刷写时机:
(1)当某个 memstore 的大小达到了 hbase.hregion.memstore.flush.size
(默认值128M),其所在region的所有memstore都会刷写。每次刷写都会有一个新的文件产生。
当 memstore 的大小达到了 hbase.hregion.memstore.flush.size(默认128M) * hbase.hregion.memstore.block.multiplier(默认4)
时,会阻止继续往该memstore写数据。
(2) 当 region server 中 memstore 的总大小达到
java_heapsize *hbase.regionserver.global.memstore.size(默认0.4) *hbase.regionserver.global.memstore.size.lower.limit(默认0.95)
,region 会按照其所有 memstore 的大小顺序(由大到小)依次进行刷写。直到 region server 中所有 memstore 的总大小减小到上述值以下。当 region server 中 memstore的总大小达到
java_heapsize*hbase.regionserver.global.memstore.size(默认0.4)
时,会阻止继续往所有的memstore写数据。
(3)到达自动刷写的时间,也会触发 memstore flush。自动刷新的时间间隔由该属性进行配置hbase.regionserver.optionalcacheflushinterval(默认1小时)
。
(4)当WAL文件的数量超过 hbase.regionserver.max.logs
,region会按照时间顺序依次进行刷写,直到WAL文件数量减小到 hbase.regionserver.max.logs
以下(该属性名已经废弃,现无需手动设置,最大值为32)。
3.4 HBase 读流程
整体流程
Merge 细节
布隆过滤器能告诉你绝对不存在,但是不能告诉你存在。
读流程
(1)Client 先访问 zookeeper,获取 hbase:meta
表位于哪个Region Server。
(2)访问对应的Region Server,获取 hbase:meta
表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个Region Server中的哪个Region中。并将该 table 的region信息以及meta表的位置信息缓存在客户端的 meta cache,方便下次访问。
(3)与目标Region Server进行通讯;
(4)分别在 MemStore 和 StoreFile(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。
(5)将查询到的新的数据块(Block,HFile 数据存储单元,默认大小为64KB)缓存到Block Cache。
(6)将合并后的最终结果返回给客户端。
整体上来看,HBase写数据的操作只需要把数据写入内存就算完成,反而读数据要从文件开始读。所以,对于HBase,会呈现出写数据比读数据更快的效果。
3.5 StoreFile Compaction
StoreFile Compaction 即 Regin 内部 StoreFile 的合并。
由于 memstore 每次刷写都会生成一个新的 HFile,且同一个字段的不同版本(timestamp)和不同类型(Put/Delete)有可能会分布在不同的 HFile 中,因此查询时需要遍历所有的 HFile。为了减少 HFile 的个数,以及清理掉过期和删除的数据,会进行 StoreFile Compaction。
Compaction分为两种,分别是Minor Compaction 和 Major Compaction。
- Minor Compaction会将临近的若干个较小的 HFile 合并成一个较大的 HFile,并清理掉部分过期和删除的数据。(每次 flush 都会小合并)
- Major Compaction会将一个Store下的所有的 HFile 合并成一个大 HFile,并且会清理掉所有过期和删除的数据。(定时的,默认7天一次)
3.6 Region Split
默认情况下,每个 Table 起初只有一个 Region,随着数据的不断写入,Region 会自动进行拆分。刚拆分时,两个子 Region 都位于当前的Region Server,但处于负载均衡的考虑,HMaster 有可能会将某个 Region 转移给其他的 Region Server。
Region Split 时机:
(1)当1个region中的某个Store下所有 StoreFile 的总大小超过 hbase.hregion.max.filesize(默认10G)
,该Region就会进行拆分(0.94版本之前)。
(2)当1个region中的某个Store下所有StoreFile的总大小超过Min(initialSize*R^2 ,hbase.hregion.max.filesize")
,该Region就会进行拆分。其中 initialSize
的默认值为hbase.hregion.memstore.flush.size(默认128M)
,R 为当前 Region Server 中属于该 Table 的 Region个数(0.94版本之后)。
具体的切分策略为:
- 第一次split:1^3 * 256 = 256MB
- 第二次split:2^3 * 256 = 2048MB
- 第三次split:3^3 * 256 = 6912MB
- 第四次split:4^3 * 256 = 16384MB > 10GB,因此取较小的值10GB
后面每次split的size都是10GB了。
(3)Hbase 2.0引入了新的 split 策略:如果当前 RegionServer 上该表只有一个 Region,按照2 * hbase.hregion.memstore.flush.size
分裂,否则按照 hbase.hregion.max.filesize
分裂。
4 HBase API
暂时略。
5 HBase 优化
5.1 预分区
每一个 region 维护着 startRow 与 endRowKey ,如果加入的数据符合某个 region 维护的 rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。(既然一定要 Region Split,为什么不提前分好呢?)
(1)手动设定预分区
hbase(main):057:0> create 'staff1','info',SPLITS => ['1000','2000','3000','4000']
(2)生成16进制序列预分区
hbase(main):058:0> create 'staff2','info',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
(3)按照文件中设置的规则预分区
创建 splits.txt
文件
[huwei@hadoop101 hbase-2.0.5]$ vim splits.txt
内容如下:
aaaa
bbbb
cccc
dddd
hbase(main):059:0> create 'staff3','info',SPLITS_FILE => 'splits.txt'
即使我们文件中设置的RowKey不是按顺序的,Hbase也会自动帮我们排好序。
(4)使用 JavaAPI 创建预分区
5.2 RowKey 设计
一条数据的唯一标识就是 rowkey,那么这条数据存储于哪个分区,取决于 rowkey 处于哪个一个预分区的区间内,设计 rowkey 的主要目的 ,就是让数据均匀的分布于所有的 region 中,在一定程度上防止数据倾斜。接下来我们就谈一谈rowkey常用的设计方案。
(1)生成随机数、hash、散列值
(2)字符串反转
这样也可以在一定程度上散列逐步put进来的数据。
(3)字符串拼接
5.3 内存优化
HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,但是不建议分配非常大的堆内存,因为GC(Garbage Collection)过程持续太久会导致 RegionServer 处于长期不可用状态,一般16~36G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。
5.4 基础优化
(1)Zookeeper 会话超时时间
hbase-site.xml
属性:zookeeper.session.timeout
解释:默认值为90000毫秒(90s)。当某个RegionServer挂掉,90s之后Master才能察觉到。可适当减小此值,以加快Master响应,可调整至60000毫秒。
(2)设置 RPC 监听数量
hbase-site.xml
属性:hbase.regionserver.handler.count
解释:默认值为30,用于指定 RPC 监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。
RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,允许在网络上的不同计算机之间进行通信和调用远程服务。通过RPC,一个计算机程序可以请求另一个计算机上的程序或服务执行特定的操作,就像调用本地函数一样,而无需了解底层网络细节。
在HBase中,RPC 用于客户端与 RegionServer 之间的通信,客户端发送读写请求到RegionServer,RegionServer 收到请求后执行相应的操作并返回结果给客户端。属性"hbase.regionserver.handler.count"指定了RegionServer用于处理这些RPC请求的处理器数量。增加此值可以提高RegionServer同时处理请求的能力,特别是在读写请求较多的情况下,适当增加处理器数量可以提升系统的并发处理能力和性能。
(3)手动控制Major Compaction
hbase-site.xml
属性:hbase.hregion.majorcompaction
解释:默认值:604800000秒(7天), Major Compaction的周期,若关闭自动Major Compaction,可将其设为0
(4)优化HStore文件大小
hbase-site.xml
属性:hbase.hregion.max.filesize(1个region中的某个Store下所有 StoreFile 的总大小)
解释:默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值,因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个Hfile。
(5)优化HBase客户端缓存
hbase-site.xml
属性:hbase.client.write.buffer
解释:默认值2097152bytes(2M)用于指定HBase客户端缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存,反之则反之。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。
(6)指定scan.next扫描HBase所获取的行数
hbase-site.xml
属性:hbase.client.scanner.caching
解释:用于指定scan.next方法获取的默认行数,值越大,消耗内存越大。
(7)BlockCache占用RegionServer堆内存的比例
hbase-site.xml
属性:hfile.block.cache.size
解释:默认0.4,读请求比较多的情况下,可适当调大
(8)MemStore占用RegionServer堆内存的比例
hbase-site.xml
属性:hbase.regionserver.global.memstore.size
解释:默认0.4,写请求较多的情况下,可适当调大
6 整合Phoenix
暂时略。
7 与 Hive 的集成
暂时略。