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(List times)
  • 列计数过滤器
    可以使用这个过滤器来限制每行最多可以取回多少列
    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
  • 其他
posted @ 2019-11-28 21:41  hacker飞猪  阅读(174)  评论(0)    收藏  举报