HBase简介

HBase 是一个可以进行随机访问的存储和检索数据平台,它介于 NoSQL 和 RDBMS(关系型数据库)之间,主要用来存储非结构化和半结构化的松散数据

HBase 是一个开源非关系型分布式数据库(NoSQL),它参考了谷歌的 BigTable 建模,实现的编程语言为 Java

HBase (Hadoop Database),是一个高可靠性高性能面向列、可伸缩的分布式存储系统,利用 HBase 技术可在廉价 PC Server 上搭建起大规模结构化存储集群

# HBase 特性

  1. 容量巨大

    HBase 的单表可以有百亿行、百万列,可以在横向和纵向两个维度插入数据,具有很大的弹性。


  2. 列存储

    什么是列存储

    传统的数据库是关系型数据库,是按照行来存储的,效果如下:

    姓名小学名称初中名称高中名称大学名称
    张三XX 小学XX 初中XX 高中XX 大学
    李四YY 小学YY 初中YY 高中
    王五KK 小学KK 初中

    主键是姓名

    这表中只有第一行的数据是填满了,李四和王五的信息并没有填满

    因为这个表的结构是固定的,即使没有填上信息,也会空在那里(是占用内存,NULL),并不能什么都没有


    而列式存储:

    姓名学校类别学校名称
    张三小学名称XX 小学
    张三初中名称XX 初中
    张三高中名称XX 高中
    张三大学名称XX 大学
    李四小学名称YY 小学
    李四初中名称YY 初中
    李四高中名称YY 高中
    王五小学名称KK 小学
    王五初中名称KK 初中

    原来张三的一列数据(就是对应属性具体的值),变成了现在的一行数据,原来只有一个主键,现在变成了多个,为什么从关系型数据库中的小学名称、初中名称等变成下面的学校类别,那是因为这些名称都属于学校名称这个列族

    原来表中可能会有空余的部分,在列式存储上就不会出现,有需要添加的,就直接加多一行,不会造成空间浪费。

    # 行列对比

    1. 行式存储倾向于结构固定列式存储倾向于结构弱化。 (行式存储相当于套餐,即使一个人来了也给你上八菜一汤,造成浪费;列式存储相等于自助餐,按需自取,人少了也不浪费)

    2. 行式存储一行数据只需一份主键,列式存储一行数据需要多份主键

    3. 行式存储存的都是业务数据,列式存储除了业务数据外,还要存储列名

    4. 行式存储更像一个 Java Bean,所有字段都提前定义好,且不能改变;列式存储更像一个 Map,不提前定义随意往里添加 key:value。

    列式存储 (Columnar or column-based) 是相对于传统关系型数据库的行式存储 (Row-basedstorage) 来说的。简单来说两者的区别就是如何组织表

    Row-based storage stores atable in a sequence of rows.(基于行的存储存储在一系列行中可用。)

    Column-based storage storesa table in a sequence of columns.(基于列的存储将表存储在一系列列中。)

    行式存储列式存储
    优点数据被保存在一起 INSERT/UPDATE 容易查询时只有涉及到的列会被读取 投影 (projection) 很高效任何列都能作为索引
    缺点选择 (Selection) 时即使只涉及某几列,所有数据也都会被读取选择完成时,被选择的列要重新组装 INSERT/UPDATE 比较麻烦

  3. 稀疏性

    传统的关系型数据库通常会先将每一列的数据类型都事先定义好的,会占用固定的内存空间,属性值为的(NULL)的列也需要占用存储空间,而 HBase 中的数据都是以字符串的形式存储的,数据为空的列并不会占用存储空间。因此 HBase 的列式存储解决了数据稀疏性问题,很大程度节省了存储开销。

    稀疏数据

    在数据库中,稀疏数据是指在二维表中含有大量空值的数据;

    即稀疏数据是指,在数据集中绝大多数数值缺失或者为零的数据。稀疏数据绝对不是无用数据,只不过是信息不完全,通过适当的手段是可以挖掘出大量有用信息


  4. 扩展性强

    HBase 构建在 HDFS 上的,理所当然的支持分布式表,也继承了 HDFS 的可拓展性。

    HBase 的扩展是横向的

    横向扩展是指扩展时不需要提升服务器本身的性能,只需添加服务器节点到现有集群即可。


  5. 高可靠性

    HBase 运行在 HDFS 上,HDFS 的多副本存储可以让他在出现故障时自动恢复,同时 HBase 内部也提供预写日志(Write-Ahead-Log,WAL)和 Replication 机制

    WAL 是 HBase 服务器处理数据插入和删除的过程中用来记录操作内容的日志,保证了数据在写入时不会因为集群异常而导致写入数据丢失

    Replication 机制是基于日志操作来同步数据。


