Hbase

1. 数据存储

1.1 RDBMS:

  1. Data is typed structured before stored

  2. 传统SQL

    data location
    entity table
    record row
    query group by 、join
  3. 大数据时代,如何做实时查询?

  4. hadoop/HDFS:能存 没法进行实时查询,随机读写

  5. NoSQL(NOT only SQL):HBase、Redis

1.2 Hbase在Hadoop生态圈中的位置

  1. HBase是Hadoop生态圈中的一个重要组成部分
  2. HBase是构建在HDFS纸上,也就是说HBase的数据可以存储在HDFS上面
  3. 可以通过MapReduce/Spark来处理Hbase中的数据
  4. HBase也提供了shell、API的方式进行数据的访问

1.3 行式VS列式

  1. 行式

    • 按行存储

    • 没有索引查询的时候需要耗费大量的IO

    • 可以通过建立索引或者视图来提速

    • 1,3zz,23

  2. 列式

    • 压缩、并行处理
    • 数据就是索引,大大降低IO

1.4 HBase特点

  1. 大:数据量大

  2. 面向列:列族(可以存放很多列),列族/列独立索引

  3. 稀疏:

    id name age ...
    1 3zz 0
    2 4xx 0
    3 5yy 0
  4. 数据类型单一:byte/string

  5. 无模式:每一行的数据所对应的列不一定相同,每行的列是可以动态添加的

    3zz age/birthday/company
    4xx company/province/city
    
  6. 数据多版本:比如company可以存放不同的版本的值

    默认情况下版本号是自动分配的,是列的值插入时的时间戳

1.5 HBase VS MySQL

  1. 数据类型不同

  2. 数据操作:

    1. 关联查询:MapReduce/Spark/Phoenix
    2. get/put/scan...
  3. 存储模式

    1. MySQL

      id name age tel Address
      1 a 11 123 ...
      2 b 22 456 ...
      3 c 33 789 ...
    2. 在HBase中

      basic_info: id	name 
      private_info: age	tel		address
      
  4. transaction事务性:单行

  5. 数据量:HBase大

  6. 吞吐量:Million级别

HBase vs HDFS

  1. write pattern(写模式)
  2. read pattern(读模式)
  3. SQL
  4. data size(数据量)

HBase优势

  1. 成熟
  2. 高效
  3. 分布式

数据模型

  1. rowkey

    • 主键

    • 字符串,按字典顺序存储,在HBase内部保存的是字节数组

  2. 列族:Column Family (CF)

    • 是在创建表的时候就要指定的

    • 列族是一系列列的集合

    • 一个列族所有列有着相同的前缀

      basic_info: id
      basic_info: name
      private_info: age
      private_info: tel
      private_info: address
      
  3. 列:Column / Qualifier

    • 属于某一个列族
  4. 每条记录被划分到若干个CF中,每条记录对应一个rowkey,每个CF由一个或者多个Column构成

  5. 存储单元:Cell

    • HBase中ROW和Column确定的一个存储单元
    • 每个Cell都保存这同一份数据的多个版本
    • 在写入数据时,时间戳可以由HBase自动赋值,也可以显示赋值
    • 每个Cell中,不同版本的数据按照时间戳的倒序排列
    {rowkey, column, version} ==> HBase 中的一个Cell
    

2. HBase安装

2.1 前置需要

  1. JDK(略过)
  2. ZooKeeper安装(brew)
  3. Hadoop安装(使用cdh版本,详细配置参照前面博客)

2.2HBase安装:

  1. 在.bash_profile中添加HBASE_HOME

  2. 在HBase的conf目录中修改两个文件

    1. hbase-env.sh文件中

      # 1.修改java的路径
      # The java implementation to use.  Java 1.7+ required.
      # export JAVA_HOME=/usr/java/jdk1.6.0/
      export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home
      # 注意 这里一定要是用1.8的版本! 上面使用java12是要出大问题的
      
      
      # 2.修改zookeeper配置
      # Tell HBase whether it should manage it's own instance of Zookeeper or not.
      export HBASE_MANAGES_ZK=false
      
    2. hbase-site.xml中配置

      <configuration>
      <!--hbase的数据存放地址(本机)-->
      <property>
        <name>hbase.rootdir</name>
        <value>hdfs://localhost:8020/hbase</value>
      </property>
      <!--分布式配置-->
      <property>
        <name>hbase.cluster.distributed</name>
        <value>true</value>
      </property>
      <!--本机zookeeper地址-->
      <property>
        <name>hbase.zookeeper.quorum</name>
        <value>localhost:2181</value>
      </property>
      </configuration>
      

