一、关键字

分布式存储系统、开源、基于列模式、适合非结构化

二、特性

海量存储,数据可达PB级别

列式存储,一种NoSql数据库

极易扩展,分布式数据库

高并发,多个PC同时处理

稀疏,指HBase列的灵活性,可以指定任意多的列

三、什么适合用HBase?

HBase不适合解决所有的问题:

数据库量要足够多, 如果有十亿及百亿行数据, 那么HBase是一个很好的选项, 如果只有几百万行甚至不到的数据量,RDBMS是一个很好的选择, 因为数据量小的话, 真正能工作的机器少, 剩余的机器处于空闲的状态

不需要辅助索引,静态类型的列,事务等特性

保证硬件资源足够,集群中节点数少于5个,都不能表现的很好

四、应用场景

  • 存储业务数据:不同小区的人口信息、个人的定位信息等
  • 存储日志数据:登录日志、中间件访问日志、推送日志、业务操作日志等信息
  • 存储业务附件:图像、视频、文档等附件信息

五、HBase架构体系

举例hbase例子 hbase的特点和应用场景_数据库

  • HMaster: 集群中的主服务器, 负责监控集群中所有的RegionServer,并且管理着所有的元数据
  • HRegionServer: 管理Regions,每个RegionServer有多个Region, 运行在DataNode上
  • Store: Region有多个Store,每个Store对应表中一个列族
  • Memstore: 一个Store有一个Memstore和多个StoreFile(HFile)
  • StoreFile: 底层是Block
  • HFile: 真实的数据存储文件
  • HLog: 预写日志文件,也叫WAL

 

六、应用开发

  1. 批量创建表

在创建表时只要指定表名、列簇就可以创建了, 当然在实际生产环境中通常会根据业务场景指定好表的预分区,这样可以防止数据存入到一个Region上,造成热点问题。

/* 判断表是否存在 */
  def isExists(tableName: String): Boolean = {
    var result = false
    val tName = TableName.valueOf(tableName)
    if (admin.tableExists(tName)) {
      result = true
    }
    result
  }

  def createTable(tableName: String, columnFamilys: Array[String]) = {
    //操作的表名
    val tName = TableName.valueOf(tableName)
    //当表不存在的时候创建Hbase表
    if (!admin.tableExists(tName)) {
      //创建Hbase表模式
      val descriptor = new HTableDescriptor(tName)
      //创建列簇i
      for (columnFamily <- columnFamilys) {
        descriptor.addFamily(new HColumnDescriptor(columnFamily))
      }
      //创建表
      admin.createTable(descriptor)

      //建立分区表,使数据均匀分布到各个服务器上
      val splitKesy = Array(2, 4, 6, 8).map(Bytes.toBytes(_))
      admin.createTable(descriptor, splitKesy)
      println("create successful!!")
    }
  }

    2. 数据写入

数据写入可以单条写入也可以批量写入, 实际环境中通常是批量写入,这样会提高写入效率

//向hbase表中插入数据, 更新操作一样
  def insertTable(tableName: String, rowkey: String, columnFamily: String, column: String, value: String) = {
    val table = connection.getTable(TableName.valueOf(tableName))

    //准备key 的数据
    val puts = new Put(rowkey.getBytes())
    //添加列簇名,字段名,字段值value
    puts.addColumn(columnFamily.getBytes(), column.getBytes(), value.getBytes())
    //把数据插入到tbale中
    table.put(puts)
    table.close()
    println("insert successful!!")
  }

   3. 数据更新

数据的更新和数据插入代码是一样的, 逻辑是不存在的列会创建插入, 存在则会覆盖掉原有的数据。

//向hbase表中插入数据, 更新操作一样
  def insertTable(tableName: String, rowkey: String, columnFamily: String, column: String, value: String) = {
    val table = connection.getTable(TableName.valueOf(tableName))

    //准备key 的数据
    val puts = new Put(rowkey.getBytes())
    //添加列簇名,字段名,字段值value
    puts.addColumn(columnFamily.getBytes(), column.getBytes(), value.getBytes())
    //把数据插入到tbale中
    table.put(puts)
    table.close()
    println("insert successful!!")
  }

  4. 数据删除

数据删除就是根据表名、rowKey、还有对应的列簇和列删除即可。

//删除某条记录
  def deleteRecord(tableName: String, rowkey: String, columnFamily: String, column: String) = {
    val table = connection.getTable(TableName.valueOf(tableName))

    val info = new Delete(Bytes.toBytes(rowkey))
    info.addColumn(columnFamily.getBytes(), column.getBytes())
    table.delete(info)
    table.close()
    println("delete successful!!")
  }

  5. 数据查询

数据查询通常根据rowKey范围进行扫描查询,这样效率最高

//获取hbase表中的数据
  def scanDataFromHTable(tableName: String, columnFamily: String, column: String) = {
    val table = connection.getTable(TableName.valueOf(tableName))
    //定义scan对象
    val scan = new Scan()
    //添加列簇名称
    scan.addFamily(columnFamily.getBytes())
    //从table中抓取数据来scan
    val scanner = table.getScanner(scan)
    var result = scanner.next()
    //数据不为空时输出数据
    while (result != null) {
      println(s"rowkey:${Bytes.toString(result.getRow)},列簇:${columnFamily}:${column},value:${Bytes.toString(result.getValue(Bytes.toBytes(columnFamily), Bytes.toBytes(column)))}")
      result = scanner.next()
    }
    //通过scan取完数据后,记得要关闭ResultScanner,否则RegionServer可能会出现问题(对应的Server资源无法释放)
    scanner.close()
    table.close()
  }

查询优化的点有

  • 设置Scan缓存, 这样能有效提升扫描查询性能
  • 指定要查询的列, 这样很大程度减少

以上就是本文的全部内容。