1.HBase基础

什么是HBase数据库:

  • Hadoop数据库,NoSQL数据库
  • 稀疏的、分布式的、持久化的、多维有序映射的
  • 基于行键(rowkey),列键(column key)、时间戳(timestamp)
  • 键值(key value)存储、面向列族的数据库
  • 可以存储结构化和非结构化数据
  • 不允许跨行事务

HBase主要使用场景

  1. 抓取增量数据
  • 抓取监控指标:OpenTSDB
  • 抓取用户交互数据
  • 遥测技术:捕获和存储用户计算机上生成的软件崩溃报告
  • 广告效果和点击流
  1. 内容服务
  • 用户生成内容
  • 内容管理系统(Content Management System,CMS)
  • URL短连接
  • 用户模型服务
  1. 信息交换

基本操作

1.建表

HBase的表按照行(row)和列(column)来组织。列组成列族(column family),HBase表至少有一个列族。

create 'users', 'info'

2.插入/查询数据

put 'users', 'row1', 'info:a', 'true'
get 'users', 'row1'

JAVA API

1.建立连接

//HBase客户端配置信息
Configuration con = HBaseConfiguration.create();
//配置访问HBase地址和端口
con.set("hbase.zookeeper.quorum","serverip");
con.set("hbase.zookeeper.property.clientPort","2222");

//建立连接
tyr(Connection conn = ConnectionFactory.createConnection(con)){
    //获取HBase表
    try(Table table = conn.getTable(TableName.valueOf("users"))){
        //扫描
        Scan scan = new Scan();
        scan.addFamily("cf".getBytes());
        
        ResultScanner scanner = table.getScanner(scan);
        for(Result result : scanner ){
            //输出行键
            System.out.println(new String(result.getRow()));
        }
    }
} catch (IOException e){
    e.printStackTrace();
}

2.数据操作

HBase表的行有唯一标识符,叫做行键(rowkey)。其他部分用来存储HBase表里的数据。

HBase中所有数据都是作为原始数据(raw data)使用字节数组的形式存储的。

HBase用坐标来定位表数据,第一个坐标是行键,下一个是列族,再下一个是列限定符(column qualifier),简称为列(column),最后是时间版本。

【rowkey,column family,column qualifier,version】4个坐标确定了单元(cell)的位置。HBase中数据作为值存储在单元里。

  • Put:增加行/修改行
Put put = new Put(Bytes,toBytes(rowKey));
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("message"),Bytes.toBytes("true"));
table.put(put);
  • Get
Get get = new Get(Bytes.toBytes("tablename"));
Result result = table.get(get);
//指定列
get.addColumn(Bytes.toBytes("info"),Bytes.toBytes("message"));
//指定列族
get.addFamily(Bytes.toBytes("info"));
  • Delete
Delete d new Delete(Bytes.toBytes("tableName"));
d.addColumn(Bytes.toBytes("info"),Bytes.toBytes("message"));
table.delete(d);

2.工作机制

1.HBase写路径

在HBase中无论是增加新行还是修改已有的行,其内部流程是相同的。HBase接到命令后存下变化信息,或者写入失败抛出异常。

默认情况下,执行写入时会写入到两个地方:**预写式日志(write-ahead log,WAL,也称HLog)**和 MemStore

只有当这两个地方的变化信息都写入并确认后,才认为写动作完成。

MemStore是内存里的写入缓冲区,HBase中数据在永久写入硬盘之前在这里积累。当MemStore填满后,其中数据会写到硬盘,生成一个HFile(HBase底层存储格式)。

HFile对应于列族,一个列族可以有多个HFile。但一个HFile不能存储多个列族的数据。在集群的每个节点上,每个列族有一个MemStore

问题/故障:MemStore还没有刷写,服务器就崩溃了,内存中没有写入硬盘的数据就会丢失

HBase处理办法:

  • 写入动作之前先写入WAL。
  • 直到WAL新记录成功写入后,写入动作才被认为成功完成。
  • 没有从MemStore里刷写到HFile的数据将可以通过回放WAL来恢复
  • HBase集群中每台服务器维护一个WAL来记录发生的变化。这台服务器上的所有表共享这个WAL。
  • WAL是底层文件系统上的一个文件。

路径:客户端–>WAL–>MemStore–>HFile

2.HBase读路径

HBase读动作必须重新衔接持久化到硬盘上HFile和内存中MemStore里的数据。在读操作上使用LRU(最近最少使用算法)缓存技术。这种缓存也叫做BlockCache,和MemStore在一个JVM堆里。BlockCache设计用来保存从HFile里读入内存的频繁访问数据,避免硬盘读。每个列族都有自己的BlockCache。

BlockCache中的Block是HBase从硬盘完成一次读取的数据单位。HFile物理存放形式是一个Block序列外加这些Block索引。从HBase里读取一个Block需要先在索引上查找一次该Block,然后从硬盘读出。

Block是建立索引的最小数据单位,也是从硬盘读取的最小数据单位。

Block大小安装列族设定,默认值是64KB。

如果主要用于随机查询,需要细粒度的Block索引,小一点的Block更好。Block变小会导致索引变大,进而消耗更多内存。

如果经常执行顺序扫描,一次读取多个Block,大点的Block更好。Block变大意味着索引项变少,索引变小,因此节省内存。

路径:首先会检查MemStore等待修改的队列,然后检查BlockCache看包含改行的Block是否最近被访问过,最后访问硬盘上对应的HFile。

注意:HFile存在某个时刻MemStore刷写的快照,一个完整行的数据可能存放在多个HFile里。为了读出完整行,HBase可能需要读取包含该行信息的所有HFile。

3.HBase删除

**Delete命令并不会立即删除内容。实际上,它只是给记录打上删除的标记(“墓碑”记录)。**墓碑记录用来标记删除的内容不能在GET和SCAN命令中返回。因为HFile文件是不能改变的,直到执行一次大合并,这些墓碑记录才会被处理,被删除记录占用的空间才被释放。

合并分为两种:

  • 大合并(major compaction)
    同时处理一个列族的全部HFile,**大合并是HBase清理被删除记录的唯一机会。**因为小合并不能保证被删除的记录和墓碑标记记录在一个HFile里面。而大合并可以确保同时访问到两种记录。
  • 小合并(minorco compaction)
    从已有的Hfile里读出记录,合并到一个HFile。然后新HFile标记为权威数据,删除老HFile

拓展

ACID

  • Atomicity(原子性):原子性是指原子不可分的操作属性,换句话说,要么全部完成要么全部不完成。如果操作成功,整个操作成功。如果操作失败,整个操作失败。
  • Consistency(一致性):一致性是指把系统从一个有效状态带入另一个有状态的操作属性。如果操作使系统出现不一致,操作不会被执行或者回退。
  • Isolation(隔离性):隔离性意味着两个操作的执行是互不干扰的。
  • Durability(持久性):意味着数据一旦写入,确保可以读回并且不会在系统正常操作一段时间后丢失。

在线系统和离线系统

在线系统:需要低延迟。某些情况下,系统哪怕给出没有答案的响应,都比花很长时间给出正确答案的响应好。

离线系统:不需要低延迟。用户可以等待答案,不期待马上给出响应。

JRuby:是在java运行时上面的Ruby编程语言的实现。除了正常Rudy语法,JRuby支持访问Java对象和函数库。

MemStore的大小由hbase-site.xml文件里的系统级属性hbase.hregion.memstore.flush.size来定义。