一、关键字
分布式存储系统、开源、基于列模式、适合非结构化
二、特性
海量存储,数据可达PB级别
列式存储,一种NoSql数据库
极易扩展,分布式数据库
高并发,多个PC同时处理
稀疏,指HBase列的灵活性,可以指定任意多的列
三、什么适合用HBase?
HBase不适合解决所有的问题:
数据库量要足够多, 如果有十亿及百亿行数据, 那么HBase是一个很好的选项, 如果只有几百万行甚至不到的数据量,RDBMS是一个很好的选择, 因为数据量小的话, 真正能工作的机器少, 剩余的机器处于空闲的状态
不需要辅助索引,静态类型的列,事务等特性
保证硬件资源足够,集群中节点数少于5个,都不能表现的很好
四、应用场景
- 存储业务数据:不同小区的人口信息、个人的定位信息等
- 存储日志数据:登录日志、中间件访问日志、推送日志、业务操作日志等信息
- 存储业务附件:图像、视频、文档等附件信息
五、HBase架构体系
- HMaster: 集群中的主服务器, 负责监控集群中所有的RegionServer,并且管理着所有的元数据
- HRegionServer: 管理Regions,每个RegionServer有多个Region, 运行在DataNode上
- Store: Region有多个Store,每个Store对应表中一个列族
- Memstore: 一个Store有一个Memstore和多个StoreFile(HFile)
- StoreFile: 底层是Block
- HFile: 真实的数据存储文件
- HLog: 预写日志文件,也叫WAL
六、应用开发
- 批量创建表
在创建表时只要指定表名、列簇就可以创建了, 当然在实际生产环境中通常会根据业务场景指定好表的预分区,这样可以防止数据存入到一个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缓存, 这样能有效提升扫描查询性能
- 指定要查询的列, 这样很大程度减少
以上就是本文的全部内容。