HBase应用(2.2)数据操作

HBase数据操作命令有5个:Get(读)、Put(写)、Delete(删)、Scan(扫描)、Increment(递增)

  1. 写操作

    向2.1中创建的users表中插入数据{“name”: “jack”, “age”: 18, “gender”: “male”}

    • HBase Shell

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      hbase(main):005:0> put 'users', 'jack', 'info:age', 18
      0 row(s) in 0.1540 seconds

      hbase(main):006:0> put 'users', 'jack', 'info:gender', 'male'
      0 row(s) in 0.0100 seconds

      hbase(main):007:0> scan 'users'
      ROW COLUMN+CELL
      jack column=info:age, timestamp=1523850571869, value=18
      jack column=info:gender, timestamp=1523850589519, value=male
      1 row(s) in 0.0210 seconds
    • Java Client

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      package info.aviraer.demo.bigdata.core.hbase.chapter2;

      import java.io.IOException;

      import org.apache.hadoop.conf.Configuration;
      import org.apache.hadoop.hbase.HBaseConfiguration;
      import org.apache.hadoop.hbase.TableName;
      import org.apache.hadoop.hbase.client.Connection;
      import org.apache.hadoop.hbase.client.ConnectionFactory;
      import org.apache.hadoop.hbase.client.Put;
      import org.apache.hadoop.hbase.client.Table;
      import org.apache.hadoop.hbase.util.Bytes;

      public class SaveData {

      public static void main(String[] args) throws IOException {
      Configuration config = HBaseConfiguration.create();
      //配置zookeeper集群
      config.set("hbase.zookeeper.quorum", "centos0,centos1,centos2");
      Connection connection = ConnectionFactory.createConnection(config);

      Table table = connection.getTable(TableName.valueOf("users"));

      Put put = new Put("jack".getBytes()); //使用name作为行键,必须唯一
      put.addColumn("info".getBytes(), "age".getBytes(), Bytes.toBytes(18)); //添加年龄
      put.addColumn("info".getBytes(), "gender".getBytes(), Bytes.toBytes("male")); //添加性别
      table.put(put);

      table.close();
      connection.close();
      }

      }

    将用户jack的年龄更新为19

    HBase更新数据和新增数据的操作是一样的,只需要将值进行修改,再操作一次。

  2. 写操作流程

    HBase新增和更新操作的内部流程是一致的。写操作的流程如下图,RegionServer写入数据时先要将操作写入HLog(故障恢复),然后将数据写入MemStore中(MemStore位于内存中,当被填满后才将数据刷写到硬盘),两次写入都成功后写操作才算成功。

    HBase写操作流程

    图片源自:http://www.larsgeorge.com/2010/01/hbase-architecture-101-write-ahead-log.html

    集群中一个节点中的HLog 、MemStore、HFile、WAL关系如下:

    节点_WAL_MS_HFile

  3. 读操作

    读取users表中行键为jack的用户性别

    • HBase Shell

      1
      2
      3
      4
      hbase(main):014:0> get 'users', 'jack', 'info:gender'
      COLUMN CELL
      info:gender timestamp=1523850589519, value=male
      1 row(s) in 0.0070 seconds
    • Java Client

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      package info.aviraer.demo.bigdata.core.hbase.chapter2;

      import java.io.IOException;

      import org.apache.hadoop.conf.Configuration;
      import org.apache.hadoop.hbase.HBaseConfiguration;
      import org.apache.hadoop.hbase.TableName;
      import org.apache.hadoop.hbase.client.Connection;
      import org.apache.hadoop.hbase.client.ConnectionFactory;
      import org.apache.hadoop.hbase.client.Get;
      import org.apache.hadoop.hbase.client.Result;
      import org.apache.hadoop.hbase.client.Table;

      public class GetData {

      public static void main(String[] args) throws IOException {
      Configuration config = HBaseConfiguration.create();
      //配置zookeeper集群
      config.set("hbase.zookeeper.quorum", "centos0,centos1,centos2");
      Connection connection = ConnectionFactory.createConnection(config);

      Table table = connection.getTable(TableName.valueOf("users"));

      Get get = new Get("jack".getBytes());
      get.addColumn("info".getBytes(), "gender".getBytes());
      Result result = table.get(get);
      String gender = new String(result.getValue("info".getBytes(), "gender".getBytes()));
      System.out.println(gender);

      table.close();
      connection.close();
      }

      }

  4. 读操作流程

    为了优化数据访问速度,数据必须尽量有序且保存在内存中。HBase使用BlockCache作为缓存,BlockCache和MemStore在同一JVM堆中,使用LRU算法,将频繁读取的HFile数据读入内存中。

    节点_WAL_MS_HFile_BC

    BlockCache中的Block是从硬盘完成一次读取的最小单位,也是建立索引的最小单位,所以HFile会存储多个Block序列和这些Block的索引。每次读取Block时,先查找索引,然后从硬盘读出。Block的默认大小为64K,随机查询较多时Block可适当调小,此时索引数据变大,且顺序扫描的性能可能降低。

  5. 删除数据

    HBase的删除操作是针对要删除的记录新建立一条“墓碑”(tombstone)记录(类似于逻辑删除),该记录标记的内容不能在Get和Scan命令中返回。因此被删除的数据只有在后面提到的大合并中被删除。

    删除行键为jack的用户的性别信息

    • HBase Shell

      1
      2
      3
      4
      5
      6
      7
      hbase(main):015:0> delete 'users', 'jack', 'info:gender'
      0 row(s) in 0.0360 seconds

      hbase(main):016:0> get 'users', 'jack'
      COLUMN CELL
      info:age timestamp=1523850571869, value=18
      1 row(s) in 0.0090 seconds
    • Java Client

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      package info.aviraer.demo.bigdata.core.hbase.chapter2;

      import java.io.IOException;

      import org.apache.hadoop.conf.Configuration;
      import org.apache.hadoop.hbase.HBaseConfiguration;
      import org.apache.hadoop.hbase.TableName;
      import org.apache.hadoop.hbase.client.Connection;
      import org.apache.hadoop.hbase.client.ConnectionFactory;
      import org.apache.hadoop.hbase.client.Delete;
      import org.apache.hadoop.hbase.client.Table;

      public class DeleteData {

      public static void main(String[] args) throws IOException {
      Configuration config = HBaseConfiguration.create();
      //配置zookeeper集群
      config.set("hbase.zookeeper.quorum", "centos0,centos1,centos2");
      Connection connection = ConnectionFactory.createConnection(config);

      Table table = connection.getTable(TableName.valueOf("users"));

      Delete delete = new Delete("jack".getBytes());
      delete.addColumn("info".getBytes(), "gender".getBytes());
      table.delete(delete);

      table.close();
      connection.close();
      }

      }

  6. 数据合并

    HBase的数据合并有:小合并和大合并两种,主要是把多个HFile合并成一个大HFile来提升性能。

    • 小合并:读取多个小HFile内容,将内容写入一个新文件,然后将新文件激活并删除老文件。

      • 仅轻微影响性能
      • 无法清除被删除记录
    • 大合并:将一个列族的所有HFile合并成一个文件。

      • 性能影响大

      • 是清除被删除记录唯一机会

      • 只能手工触发

  7. 时间版本

    每次对单元进行增删改操作,HBase都自动存储一个新的时间版本(默认只保存一个版本),可以通过Get命令指定时间戳来访问特定时间版本的数据,若没有指定则按当前时间来访问。

    修改users表的info列族,使其默认保存3个版本,然后更新jack的age,查看多版本效果

    • HBase Shell

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      hbase(main):001:0> alter 'users', {NAME => 'info', VERSIONS => '3'}
      Updating all regions with the new schema...
      1/1 regions updated.
      Done.
      0 row(s) in 2.0810 seconds

      hbase(main):004:0> put 'users', 'jack', 'info:age', 22
      0 row(s) in 0.0640 seconds

      hbase(main):007:0> get 'users', 'jack', {COLUMN => 'info:age', VERSIONS => 3 }
      COLUMN CELL
      info:age timestamp=1523868650998, value=22
      info:age timestamp=1523850571869, value=18
      2 row(s) in 0.0080 seconds
    • Java Client

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      package info.aviraer.demo.bigdata.core.hbase.chapter2;

      import java.io.IOException;

      import org.apache.hadoop.conf.Configuration;
      import org.apache.hadoop.hbase.Cell;
      import org.apache.hadoop.hbase.CellUtil;
      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.Admin;
      import org.apache.hadoop.hbase.client.Connection;
      import org.apache.hadoop.hbase.client.ConnectionFactory;
      import org.apache.hadoop.hbase.client.Get;
      import org.apache.hadoop.hbase.client.Result;
      import org.apache.hadoop.hbase.client.Table;

      public class TimestampVersion {

      public static void main(String[] args) throws IOException {
      Configuration config = HBaseConfiguration.create();
      //配置zookeeper集群
      config.set("hbase.zookeeper.quorum", "centos0,centos1,centos2");
      Connection connection = ConnectionFactory.createConnection(config);

      Admin admin = connection.getAdmin();
      HColumnDescriptor hColumnDescriptor = new HColumnDescriptor("info".getBytes());
      hColumnDescriptor.setVersions(1, 3); //至少保存1个版本,至多3个版本
      HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("users"));
      hTableDescriptor.addFamily(hColumnDescriptor);

      admin.modifyTable(TableName.valueOf("users"), hTableDescriptor);
      admin.close();

      Table table = connection.getTable(TableName.valueOf("users"));
      Get get = new Get("jack".getBytes());
      get.addColumn("info".getBytes(), "age".getBytes());
      get.setMaxVersions(3);
      Result result = table.get(get);
      for (Cell cell : result.listCells()) {
      //打印最近3个版本的age值
      System.out.println(cell.getTimestamp() + ":" + new String(CellUtil.cloneValue(cell)));
      }
      table.close();
      connection.close();
      }

      }

    多版本的使用需要谨慎,否则会导致过多的冗余数据

  8. 数据模型

    对2.2中所有的模型做一个总结

名称 简介 数据类型 备注
表 (table) 组织数据 String(文件系统支持的字符)
行(row) 数据按行存储 byte[]
列族(column family) 行里数据按列族分组 String(文件系统支持的字符)
列限定符(column qualifier) 列族数据按列限定符定位 byte[]
单元(cell) 行键、列族、列限定符确定一个单元 byte[]
时间版本(version) cell的值有时间版本 long