Hbase入门
Hbase简介
Hbase是一种NoSQL数据库,这意味着它不像传统的RDBMS数据库那样支持SQL作为查询语言。Hbase是一种分布式存储的数据库,技术上来讲,它更像是分布式存储而不是分布式数据库,它缺少很多RDBMS系统的特性,比如列类型,辅助索引,触发器,和高级查询语言等待。那Hbase有什么特性呢?如下:
- 强读写一致,但不是“最终一致性”的数据存储,这使得它非常适合高速的计算聚合
- 自动分片,通过Region分散在集群中,当行数增长的时候,Region也会自动的切分和再分配
- 自动的故障转移
- Hadoop/HDFS集成,和HDFS开箱即用,不用太麻烦的衔接
- 丰富的“简洁,高效”API,Thrift/REST API,Java API
- 块缓存,布隆过滤器,可以高效的列查询优化
- 操作管理,Hbase提供了内置的web界面来操作,还可以监控JMX指标
什么时候用Hbase?
Hbase不适合解决所有的问题:
- 首先数据库量要足够多,如果有十亿及百亿行数据,那么Hbase是一个很好的选项,如果只有几百万行甚至不到的数据量,RDBMS是一个很好的选择。因为数据量小的话,真正能工作的机器量少,剩余的机器都处于空闲的状态
- 其次,如果你不需要辅助索引,静态类型的列,事务等特性,一个已经用RDBMS的系统想要切换到Hbase,则需要重新设计系统。
- 最后,保证硬件资源足够,每个HDFS集群在少于5个节点的时候,都不能表现的很好。因为HDFS默认的复制数量是3,再加上一个NameNode。
Hbase在单机环境也能运行,但是请在开发环境的时候使用。
- 存储业务数据:车辆GPS信息,司机点位信息,用户操作信息,设备访问信息。。。
- 存储日志数据:架构监控数据(登录日志,中间件访问日志,推送日志,短信邮件发送记录。。。),业务操作日志信息
- 存储业务附件:UDFS系统存储图像,视频,文档等附件信息
Hbase架构
- Zookeeper,作为分布式的协调。RegionServer也会把自己的信息写到ZooKeeper中。
- HDFS是Hbase运行的底层文件系统
- RegionServer,理解为数据节点,存储数据的。
- Master RegionServer要实时的向Master报告信息。Master知道全局的RegionServer运行情况,可以控制RegionServer的故障转移和Region的切分。
Hbase数据模型
在Hbase中,有一些术语需要提前了解。如下:
- Table:Hbase的table由多个行组成
- Row:一个行在Hbase中由一个或多个有值的列组成。Row按照字母进行排序,因此行健的设计非常重要。这种设计方式可以让有关系的行非常的近,通常行健的设计是网站的域名反转,比如(org.apache.www, org.apache.mail, org.apache.jira),这样的话所有的Apache的域名就很接近。
- Column:列由列簇加上列的标识组成,一般是“列簇:列标识”,创建表的时候不用指定列标识
- Column Family:列簇在物理上包含了许多的列与列的值,每个列簇都有一些存储的属性可配置。例如是否使用缓存,压缩类型,存储版本数等。在表中,每一行都有相同的列簇,尽管有些列簇什么东西也没有存。
- Column Qualifier:列簇的限定词,理解为列的唯一标识。但是列标识是可以改变的,因此每一行可能有不同的列标识
- Cell:Cell是由row,column family,column qualifier包含时间戳与值组成的,一般表达某个值的版本
- Timestamp:时间戳一般写在value的旁边,代表某个值的版本号,默认的时间戳是你写入数据的那一刻,但是你也可以在写入数据的时候指定不同的时间戳
HBase 是一个稀疏的、分布式、持久、多维、排序的映射,它以行键(row key),列键(column key)和时间戳(timestamp)为索引。

