第一章 HBase 简介
1.1 HBase定义
HBase是一种分布式、可扩展、支持海联数据存储的NoSQL数据库。
1.2 HBase 数据模型
逻辑上,HBase的数据模型同关系型数据库很类似,数据存储在一张表中,有行有列,但从HBase的底层物理存储结构(k-v)来看,HBase更像是一个multi-dimensional map。
1.2.1 HBase逻辑结构
逻辑概念:
- row key 行键 按字典序
- 列簇 一个类簇中有多个列,列可以动态添加。(personal_info office_info 都是列簇)
- 列 没啥好解释的 (name city phone tel address 都是列)
- Region HBase表的切片,存储在不同的文件夹里,类似于mysql横向切分表
1.2.2 HBase的物理存储结构
HBase实际上面向列式存储,我们逻辑概念上的表在底层是按照Row Key、Column Family、Column Qualifier 、TimeStamp、Type、Value的物理结构存储。
- Row Key 行键值
- Column Family 列簇名
- Column Qualifier 列名
- TimeStamp 时间戳,HBase面向列式存储,某一行的某一列会存储多条记录,但以时间戳最大的为准
- Type 操作类型,本次操作是Put操作、还是Delete操作。没有修改操作,修改只要Put一条最新的数据即可
- Value 存储的该列的列值,例如"张三","李四",“王五”
1.2.3 数据模型
1)Name Space
命名空间,类似与关系型数据库database的概念,每个命名空间下有多个表。HBase有连个自带的命名空间,分别是hbase和 default,hbase中存放的是HBase内置的表,default表使用户默认使用的命名空间。
2)Region
类似有关系型数据库的表概念,不同的是,HBase定义表是只需要声明列簇即可,不需要声明具体的列。这意味着,往HBase
写入数据时候,字段可以动态、按需指定。因此,和关系型数据库相比,HBase能轻松应对字段变更的场景。
3) Row
HBase表中的每行数据都由一个RowKey和多个Column(列)组成,数据按照RowKey的字典顺序存储的,并且查询数据时只能根据RowKey进行建所,所以RowKey的设计十分重要。
4)Column
HBase中的每个列都有Column Family(列簇)和Column Qualifier(列限定符)进行限定,例如info:name 、info:age。建表时,只需要指明列簇,而列限定符无需预先定义。
5)Time Stamp
用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会自动为其加上该字段,其值为写入HBase的时间。
6)Cell
由{Rowkey,lomn Family,Columnb Qualifier,TimeStamp}唯一确定的单元。cell中的数据都是没有类型的,全部都是字节码形式存储的。
1.3 HBase 基本架构
RegionServe作用:数据的增删改查、切分或者合并Region。
Master :表的增删改;分配Region到每个RegionServer,监控每个RegionServer的状态。在Master发生宕机后数据的增删改查不受影响,但是表的增删改受到影响。
第二章 HBase快速入门
2.1 HBase Linux单机安装部署
帮助文档:http://hbase.apache.org/book.html PDF:http://hbase.apache.org/apache_hbase_reference_guide.pdf
为了快速熟悉HBase,我们先单机部署HBase,不用花经历先学些Zookeeper和HDFS。
单机模式下HBase使用系统的文件系统存储,而不是HDFS存储数据,因此我们不需要安装HDFS。
2.1.1 安装JDK
....
2.2.2 HBase 开始
1. 下载HBase稳定版本xxxx-bin.tar.gz,不要下xxxx-src.tar.gz 。https://mirror.bit.edu.cn/apache/hbase/stable/
2. 解压HBase tar xzvf hbase-2.2.6-bin.tar.gz
3. 修改配置,指定JAVA_HONE (可使用whereis java 查询Java安装目录)
4.启动HBase ./bin/start-hbase.sh
5. 访问HBase web UI http://192.168.43.200:16010/
2.2 HBase正常安装部署
2.1.1 Zooleeper 正常部署
2.1.2 Hadoop 正常部署
2.1.3 HBase 解压
2.1.4 HBase 的配置文件
2.1.5 HBase 远程发送到其它
2.1.6 HBase 服务的启动
2.3 HBase Shell操作
2.2.1 基本操作
1. 进入HBase客户端命令行
bin/hbase shell
2. 查看帮助命令
hbase(main):001:0> help
3. 查看当前数据库中有哪些表
hbase(main):002:0> list
2.2.2 表的操作
1. 创建表
2. 插入数据到表
需要注意的是列名必须和列簇一起指定,用冒号拼接
3. 查询表的列簇信息
4.查询表数据
5. scan表,查询所有数据 scan 'tableName'
6.删除表数据
delete命令删除数据至少得精确到某一单元格,不能按行来删除
hbase(main):030:0> deleteall 'employee','1002' 按行删除
7. 万能命令
help 'commond命令' hbase会列出该命令的所有的使用方式。
第三章 HBase进阶
3.1 架构原理
3.1.1 HBase 详细架构
HBase底层依赖HDFS文件系统,数据的存储都是有HDFS负责存储。HBase集群一般由Master和多台RegionServer组成,HBase 的逻辑结构上的表实际上是被横向拆分成多个Region,分布在不同的RegionServer上。当客户端发起写请求时,从zk拿到RegionServer ip地 址,选中某一台RegionServer,RegionServer为了防止数据丢失,采用“数据预写日志”,也就是先写HLog。由于一个Region还会有多个列簇,列簇的数据都存放在Store中,所以列簇对应物理结构上的Store。Store的数据也不是直接写HFile文件的,有一部分也是在内存中,等到了合适时机或者是根据某种策略才会写入到HFile文件中(例如Flush操作)。
3.2 写流程
3.3 MemStore Flush
MemStore 刷写时机:
1. 当某个memstore的大小达到了 hbase.hregion.memstore.flush.size(默认值128M),其所在region的所有memstore都会刷写。
当memstore的大小达到了hbase.hregion.memstore.flush.size(默认值128M)* hbase.hregion.memstore.block.multiplier(默认值4)时,会阻止继续往该memstore写数据。
2. 当达到自动刷写的时间,也会出发memstore flush。自动刷鞋的时间间隔由该属性进行配置 hbase.regionserver.optionalcacheflushinterval(默认1小时)。
3.4 读流程
HBase是读比写慢的存储框架,HBase数据读取的时候会同时读取StoreFile和MemStore,最后把从StoreFile 和MemStore读取的数据合并,为了防止从StoreFile读取的数据会再用到,会放到Block Cache
3.5 StoreFile Compaction
MemStore每发生一次Flush,便会往磁盘上写入一个HDFS小文件,小文件太多是需要进行合并的。
HBase 数据真正删除的时机,在Flush或者是Campaction(并且要Major compaction)的时候。
3.6 Region Split
默认情况下,每个Table起初只有一个Region,随着数据的不断写入,Region会 自动进行拆分。刚拆分时,两个子Region都位于当前的Region Server,但出于负载均衡的考虑,HBMaster有可能会将某个Reion转移给其他的Region Server。
Region Split时机:
1. 当一个reion中的某个Store下所有StoreFile的总大小超过hbase.hregion.max.filesize,该Region就会进行拆分(0.94版本之前)。
2. 当一个region中的某个Store下所有的StoreFile的总大小超过Min(R^2*"hbase.hregion.memstore.flush.size","hbase.hregion.max.filesize"),该Region就会进行拆分,其中R为当前Region Server中属于该Table的个数(0.94版本之后)。
Java Api 报连接错误 java.net.ConnectException: Call to localhost/127.0.0.1:16020 failed on connection exception: org.apache.hbase.thirdparty.io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information。
因为hbase 往zk注册的时候,自己的主机名注册的是localhost,解决办法:修改Linux虚拟机主机名。
# 查看Linux hostname [root@www ~]# hostname www.pipi.hbase # 如果hostname 是localhost.localdomain,修改centos 主机名为 www.pipi.hbase [root@www ~]# vi /etc/sysconfig/network HOSTNAME=www.pipi.hbase # 注意 windows 的host文件需要指定虚拟机的ip 和虚拟机的主机名
第四章 HBase API
4.1 环境搭建
HBase用到的版本是2.2.6,所以我们使用2.2.6的官方版本
项目启动会报java.io.FileNotFoundException: java.io.FileNotFoundException: HADOOP_HOME and hadoop.home.dir are unset 异常,我们可以忽略,我们并没有用到windows 上的hadoop
<dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>2.2.6</version> </dependency>
4.2.1 获取Configuration对象
HBase 官方API文档 https://hbase.apache.org/2.3/apidocs/index.html?overview-summary.html
官方文档里提到 需要在我们能在项目里添加hbase-site.xml和hbase-default.xml文件,hbase-site 可以参考hbase 安装目录下的conf/hbase-site.xml ,hbase-default.xml hbase默认配置可以去Web UI控制台找 http://192.168.43.200:16010/conf
hbase-site.xml
<property> <name>hbase.cluster.distributed</name> <value>false</value> </property> <property> <name>hbase.unsafe.stream.capability.enforce</name> <value>false</value> </property>
hbase-default.xml
<!-- zk 地址--> <property> <name>hbase.zookeeper.quorum</name> <value>192.168.43.200</value> <final>false</final> <source>hbase-default.xml</source> </property> <property> <name>hadoop.registry.zk.quorum</name> <value>192.168.43.200:2181</value> <final>false</final> <source>core-default.xml</source> </property> <property> <name>hbase.zookeeper.property.clientPort</name> <value>2181</value> <final>false</final> <source>programatically</source> </property> <property> <name>hbase.defaults.for.version</name> <value>2.2.6</value> <final>false</final> <source>hbase-default.xml</source> </property>
@Before public void beforeTest() { Configuration config = HBaseConfiguration.create(); try { connection = ConnectionFactory.createConnection(config); } catch (IOException e) { e.printStackTrace(); } Assert.assertNotNull("获取HBase连接为空", connection); } @After public void close() { log.info("关闭连接...."); if (connection != null) { try { connection.close(); } catch (IOException e) { log.error("关闭连接出错", e); } log.info("connection 连接关闭!!!"); } }
4.2.2 判断表是否存在
Admin 客户端能够创建,删除,list,启用,禁用表以及其它修改表操作。
@Test public void testTableIsExsist() { try (Admin admin = connection.getAdmin()) { boolean existed = admin.tableExists(TableName.valueOf("employee")); Assert.assertTrue(existed); log.info("Table employee 存在"); } catch (IOException e) { log.error("获取Admin客户端失败", e); } }
4.2.3 创建表
@Test public void testCreateTable() { try (Admin admin = connection.getAdmin()) { admin.createTable(TableDescriptorBuilder.newBuilder(TableName.valueOf("position")) .setColumnFamily(ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("basic_info")).build()) .build()); log.info("创建position表成功"); } catch (IOException e) { log.error("创建position表失败", e); } }
4.2.4 删除表
@Test public void deleteTable() { try (Admin admin = connection.getAdmin()) { TableName positionTable = TableName.valueOf("position"); /*删除表之前 必须禁用表*/ admin.disableTable(positionTable); admin.deleteTable(positionTable); log.info("删除position表成功"); } catch (IOException e) { log.error("删除position表失败", e); } }
4.2.5 向表中插入数据
@Test public void testPutData() { try (Table table = connection.getTable(TableName.valueOf("position")) ) { Put p = new Put(Bytes.toBytes(2)); p.addColumn(Bytes.toBytes("basic_info"), Bytes.toBytes("position_name"), Bytes.toBytes("经理")); table.put(p); log.info("插入数据成功"); } catch (IOException e) { log.error("插入数据失败", e); } }
4.2.6 删除多行数据
....
4.2.7 获取所有数据
@Test public void testScanConditionQuery() { try (Table table = connection.getTable(TableName.valueOf("position")) ) { Scan scan = new Scan(); ValueFilter valueFilter = new ValueFilter(CompareOperator.EQUAL,new BinaryComparator(Bytes.toBytes("经理"))); scan.setFilter(valueFilter); ResultScanner scanner = table.getScanner(scan); scanner.forEach(result -> { for (Cell cell : result.listCells()) { log.info("RowKey:{} Colomn Family:{} Column:{} value:{}", Bytes.toInt(CellUtil.cloneRow(cell)), Bytes.toString(CellUtil.cloneFamily(cell)), Bytes.toString(CellUtil.cloneQualifier(cell)), Bytes.toString(CellUtil.cloneValue(cell)) ); } }); log.info("查询数据成功"); } catch (IOException e) { log.error("查询数据失败", e); } }
第五章 HBase 优化