Hbase笔记
简介
数据库分类
行式数据库
列式数据库
- 列式存储以列为单位聚合数据,然后将列值顺序存入磁盘(同一列族数据一般存储在同一个文件中,按列读取方便),同一列的数据一般是相似的,这样聚集在一起有利于压缩,像用增量压缩或前缀压缩等算法进行压缩。

HBase并不是一个列式存储的数据库,但 是它利用了磁盘上的列存储格式
HBase 既不像行式存储,又不像列式存储。它其实更像是面向列族的存储数据库,因为不同行相同的列族数据是相邻存储的;而同一行不同列族的数据是存储在不同位置的。
注意:HBase实际存储在hdfs上的形式:/hbase/data/default/tbl_name/region_id/cf/hfile_id
- 例子
![]()
文档型数据库
Hbase简介
JDK版本
- 2.0+ --> jdk8+
- 1.2+ --> jdk7+
背景
2003年谷歌发表了论文 The Google File System (GFS),GFS这个系统缺陷:只适合存储大文件,不适合存放许多小文件,因为元数据要存储在主节点的内存中,文件越多主节点压力越大,后来Google又在2006年发表了BigTable的相关论文,Hbase实现了BIgTable架构。
表、行、列、单元格
最基本的单位是列,若干列构成一个列族,一列或多列形成一行,并由唯一的行键来确定存储,反过来,一个表中有若干行,其中每列可能有多个版本(时间戳),在每一个单元格中存储了不同值
一行由若干列组成,若干列又构成一个列族(column family),这不仅有助于构建数据 的语义边界或者局部边界,还有助于给它们设置某些特性(如压缩),或者指示它们存 储在内存中。一个列族的所有列存储在同一个底层的存储文件里,行族建表时就要定义好,不能频繁修改,数量也不能太多,数量仅限于几十个内,列的数量没限制,列值也没有类型和长度限制
HBASE是一个稀疏的、分布式的、持久化 的、多维的映射,由行键、列键和时间戳索引。将以上特点联系在一起,我们就有了 如下的数据存取模式:(Table, RowKey, Family, Column, Timestamp) —> Value
行序是按行键的字典序进行排序的(按二进制逐字节从左到右依次对比)
- row-1
- row-11
- row -2
- row- 24
行键是唯一的,且只出现一次(可以通过行键更新数据),相当于关系型数据库的主键,行键可以是任意字节数组,并不一定直接可读
事务
行数据的存取操作是原子的,可以读写任意数目的列,目前还不支持跨行事务和跨表事务
自动分区
HBASE中扩展和负载均衡的基本单位是region(区域),region的本质是以行键排序的连续存储的区间(如:row1-500,row501-1000),如果region太大,系统会自动拆分,相反,也可以合并多个region,都是动态的。
按现在的硬件性能,每台服务器最佳加载region的数量大概在10~1000,每个region的最佳大小是1G~2G,具体视硬件性能而定,当某个region的服务器负载过大、或者出错导致不可用时,系统会将该region移到其他服务器上。
实现
* 数据存储在HDFS的文件中,HFile中存储的是经过排序的键值映射结构,HFile也存在HDFS中,文件内部由连续的块组成,块的索引信息存储在文件的尾部,每个块的默认大小是64KB
* 每一个HFile都有一个块索引,通过一个磁盘查找就可以实现査询。首先,在内存 的块索引中进行二分查找,确定可能包含给定键的块,然后读取磁盘块找到实际要 找的键
* 基于HDFS,所以享受了HDFS的可扩展性,持久性,冗余性
* HBASE主要有3个组件:客户端、一台主服务器、多台region服务器,可以动态移除、增加region服务器,用Zookeeper做协调
HBASE架构
数据查找和传输
采用LSM树,输入的数据首先被存储在日志文件中,这些文件内的数据完全有序,当有日志文件被修改时对应的更新会被先保存在内存中,当MemStore逐渐被占满后,LSM树会把有序数据写入磁盘中,内存中已保存的数据就可以丢弃了。查询会同时查询写入磁盘的数据和内存中还没写入磁盘的数据
存储