Hbase与关系型数据库对比
Hbase | RDBMS | |
---|---|---|
数据类型 | 只有字符串 | 丰富的数据类型 |
数据操作 | 增删改查,不支持join | 各种各样的函数与表连接 |
存储模式 | 基于列式存储 | 基于表结构和行式存储 |
数据保护 | 更新后仍然保留旧版本 | 替换 |
可伸缩性 | 轻易增加节点 | 需要中间层,牺牲性能 |
Hbase设计时要考虑的因素
Hbase关键概念:表,rowkey,列簇,时间戳
- 这个表应该有多少列簇
- 列簇使用什么数据
- 每个列簇有有多少列
- 列名是什么,尽管列名不必在建表时定义,但读写数据是要知道的
- 单元应该存放什么数据
- 每个单元存储多少时间版本
- 行健(rowKey)结构是什么,应该包含什么信息
设计要点
行健设计
关键部分,直接关系到后续服务的访问性能。如果行健设计不合理,后续查询服务效率会成倍的递减。
- 避免单调的递增行健,因为Hbase的行健是有序排列的,这样可能导致一段时间内大部分写入集中在某一个Region上进行操作,负载都在一台节点上。可以设计成: [metric_type][event_timestamp],不同的metric_type可以将压力分散到不同的region上
- 行健短到可读即可,因为查询短键不必长键性能好多少,所以设计时要权衡长度。
- 行健不能改变,唯一可以改变的方式是先删除后插入
列簇设计
列簇是一些列的集合,一个列簇的成员有相同的前缀,以冒号(:)作为分隔符。
- 现在Hbase不能很好处理2~3个以上的列簇,所以尽可能让列簇少一些,如果表有多个列簇,列簇A有100万行数据,列簇B有10亿行,那么列簇A会分散到很多的Region导致扫描列簇A的时候效率底下。
- 列簇名的长度要尽量小,一个为了节省空间,另外加快效率,比如d表示data,v表示value
列簇属性配置
HFile数据块,默认是64KB,数据库的大小影响数据块索引的大小。数据块大的话一次加载进内存的数据越多,扫描查询效果越好。但是数据块小的话,随机查询性能更好
复制create 'mytable',{NAME => 'cf1', BLOCKSIZE => '65536'}
数据块缓存,数据块缓存默认是打开的,如果一些比较少访问的数据可以选择关闭缓存
复制create 'mytable',{NAME => 'cf1', BLOCKCACHE => 'FALSE'}
数据压缩,压缩会提高磁盘利用率,但是会增加CPU的负载,看情况进行控制
复制create 'mytable',{NAME => 'cf1', COMPRESSION => 'SNAPPY'}
Hbase表设计是和需求相关的,但是遵守表设计的一些硬性指标对性能的提升还是很有帮助的,这里整理了一些设计时用到的要点。
Hbase安装
下载地址,这里我们使用2.2.6版本。
HBase支持单机安装和分布式安装,这里我们使用伪分布式安装。
修改配置文件
首先修改 hbase-env.sh,在文件末尾直接添加以下配置即可
复制export JAVA_HOME=/root/test_hadoop/jdk8
export HADOOP_HOME=/root/test_hadoop/hadoop3.2
export HBASE_MANAGES_ZK=false # 表示使用外部的zookeeper
export HBASE_LOG_DIR=/root/test_hbase/logs
接下来修改 hbase-site.xml,修改以下几个参数的值
复制<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- 本地文件系统tmp目录-->
<property>
<name>hbase.tmp.dir</name>
<value>/root/test_hbase/tmp</value>
</property>
<!-- 在分布式情况下, 一定设置为false -->
<property>
<name>hbase.unsafe.stream.capability.enforce</name>
<value>false</value>
</property>xml
还需要向hbase-site.xml中添加下面这些参数
复制<!--设置HBase表数据,也就是HBase数据在hdfs上的存储根目录-->
<property>
<name>hbase.rootdir</name>
<value>hdfs://bigdata01:9000/hbase</value>
</property>
<!--zookeeper集群的URL配置,多个host中间用逗号隔开-->
<property>
<name>hbase.zookeeper.quorum</name>
<value>42.192.20.119</value>
</property>
<!--HBase在zookeeper上数据的根目录znode节点-->
<property>
<name>zookeeper.znode.parent</name>
<value>/hbase</value>
</property>
<!--设置zookeeper通信端口,不配置也可以,zookeeper默认就是2181-->
<property>
<name>hbase.zookeeper.property.clientPort</name>
<value>2181</value>
</property>
启动HBase集群
注意:在启动HBase集群之前一定要确保Hadoop集群和Zookeeper集群已经正常启动了。
复制bin/start-hbase.sh
执行jps命令,会发现多了一个HMaster进程(HBase集群主节点中的进程)和HRegionServer进程(HBase集群从节点中的进程)。
如果发现HMaster进程和HRegionServer进程都在,说明HBase进程正常启动了
HBase提供有web界面,可以通过浏览器确认集群是否正常启动,端口默认是16010 http://bigdata01:16010/
停止HBase集群
注意:在停止集群进程的时候,要先停HBase集群进程,再停止Zookeeper集群进程,否则HBase停止程序会一直卡住不动。
复制bin/stop-hbase.sh
Hbase常用Shell命令
进入shell界面
复制bin/hbase shell
基础命令
复制status # 查看集群状态
version # 查看当前版本
whoami # 查看当前用户
DDL命令
复制create 'student','info','level' # 创建student表,包含info和level两个列簇
list # 查看所有表
exists 'student' # 表是否存在
disable 'student' # 禁用student表,禁用之后不能被使用
enable 'student' # 启用student表
drop 'student' # 删除student表,删除之前必须先禁用
truncate 'student' # 清空表数据
is_disabled 'student' # 检查student表是否被禁用
is_enabled 'student' # 检查student表是否被启用
desc 'student' # 查看表详情信息
alter 'student',{NAME=>'level',VERSIONS=>'3'} # 修改student表的level列簇,VERSIONS改为3,表示可以保存3个版本的数据
alter 'student','about' # 对student表增加列簇about
alter 'student',{NAME='about',METHOD='delete'} # 对student表删除列簇about,一个表最少包含一个列簇
增删改查命令
复制put 'student','lisi','info:sex','man' # 对student表增加lisi这个行键,对info列簇增加sex列,值为man
put 'student','lisi','info:age','21' # 对student表增加lisi这个行键,对info列簇增加age列,值为21
put 'student','lisi','level:class','A' # 对student表增加lisi这个行键,对level列簇增加class列,值为A
get 'student','lisi' # 查看指定行的信息,包含所有列簇
get 'student','lisi','info' # 查看指定行的信息,仅包含info列簇
get 'student','lisi','info:age' # 查看指定行的信息,仅包含info列簇的age列
count 'student' # 统计行数
scan 'student' # 扫描表数据,就是查询多行
delete 'student','lisi','info:age' # 删除lisi这一行info列簇的age列信息,仅能删除一个列簇
deleteall 'student','lisi' # 删除lisi这一行,可以删除多个列簇
命名空间
- hbase:系统命名空间,包含 HBase 内部表。
- default:如果在创建表时没有显式指定命名空间,默认会在此命名空间创建表。
复制list_namespace # 查询所有命名空间 类似mysql的database
create_namespace 'n1' # 创建命名空间
create 'n1:student','lisi','info' # 在n1命名空间下创建表student
list # 查询所有命名空间下的表
list_namespace_tables 'n1' # 查询n1命名空间下的表
JavaAPI操作
复制<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.2.6</version>
</dependency>
复制import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
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.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.RowFilter;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* 通过Java操作表数据及表的创建删除操作
*/
public class TestHbaseClient {
public static void main(String[] args) throws IOException {
// testPut();
// testGet();
// testCreateTable();
testScan();
}
private static void testCreateTable() throws IOException {
Connection connection = createConnection();
Admin admin = connection.getAdmin();
ColumnFamilyDescriptor infoColumnFamily = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("info"))
.setMaxVersions(2)
.build();
TableDescriptor userTable = TableDescriptorBuilder.newBuilder(TableName.valueOf("user"))
.setColumnFamilies(Collections.singletonList(infoColumnFamily))
.build();
admin.createTable(userTable);
admin.close();
connection.close();
}
private static void testScan() throws IOException {
Connection connection = createConnection();
Table studentTable = connection.getTable(TableName.valueOf("student"));
Scan scan = new Scan();
//指定查询范围,优化查询性能
scan.withStartRow(Bytes.toBytes(""));
scan.withStopRow(Bytes.toBytes(""));
RowFilter filter = new RowFilter(CompareOperator.EQUAL, new BinaryComparator(Bytes.toBytes("lisi")));
//根据rowkey过滤
scan.setFilter(filter);
ResultScanner resultScanner = studentTable.getScanner(scan);
for (Result result : resultScanner) {
List<Cell> cells = result.listCells();
for (Cell cell : cells) {
byte[] family = CellUtil.cloneFamily(cell);
byte[] qualifier = CellUtil.cloneQualifier(cell);
byte[] value = CellUtil.cloneValue(cell);
System.out.println("列簇:" + new String(family) + ",列:" + new String(qualifier) + ",value:" + new String(value));
}
}
studentTable.close();
connection.close();
}
private static void testGet() throws IOException {
Connection connection = createConnection();
Table studentTable = connection.getTable(TableName.valueOf("student"));
//指定rowkey
Get getReq = new Get(Bytes.toBytes("jack"));
//指定列簇 列名和具体值
getReq.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"));
Result result = studentTable.get(getReq);
String rowKey = new String(result.getRow());
System.out.println("rowKey:" + rowKey);
List<Cell> cells = result.listCells();
for (Cell cell : cells) {
byte[] family = CellUtil.cloneFamily(cell);
byte[] qualifier = CellUtil.cloneQualifier(cell);
byte[] value = CellUtil.cloneValue(cell);
System.out.println("列簇:" + new String(family) + ",列:" + new String(qualifier) + ",value:" + new String(value));
}
byte[] infoAgeValue = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
System.out.println("infoAgeValue:" + new String(infoAgeValue));
studentTable.close();
connection.close();
}
private static void testPut() throws IOException {
Connection connection = createConnection();
Table studentTable = connection.getTable(TableName.valueOf("student"));
//指定rowkey
Put putReq = new Put(Bytes.toBytes("jack"));
//指定列簇 列名和具体值
putReq.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("35"));
studentTable.put(putReq);
studentTable.close();
connection.close();
}
private static Connection createConnection() throws IOException {
Configuration conf = HBaseConfiguration.create();
//zookeeper集群地址
conf.set("hbase.zookeeper.quorum", "ip:2181");
//在zookeeper上数据的根目录znode节点
conf.set("zookeeper.znode.parent", "/hbase");
return ConnectionFactory.createConnection(conf);
}
}
参考
入门Hbase,看这一篇就够了
HBase集群安装部署
美团一面:为什么选用 Hbase,Hbase 和 MySQL 的区别是什么?
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2022-06-01 Kotlin学习之委托
2022-06-01 Kotlin学习之面向对象
2021-06-01 java处理emoji表情