# 组件和功能

# 客户端

客户端包含访问 HBase 的接口,是整个 HBase 系统的入口,使用者直接通过客户端操作 HBase。客户端使用 HBase 的 RPC 机制与 HMaster 和 RegionServer 进行通信。

在一般情况下,客户端与 HMaster 进行管理类操作的通信,在获取 RegionServer 的信息后,直接与 RegionServer 进行数据读写类操作。而且客户端获取 Region 的位置信息后会缓存下来,用来加速后续数据的访问过程。

客户端可以用 Java 语言来实现,也可以使用 Thtift、Rest 等客户端模式,甚至 MapReduce 也可以算作一种客户端。

# ZooKeeper

什么是ZooKeeper

ZooKeeper 是一个高性能、集中化、分布式应用程序协调服务,主要是用来解决分布式应用中用户经常遇到的一些数据管理问题。

  1. Master 选举(保证任何时候,集群中只有一个 master,确保集群的高可靠性)

  2. 系统容错

    说明

    在 HBase 启动时,每个 RegionServer 在加入集群时都需要到 ZooKeeper 中进行注册,创建一个状态节点,然后 ZooKeeper 实时监控 RegionServer 的状态,将 Regionserver 的上线和下线信息实时通知给 Master,当某个 RegionServer 挂断的时候,ZooKeeper 会因为一段时间内接收不到它的心跳信息而删除该 RegionServer 对应的状态节点,并且给 HMaster 发送节点删除的通知。这时,HMaster 获知集群中某节点断开,会立即调度其他节点开启容错机制。

  3. Region 元数据管理

    在 HBase 集群中,Region 元数据被存储在 Meta 表中。每次客户端发起新的请求时,需要查询 Meta 来获取 Region 的位置,而 Meta 表是存在 ZooKeeper 中的。

  4. Region 状态管理(存贮所有 Region 的寻址入口)

    当 Region 发生改变时,需要集群的所有节点知晓,由于 Region 的数量非常庞大,所以需要 ZooKeeper 来管理。

  5. 提供 Meta 表存储位置

    在 HBase 集群中,数据库表信息、列族信息及列族存储位置信息都属于元数据。而元数据存贮在 Meta 表中,Mate 表的入口由 ZooKeeper 提供。


# HMaster 的作用

说明

HMaster 是 HBase 集群中的主服务器,负责监控集群中的所有 RegionServer,并且是所有元数据更改的接口。

HMaster 主要负责表和 Region 的管理工作

1、管理用户对表的增、删、改、查操作

2、管理 RegionServer 的负载均衡、调整 Region 的分布

3、在 Region Split 后,将新 Region 分布到不同的 RegionServer。
4、在 RegionServer 故障后,那该 RegionServer 上所管理的 Region 由 HMaster 进行重新分配。

总结 HMaster 的主要任务:

1、HTable DDL 操作

2、Region 分配工作。

其余的基本上都是 client 和 RegionServer 打交道来完成的


# RegionServer

说明

RegionServer 主要负责响应用户的请求,向 HDFS 读写数据。一般在分布式集群中,RegionServer 运行在 DataNode 服务器上,实现数据的本地性。

每个 RegionServer 包含多个 Region,它负责的功能如下:

  • 处理分批给它的 Region。
  • 处理客户端读写请求。
  • 刷新缓存到 HDFS 中。
  • 处理 Region 分片。
  • 执行压缩。

RegionServer 是 HBase 中最核心的模块,其内部管理了一系列 Region 对象,每个 Region 由多个 HStore 组成,每个 HStore 对应表中一个列族的存储。


每个 RegionServer 启动的时候,都会分配一个 startcode、host、port 和 startcode 统一构成一个 RegionServer 的唯一标志,所以在一台机器重启后和以前其实是两个不同 rs,这样可以区分 rs。