- HBASE使用一个HDFS中可配置的根目录,默认为“/hbase”来存储文件,也可以用不同的根目录来让多个HBase共享HDFS
表级文件 - 第一组文件是被HLog实例管理的WAL文件,这些日志文件被创建在HBase的根目录下一个名为.logs的目录中
- 每张表有自己的目录,位于文件系统的hbase目录下,每张表包括一个名为.tableinfo的顶层文件(一些表和列族的定义)和.tmp(包含一些临时数据)
region级文件
数据文件 ->// / / /
region目录中有一个.regioninfo文件 存放region相关信息,有一个可选.tem目录存放临时文件(如:数据压缩时的临时文件等)
WAL
高级用法
行键设计
概念
磁盘上同一个列族下的所有单元格被有序存储在同一个文件中,不同列族在不同文件中,hbase不存储没有值的单元格。
每个单元格一般会有时间戳信息,单元格按时间降序排列,每次读取先读到最新的数据,含有结构信息的整个单元格在HBASE中叫做KeyValue(包含:列、值、行键、时间戳)。
从左到右查询数据的性能变差,所以在keyValue设计时可以把一些重要的筛选信息往左移(如:userId-megId-date,缺点:如果需要修改同一个user下的全局属性就需要修改多行,不保证原子性),尽量将需要查询的维度或信息存储在行键中,因为它筛选效率最高
Rowkey 设计指南:https://www.iteblog.com/archives/2486.html | https://blog.csdn.net/weixin_42685589/article/details/81030159
为什么不建议使用过多列族:https://www.iteblog.com/archives/2474.html
高表与宽表
高表就是列少行多,宽表相反。Hbase只能按行分片,因此高表更有优势。
时间序列
当处理流式事件时,最常见的行键设计就是以时间来设计行键,会出现一个问题,这些数据会被存储到一个特定的范围,可能分配到一个region中,就只有一台机器管理,就发挥不了负载均衡。
解决方案:为行键添加一个不连续的前缀
高级模式
使用Avro等序列化方式以便存储复杂格式的数据
辅助索引
就是自己新建一张表,存放新的坐标和现有坐标(主表)的一个映射关系,也就是说可以存放主表的行键,在更新主表数据后也要同步更新辅助表的数据(推荐先写副表再写主表),由于HBASE不支持跨表事务,所以不能保证两张表的原子性,这个问题可以通过MapReduce程序来定时扫描维护。
事务
有支持事务的开源项目,ITHBase,还有开源的支持索引的 IHBase,或者使用Zookeeper做协调利用两阶段提交来保证事务
搜索集成
HBase用户受限于使用键或过滤器来筛选数据,或者遍历所有数据来查找需要的数据,一个普遍的需求是使用任意关键字来搜索数据,为了满足这种需求需要集成一个搜索引擎,如Lucene、Solr
布隆过滤器
版本管理
集群监控
性能优化
集群管理
客户端API
客户端缓冲区默认关闭,可以开启(通过setAutoFlush(false)),开启后数据达到缓冲阈值(缓冲区大小在配置文件中设置)就会自动flush,也可以手动调用flushCommits()方法。
一般请求数据量小,请求次数多适合开启缓冲区。可以减少RPC调用次数
CRUD
- put 增(支持传入list添加多个)
Put类部分方法:addColumn(),getFamilyCellMap(),getRow()等
- get 查 (支持传入list查询多个)
Get类部分方法:addColumn(),addFamily(),setFilter(),getRow(),getFamilyMap()等
- delete 删 (支持传入list删除多个)
批量处理
- batch 操作,(get(list),delete(list) 都是基于batch实现的),可以同时传入多个增删的数据。
行锁
客户端没传锁服务端会隐式创建一个锁,这个锁生存期短暂,客户端一般获取不到
除了服务器隐式加锁,客户端也可以显示的对单行数据进行加锁,(lockRow(),unlockRow()),默认锁超时时间1分钟,可在hbase-site.xml配置文件中设置
扫描(scan)
scan(),用户可以选择提供startRow和stopRow,[startRow,stopRow),不提供则查询全表。给定的row不一定必须精确匹配,扫描会匹配相等或大于给定的值。
另一个可选参数是过滤器,可以传入Filter进行过滤
Scan类部分方法:addColumn(),addFamily(),setFilter()等
创建好Scan实例后就可以调用Htable的getScanner()方法,获得数据结果ResultScanner,扫描不会一次返回所有数据,可能有成千上万条,需要迭代输出,调用方法next(),next(int nbRows)。扫描器积累多了耗资源,需及时关闭,close()可关闭,也可以配置过期时间,配置文件中可配置,next()也可以配置缓存,将多次RPC调用合并成一次
Bytes类
这个类可以将Java 中常见的类型与byte[]进行互转,方便操作HBase,因为HBase中都是以二进制数组保存的
其他特性
客户端(HTable)使用完后需调用close()关闭,close()方法会隐式调用flushCache()提交缓冲区中还没执行的修改操作
创建HTable时如果没提供表名,会默认读classpath下的配置文件寻找要操作的表名
过滤器
get()和scan()都支持过滤器,自带的过滤只能过滤列族,列,时间戳,版本号,通过添加过滤器可以支持更细粒度的过滤(比如过滤行键,列名,列值)。过滤器最基本的接口是Filter。
![]()
创建过滤器需要提供一些参数来设定过滤器的用途,其中有一组特殊的过滤器继承自CompareFilter,需要提供至少两个参数
比较过滤器(继承自CompareFilter,get,scan都支持)
继承自CompareFilter的过滤器比FilterBase多了一个compare()方法,需要传入参数定义比较操作的过程
除了提供一个比较运算符还需要提供一个比较器
比较的值是在实例化过滤器时,通过构造函数传入的
- 行过滤器
基于行键来过滤数据
例:Filter filter =
new RowFilter(CompareFilter.CompareOp.LESS_OR_EQUAL,new BinaryComparator(Bytes.toBytes("row-22"))) - 列族过滤器
列族过滤器与行过滤器相似
例:Filter filter =
new FamilyFilter(CompareFilter.CompareOp.EQUAL,new BinaryComparator(Bytes.toBytes("colfam3"))) - 列名过滤器
与上面的类似
例:Filter filter =
new QualifierFilter(CompareFilter.CompareOp.LESS_OR_EQUAL,new BinaryComparator(Bytes.toBytes("col-2"))) - 值过滤器
这个过滤器可以筛选特定值得单元格,用法与上面类似。
例:Filter filter = new ValueFilter(CompareFilter.CompareOp.EQUAL,new SubstringComparator(".4"))需要注意的是:特定比较器只能与部分运算符搭配,比如这里用的SubstringComparator只能搭配EQUAL和NOT_EQUAL
- 参考列过滤器
参考列过滤器允许用户指定一个参考列或引用列,使用参考列的时间戳过滤,过滤时包含所有与引用时间戳相同的列。
可以简单理解为:参考过滤器 = 值过滤器+ 时间戳过滤器。如果不指定值过滤器就只按时间戳进行过滤
专用过滤器(直接继承自FilterBase)
Hbase提供的第二类过滤直接继承自FilterBase,同时用于特定场景,其中一些的过滤器只能做行筛选,因此只适用于扫描操作(scan)
- 单列值过滤器(SingleColumnValueFilter)
用一列的值决定是否一行数据被过滤。与比较过滤器用法类似,会多传一个参考列的参数
例:
SingleColumnValueFilter filter =
new SingleColumnValueFilter(Bytes.toBytes("colfam1"),Bytes.toBytes("col-5"),
CompareFilter.CompareOp.NOT_EQUAL,new SubstringComparator("val-5")); - 单列排除过滤器(SingleColumnValueExcludeFilter)
继承自SingleColumnValueFilter ,与SingleColumnValueFilter 相反 - 前缀过滤器
在构造过滤器时传入一个前缀,所有与前缀匹配的行都会被返回
例:
Filter filter = new PrefixFilter(Bytes.toBytes("row-1")) - 分页过滤器
此过滤器可以对结果进行分页,创建时需指定pageSize参数
例:Filter filter = new PageFilter(10); - 行键过滤器(KeyOnlyFilter)
该过滤器只返回键,不返回值,构造时传入布尔值 - 首次行键过滤器
用户需访问一行中的第一列(HBase隐式排序),则可以用这种过滤器,通常用于行数统计,在列式存储库中某一行存在则必然有列,检查到有一列后扫描器就停止该行的扫描,所以效率比较高 - 包含结束的过滤器(InclusiveStopFilter)
构建时提供一个终止行,扫描到该行时结束
例: Filter filter = new InclusiveStopFilter(Bytes.toBytes("row-5")) - 时间戳过滤器(TimestampsFilter)
根据时间戳进行过滤
new TimestampsFilter(Listtimes) - 列计数过滤器
可以使用这个过滤器来限制每行最多可以取回多少列
new ColumnCountGetFilter(int n) - 列分页过滤器
与PageFilter类似,该过滤器可以对一行的所有列进行分页
new ColumnPaginationFilter(int limit,int offset) 跳过所有偏移量小于offset的列。并包括之后所有偏移量在limit之前的列 - 列前缀过滤器
类似PrefixFilter,该过滤器对列名称的前缀进行匹配 new ColumnPrefixFilter(byte[] prefix) - 随机行过滤器
该过滤器让结果返回随机行 new RandomRowFilter(float chance) chance取值在0.0~1.0
附加过滤器
- 跳转过滤器(SkipFilter)
该过滤器用来包装其他过滤器,只要其他过滤器过滤了某一单元格,那么该单元格所在行都被过滤掉。(被包装的过滤器必须实现filterKeyValue()方法,因为SkipFilter要调用该方法做判断) - 全匹配过滤器(WhileMatchFilter)
也是用于包装其他过滤器,用于当一条数据被过滤时就停止扫描
FilterList
用于同时使用多个过滤器来共同过滤。FilterList(Operator operator,List<Filter> filters )
添加好list后,scan.setFilter(filterList)就可以了
自定义过滤器
过滤器总结

计数器
可以用来做实时统计,从而放弃延时较高的批处理
协处理器
可以把一部分计算移动到数据存放端
HTablePool
HTable的连接池,类似于数据库连接池
- Observer
这一类协处理器与触发器类似:回调函数在一些特定时间发生时被执行
提供如下接口:RegionObserver,MasterObserver,WALObserver - Endpoint
这类协处理器类似于存储过程,通过添加一些远程过程调用来动态扩展RPC协议
这些接口都是基于Coprocessor框架的接口,所有协处理器的类都必须实现这个接口
![]()
可用客户端
除了HBase提供的默认RPC客户端外,HBase还提供了REST、Thrift、Avro的客户端,需要先在服务端开启,然后再使用对应的客户端
交互客户端
- 原生Java
- REST
- Thrift
- Avro
- 其他客户端
批处理客户端
- MapReduce
- Hive
- Pig
- 其他



浙公网安备 33010602011771号