2.3 启动HBase

  1. 先启动hadoop目录下sbin/start-dfs.sh
  2. 启动zookeeperzKserver start
  3. 启动HBase目录下的bin/start-hbase.sh
  4. 打开本机60010端口,有界面 则成功

2.4 HBase shell使用

  1. bin目录下输入hbase shell即可进入shell脚本
    1. 查看版本version
    2. 查看服务器的状态status

3. HBase相关操作

3.1 DDL操作

  • 创建、查询
# 创建
create 'member','member_id','address','info'
# 查询详细信息
desc 'member'
  • 返回信息如下:
{NAME => 'address', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCOD
ING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICA
TION_SCOPE => '0'}                                                                                                              
{NAME => 'info', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING
 => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATIO
N_SCOPE => '0'}                                                                                                                 
{NAME => 'member_id', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENC
ODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLI
CATION_SCOPE => '0'} 
  • 也可以在图形化界面,即60010端口上查看,点击最上面的Table Details
# 查看有哪些表
hbase(main):007:0> list
=> ["member"]
# 删除列族中的一列
hbase(main):008:0> alter 'member' ,'delete'=>'member_id'
1/1 regions updated.
Done.
  • 此时在查看member表的结构
hbase(main):009:0> desc 'member'

COLUMN FAMILIES DESCRIPTION                                                                                                     
{NAME => 'address', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCOD
ING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICA
TION_SCOPE => '0'}                                                                                                              
{NAME => 'info', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING
 => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATIO
N_SCOPE => '0'} 
  • 可以发现member_id已经被我们删掉了

  • 删除表

# 先disable
hbase(main):011:0> disable 'member'
# 再drop
hbase(main):012:0> drop 'member'
# 此时再查看所有表
hbase(main):013:0> list
=> []

3.2 DML操作

  1. 为了方便,重新创建一张表

    # 表名为member,列族为address、info
    create 'member','address','info'
    
  2. 创建/修改/删除表

    # rowkey为行名(表名) cf为列族 column为列族中的一列
    插入数据: put 表名,rowkey,cf:column,key
    
  3. 实际语句如下

    put 'member','3z','info:age','23'
    put 'member','3z','info:birthday','1996-07-31'
    
  4. 查看member中数据

    scan 'member'
    # 返回结果
    ROW					COLUMN+CELL
    3z					column=info:age, timestamp=12341241212, value=23
    3z					column=info:birthday, timestamp=12341241213, value=1996-07-31
    
  5. 获取某一行的数据

    # 获取所有3z的数据
    get 'member','3z'
    # 返回结果
    COLUMN                            CELL                                                                                          
     info:age                         timestamp=1577955149575, value=23
     info:birthday                    timestamp=1577955128970, value=1996-07-31
    
  6. 修改某一列

    put 'member','3z','info:age','18'
    # 获取当前年龄
    get 'member','3z','info:age'
    # 返回结果
    COLUMN                            CELL
     info:age                         timestamp=1577955321212, value=18
    # age已经被更新
    
  7. 删除某一列

    delete 'member','3z','info:birthday'
    # 再查看3z的信息
    get 'member','3z'
    # 返回结果
    COLUMN                            CELL
     info:age                         timestamp=1577955321212, value=18
    # 删除成功
    
  8. 统计一下几行

    count 'member'
    # 返回结果
    ==> 1
    
  9. 删除某个一整行

    deleteall 'member','3z'
    
  10. 清空一张表

    truncate 'member'
    # 会先自己disable 在truncate
    Truncating 'member' table (it may take a while):
     - Disabling table...
     - Truncating table...
    0 row(s) in 3.4130 seconds
    