# HBase 数据模型

HBase 数据存储结构中主要包括:表、行、列族、列限定符、单元格和时间戳

  • 表 (Table):数据以表的形式存储起来(需要注意的是表名必须是能用在文件路径里的合法名字,因为 HBase 的表是映射成 HDFS 上面的文件)
  • 行 (Row):在表里面,每一行代表着一个数据对象,每一行都是以一个行键(Row Key)来进行唯一标识的行键可以是任意的数据类型,并且行键是按照字典顺序由低到高存储在表中的。
  • 列族 (Column Family)列族是一些列的集合,列族中所有列成员有着相同的前缀,列族的名字必须是可显示的字符串。列族支持动态扩展,用户可以很轻松地添加一个列族或列,无须预定义列的数量以及类型所有列均以字符串形式存储,用户在使用时需要自行进行数据类型转换。
  • 列标识 (Column Qualifier):存储在在列族中的数据通过列标识或列来寻址的列不需要提前定义(不需要在定义表和列族的时候就定义列),列与列之间也不需要保持一致。列和行键一样没有数据类型,并且在 HBase 存储系统中列也总是被看作一个 byte 数组。
  • 单元格 (Cell)每一个行键、列族、列标识共同确定一个单元格,单元格的内容没有特定的数据类型,以二进制字节来存储。每个单元格保存着同一份数据的多个版本,不同时间版本的数据按照时间先后顺序排序,最新的数据排在最前面。
  • 时间戳 (Timestamp)时间戳是给定值的一个版本号标识,每一个值都会对应一个时间戳,时间戳是和每一个值同时写入 HBase 存储系统中的。在默认情况下,时间戳表示数据服务在写入数据时的时间,但可以在将数据放入单元格时指定不同的时间戳值。


上图中是一个 HBase 表,由两个列族(Personal 和 Office)构成,每个列族都有两列,把它转换成表中存储,它长这样:


使用键值对来说:


# HBase 基本操作

DDL|DML

数据定义语言(Data Defination Language,DDL)主要针对表为对象进行操作,如创建表、修改表、删除表等

  • create(创建)
  • list(查看所有表)
  • describe ' 表名 ' ➡️查看表结构
  • 删除的方法(alter;先 disable 然后 drop...)
  • exists ' 表名'➡️查看表是否存在
  • is_disable ' 表名 ' ➡️查看表的状态

数据操作语言(Data Manipulation Language,DML)主要针对表的记录的操作,如插入记录、查询记录等