4. HBase API开发:Java/Scala

  1. maven:pom.xml:良好的网络支持

    package com.zth.bigdata.hbase;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.hbase.*;
    import org.apache.hadoop.hbase.client.*;
    import org.apache.hadoop.hbase.util.Bytes;
    import org.junit.After;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    
    import java.io.IOException;
    
    /**
     * Author: 3zZ.
     * Date: 2020/1/11 6:14 下午
     */
    public class HbaseApp {
        Connection connection = null;
        Table table = null;
        Admin admin = null;
    
        String tableName = "3z_hbase_java_api";
    
        @Before
        public void setUp() {
            Configuration configuration = new Configuration();
            configuration.set("hbase.rootdir", "hdfs://localhost:8020/hbase");
            configuration.set("hbase.zookeeper.quorum", "localhost:2181");
            try {
                connection = ConnectionFactory.createConnection(configuration);
                admin = connection.getAdmin();
    
                Assert.assertNotNull(connection);
                Assert.assertNotNull(admin);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        @Test
        public void getConnection() {
        }
    
        @Test
        public void createTable() throws Exception {
            TableName table = TableName.valueOf(tableName);
            if (admin.tableExists(table)) {
                System.out.println(tableName + "已经存在");
            } else {
                HTableDescriptor descriptor = new HTableDescriptor(table);
                descriptor.addFamily(new HColumnDescriptor("info"));
                descriptor.addFamily(new HColumnDescriptor("address"));
                admin.createTable(descriptor);
                System.out.println(tableName + "创建成功");
            }
        }
    
        @Test
        public void queryTableInfos() throws Exception {
            HTableDescriptor[] tables = admin.listTables();
            if (tables.length > 0) {
                for (HTableDescriptor table : tables) {
                    System.out.println(table.getNameAsString());
                    HColumnDescriptor[] columnDescriptors = table.getColumnFamilies();
                    for (HColumnDescriptor hColumnDescriptor : columnDescriptors) {
                        System.out.println("\t" + hColumnDescriptor.getNameAsString());
                    }
                }
            }
        }
    
        @Test
        public void testPut() throws Exception {
            table = connection.getTable(TableName.valueOf(tableName));
            Put put = new Put(Bytes.toBytes("3z"));
            // 通过PUT设置要添加数据的CF、qualifier、value
            put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("24"));
            put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("birthday"), Bytes.toBytes("731"));
            put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("company"), Bytes.toBytes("HIT"));
    
            put.addColumn(Bytes.toBytes("address"), Bytes.toBytes("country"), Bytes.toBytes("CN"));
            put.addColumn(Bytes.toBytes("address"), Bytes.toBytes("province"), Bytes.toBytes("BJ"));
            put.addColumn(Bytes.toBytes("address"), Bytes.toBytes("city"), Bytes.toBytes("BJ"));
            //  将数据put到hbase中去
            table.put(put);
        }
    
        @Test
        public void testUpdate() throws Exception {
            table = connection.getTable(TableName.valueOf(tableName));
            Put put = new Put(Bytes.toBytes("2z"));
            put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("25"));
            table.put(put);
        }
    
        @Test
        public void testGet01() throws Exception {
            table = connection.getTable(TableName.valueOf(tableName));
            Get get = new Get("3z".getBytes());
            get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"));
            Result result = table.get(get);
            printResult(result);
        }
        @Test
        public void testScan01() throws Exception{
            table = connection.getTable(TableName.valueOf(tableName));
            Scan scan = new Scan();
            ResultScanner rs = table.getScanner(scan);
            for (Result result: rs){
                printResult(result);
            }
        }
    
        public void printResult(Result result) {
            for (Cell cell : result.rawCells()) {
                System.out.println(Bytes.toString(result.getRow()) + "\t" +
                        Bytes.toString(CellUtil.cloneFamily(cell)) + "\t" +
                        Bytes.toString(CellUtil.cloneQualifier(cell)) + "\t" +
                        Bytes.toString(CellUtil.cloneValue(cell)) + "\t" +
                        cell.getTimestamp()
                );
            }
        }
    
        @After
        public void tearDown() {
            try {
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
  2. 小结

    • Connection

    • Admin

    • HTableDescriptor

    • HcolumnDescriptor

    • 创建表

    • 删除表

    • 添加记录:单挑、多条

    • 修改记录

    • 根据RowKey获取单挑记录

    • Scan:空、起始、起始结尾、Get

    • Filter : RowFilterPrefixFilterFilterList