首先获得” 引用 “ stu=get_table'students'

  • stu.put '2018111701', 'basic_info:name','gyt'
    stu.put '2018111701', 'basic_info:gender', 'male'
    stu.put '2018111701', 'basic_info:birthday','2018-10-01'

    这些都是插入操作

  • get ' 表名 ' ' 行键 '[, ' 列族 [: 列]'] ➡️查询记录

  • truncate 'students' ➡️清空数据(伪,实际上它是先禁用然后删除,在重建一个表结构和之前表一样的

    • ** truncate_preserve ** 只清除数据
    • ** truncate ** 会把表分区也清除掉

# Shell 命令

常用命令

命令描述
create创建指定模式新表
alter修改表结构
describe展示表结构信息,包括列族的数量和属性
list展示 HBase 中已有的表
disable/enable删除 / 更改表的前驱动作,禁用表,删除 / 更改完需要解禁表
disable_all禁用所有的表,可以使用正则表达式
is_disabled判断表是否被禁用
drop删除表
truncate只想删除表中数据,而不想删除表结构则可以使用此命令来禁用表、删除表并自动重建表结构
使用方法
  • 创建表 :create ‘表名’,‘列族名’

    hbase(main):002:0> create 'student','info'
  • 添加数据 :put ‘表名’,‘Row_key’,‘列族名:列名’,‘数据’

hbase(main):003:0> put 'student','1001','info:sex','male'
hbase(main):004:0> put 'student','1001','info:age','18'
hbase(main):005:0> put 'student','1002','info:name','Janna'
hbase(main):006:0> put 'student','1002','info:sex','female'
hbase(main):007:0> put 'student','1002','info:age','20'
  • 指定字段的更新操作

    hbase(main):008:0> put 'student','1001','info:name','Nick'
    hbase(main):009:0> put 'student','1001','info:age','100'
  • 扫描查看表数据 : scan

    //扫描查看整张表
    hbase(main):010:0> scan 'student' 
    //从row_key 1001开始扫描查看 到row_key 1001结束查看,也就是只查看row_key 1001的数据
    hbase(main):011:0> scan 'student',{STARTROW => '1001', STOPROW => '1001'} 
    //从row_key 1001开始扫描查看直到表的末尾
    hbase(main):012:0> scan 'student',{STARTROW => '1001'}
  • 查看表结构 :describe

    hbase(main):013:0> describe ‘student’
  • 查看 “指定行” 或 “指定列族:列” 的数据 : get

    hbase(main):014:0> get 'student','1001'
    hbase(main):015:0> get 'student','1001','info:name'
  • 统计数据行数 : count

    hbase(main):021:0> count 'student'
  • 删除数据

    • 删除某 rowkey 的全部数据

      hbase(main):016:0> deleteall 'student','1001'
    • 删除某 rowkey 的某一列数据

      hbase(main):017:0> delete 'student','1002','info:sex'
  • 清空表数据 : truncate

    hbase(main):018:0> truncate 'student'
  • 删除表 : disable

    想要删除某张表,就要先禁用它

    hbase(main):019:0> disable 'student'
    hbase(main):020:0> drop 'student'

    如果直接 drop 表,会报错:ERROR: Table student is enabled. Disable it first.

  • 变更表信息 : alter

    将 info 列族中的数据存放 3 个版本

    hbase(main):022:0> alter 'student',{NAME=>'info',VERSIONS=>3} 
    hbase(main):022:0> get 'student','1001',{COLUMN=>'info:name',VERSIONS=>3}
  • 查看命名空间

    hbase(main):023:0> list_namespace
    NAMESPANCE
    name1
    name2
    2 row(s) in 0.0101 seconds
  • 创建命名空间

    hbase(main):024:0> create_namespance 'bigdata'
  • 在命名空间下建表

    hbase(main):025:0> create 'bigdata:stu','info'

    在不同的命名空间下可以建同名表

    hbase(main):026:0> list
    TABLE
    bigdata:stu
    stu
    2 row(s) in 0.0070 seconds

    第二个 stu 是在 default 下的

  • 删除命名空间

    hbase(main):027:0> disable 'bigdata:stu'
    hbase(main):028:0>drop 'bigdata:stu'
    hbase(main):029:0>drop_namespance 'bigdata'

    需要先让命名空间为空,也就是命名空间下的表先下线删除,然后才能删除命名空间


表设计要点
  1. 设计行键是 HBase 表结构设计中最重要的一件事情,行键决定了应用程序如何与 HBase 表进行交互。如果没有设计好行键还会影响从 HBase 中读出数据的性能。
  2. HBase 的表结构很灵活,而且不关心数据类型,可以以 byte 数组的形式存储任何数据。
  3. 存储在相同的列族中的数据具有相同的特性(易于理解)。
  4. HBase 主要是通过行键来建立索引
  5. HBase 不支持多行事务,所有尽量在一次 API 请求操作中获取到结果。
  6. HBase 中的键可以通过提取其 hash 值来保证键长度是固定的和均匀分布,但是这样做会牺牲键的数据排序和可读性。
  7. 列限定符和列族名字的长度都会影响 I/O 的读写性能和发送给客户端的数据量,所以给它们命名的时候应该尽量简短
JavaAPI调用(未全,待添加)

HBase 主要包括 5 大类操作:HBase 的配置、HBase 表的管理、列族的管理、列的管理、数据操作。

创建表:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.HBaseAdmin;
public class HBaseDemo {
    public static void main(String[] args) throws Exception {
        // TODO 自动生成的方法存根
        Configuration conf=HBaseConfiguration.create();
        // 连接 zookeeper
        conf.set("hbase.zookeeper.quorum", "itcast04:2181,itcast05:2181,itcast06:2181"); 
        // 获取表的对象 hbaseasmin
        HBaseAdmin admin = new HBaseAdmin(conf);
        
        // 定义表名
        HTableDescriptor htd=new HTableDescriptor(TableName.valueOf("peoples"));
        // 定义表中列族 1
        HColumnDescriptor hcd_info=new HColumnDescriptor("info");
        hcd_info.setMaxVersions(3);
    
        // 第一列族 2
        HColumnDescriptor hcd_data=new HColumnDescriptor("data");
        
        htd.addFamily(hcd_info);
        htd.addFamily(hcd_data);
        
        admin.createTable(htd);
        admin.close();
    }
}

# Shell 操作

# 进入 HBase

[hadoop@w ~]$  hbase shell
HBase Shell; enter 'help<RETURN>' for list of supported commands.
Type "exit<RETURN>" to leave the HBase Shell
Version 0.94.16, r1557241, Fri Jan 10 20:10:24 UTC 2014

# 查看当前版本

hbase(main):002:0>  version
0.94.16, r1557241, Fri Jan 10 20:10:24 UTC 2014

# 查看当前 hbase 的状态

hbase(main):003:0>  status
2 servers, 0 dead, 1.0000 average load

# 创建表

例子以实现一个学生成绩表

  1. 建立一个表 scores,包含两个列族:grade 和 course

    hbase(main):001:0> create 'scores','grade','course'
  2. 按设计的表结构添加值

    put'scores','Jim1','grade:'1'
    put'scores','Jim','course:Chinese','90'
    put'scores','Jim','course:math','85'
    put'scores','Jim','course:english','85'
    put'scores','Tom','grade:','2'
    put'scores','Tom','course:Chinese','97'
    put'scores','Tom','course:math','100'
    put'scores','Tom','course:english','92'

    列族里边可以自由添加子列。如果列族下没有子列,则只加 “:” 即可

  3. 根据键值查询数据

    查看 scores 表中的 Jim 行的相关数据

    hbase(main):012:0>get 'scores','Jim'

    查看 scores 表中 Jim 行、course 列族中 math 列的值。

    hbase(main):012:0>get 'scores','Jim','course:math'

    或者用以下代码。

    hbase(main):013:0>get 'scores','Jim',{COLUMN=>'course:math'}
  4. 扫描所有数据

    hbase(main):012:0>scan 'scores'

    为了限制返回的结果,可以指定一些修饰词,如 TIMERANGE (限定范围时间戳)、FILTER (过滤器)、LIMIT(限制查询结果行数)、STARTROW (开始行)、STOPROW (结束行)、TIMESTAMP (时间戳)、MAXLENGTH (最长长度) 或 COLUMN (列名)。
    获取 Jim 和 Tom 的 english 的成绩。

    scan'scores',{COLUMN=>'course:english',STARTROW=>Jim,ENDROW=Tom}
  5. 添加新的列

    为 Tom 添加 computing 课程的成绩。

    hbase(main):012:0>put'scores','Tom','course:computing','90'

# 过滤器(Filter)

说明

用户可以通过内置或自定义的过滤器来对数据进行过滤,所有的过滤器都在服务端生效

用法:

scan '表名' ,{Filter => "过滤器(比较运算符,'比较器')"}

整体可以使用大括号引用,也可以不用

可选👇

过滤器基本上有以下 5 种:

  • RowFilter 基于 RowKey 的过滤
  • FamilyFilte 基于列簇的过滤
  • QualifierFilter 基于字段的过滤
  • ValueFilter 基于值的过滤
  • DependentColumnFilter 参考值过滤
比较器描述
BinaryComparator匹配完整字节数组
BinaryPrefixComparator匹配字节数组前缀
BitComparator匹配比特位
NullComparator匹配空值
RegexStringComparator匹配正则表达式
SubstringComparator匹配子字符串

# 行键过滤器

RowFIlter 可以配合比较器和比较运算符,实现行键字符串的比较和过滤,匹配行键中大于 001 的数据可以使用 binary 比较器;匹配 001 开头的行键,可以使用 substring 比较器 (substring 不支持大于小于运算符)

scan 'Student' ,{Filter => "RowDilter(=,'substring:001')"}
scan 'Student' ,{Filter => "RowDilter(>,'binary:001')"}

针对行键进行匹配的过滤器还有:PrefixFilter、KeyOnlyFilter、FristKeyOnlyFilter 和 InclusiveStopFilter

详情
行键过滤器描述示例
PrefixFilter行键前缀比较器scan 'Student' ,{Filter => "PrefixFilter ('001)'‘} 或者 scan 'Student' ,{Filter =>"PrefixFilter (>='substring:001')"}
KeyOnlyFilter只对单元格的键进行过滤和显示,不显示值scan 'Student' ,Filter => "KeyOnlyFilter()"
FristKeyOnlyFilter只扫描显示相同的第一个单元格,其键值对会显示出来scan 'Student' ,Filter => "FristKeyOnlyFilter()"
InclusiveStopFilter代替 endrow 返回终止条件scan 'Student' ,{STOPROW => '001' ,Filter => "InclusiveStopFilter (binary:003)"} 或者 scan 'Student',{STOPROW => '001' ,ENDROW=>'003'}

# 列族和列过滤器

针对列族进行过滤的过滤器为 FamilyFilter,其语法结构与 RowFilter 类似,不同之处在于 FamilyFilter 是对列族名称进行过滤的。

scan 'Student', FILTER=>" FamilyFilter(= , 'substring:Grades')"

列的过滤如下:

列过滤器描述示例
QualifierFilter列标识过滤器,只显示对应列名的数据scan 'Student', FILTER => "QualifierFilter(=,'substring:Math')"
ColumnPrefixFilter对列名称的前缀进行过滤scan 'Student', FILTER => "ColumnPrefixFilter('Ma')"
MultipleColumnPrefixFilter可以指定多个前缀对列名称过滤scan 'Student', FILTER => "MultipleColumnPrefixFilter('Ma','Ag')"
ColumnRangeFilter过滤列名称的范围scan 'Student', FILTER=>"ColumnRangeFilter('Big',true,'Math',false')"

上表中 QualifierFilterColumnPrefixFilter 过滤效果类似,只是 ColumnPrefixFilter 无须结合运算符和比较器即可完成字符串前缀的过滤

# 值过滤器

过滤器中也有针对单元格进行扫描的过滤器,即值过滤器

值过滤器描述示例
ValueFilter值过滤器,找到符合值条件的键值对scan 'Student', FILTER => "ValueFilter (=,'substring:curry')" 同 get 'Student', '0001', FILTER => "ValueFilter (=,'substring:curry')"
SingleColumnValueFilter在指定的列族和列中进行比较的值过滤器scan 'Student', Filter => "SingleColumnValueFilter('StuInfo', 'Name', =, 'binary:curry')"
SingleColumnValueExcludeFilter排除匹配成功的值scan 'Student', Filter => "SingleColumnValueExcludeFilter('StuInfo', 'Name', =, 'binary:curry')"

ValueFilter 过滤器可以利用 get 和 scan 方法对单元格进行过滤,但是使用 get 方法时,需要指定行键。

SingleColumnValueFilterSingleColumnValueExcludeFilter 过滤器扫描的结果是相反的都需要在过滤条件中指定列族和列的名称

# 其他过滤器

还有一些其他的过滤器

值过滤器描述示例
ColumnCountGetFilter限制每个逻辑行返回键值对的个数,在 get 方法中使用get 'Student', '0001', FILTER => "ColumnCountGetFilter(3)"
TimestampsFilter时间戳过滤,支持等值,可以设置多个时间戳scan 'Student', Filter => "TimestampsFilter(1,4)"
InclusiveStopFilter设置停止行scan 'Student',
PageFilter对显示结果按行进行分页显示scan 'Student',
ColumnPaginationFilter对一行的所有列分页,只返回 [offset,offset+limit] 范围内的列scan 'Student',
说明
  • ColumnCountGetFilter 过滤器限制每个逻辑行返回多少列,一般不用在 scan 方法中
  • Timestamps Filter 匹配相同时间戳的数据。
  • InclusiveStopFilter 过滤器设置停止行,且包含停止的行,上表中示例最终展示数据为行键 0001〜0003 范围内的数据。
  • PageFilter 设置每页最多显示多少逻辑行,示例中显示三个逻辑行。
  • ColumnPaginationFilter 过滤器对一个逻辑行的所有列进行分页显示。