海量列式非关系数据库HBase 架构,shell与API
HBase的特点:
- 海量存储: 底层基于HDFS存储海量数据
- 列式存储:HBase表的数据是基于列族进行存储的,一个列族包含若干列
- 极易扩展:底层依赖HDFS,当磁盘空间不足的时候,只需要动态增加DataNode服务节点就可以
- 高并发:支持高并发的读写请求
- 稀疏:稀疏主要是针对HBase列的灵活性,在列族中,你可以指定任意多的列,在列数据为空的情 况下,是不会占用存储空间的。
- 数据的多版本:HBase表中的数据可以有多个版本值,默认情况下是根据版本号去区分,版本号就 是插入数据的时间戳
- 数据类型单一:所有的数据在HBase中是以字节数组进行存储
HBase的应用场景:
HBase适合海量明细数据的存储,并且后期需要有很好的查询性能(单表超千万、上亿, 且并发要求高)
HBase数据模型:
HBase整体架构:
Zookeeper
- 实现了HMaster的高可用
- 保存了HBase的元数据信息,是所有HBase表的寻址入口
- 对HMaster和HRegionServer实现了监控
HMaster(Master)
- 为HRegionServer分配Region 维护整个集群的负载均衡
- 维护集群的元数据信息
- 发现失效的Region,并将失效的Region分配到正常的HRegionServer上
HRegionServer(RegionServer)
- 负责管理Region 接受客户端的读写数据请求
- 切分在运行过程中变大的Region
Region
- 每个HRegion由多个Store构成, 每个Store保存一个列族(Columns Family),表有几个列族,则有几个Store,
- 每个Store由一个MemStore和多个StoreFile组成,MemStore是Store在内存中的内容,写到文件 后就是StoreFile。
- StoreFile底层是以HFile的格式保存
HBase shell 基本操作:
入口:hbase shell
hbase(main):001:0> create 'lagou', 'base_info', 'extra_info' 或者(Hbase建表必须指定列族信息) create 'lagou', {NAME => 'base_info', VERSIONS => '3'},{NAME => 'extra_info',VERSIONS => '3'} VERSIONS 是指此单元格内的数据可以保留最近的 3 个版本
添加数据操作:
向lagou表中插入信息,row key为 rk1,列族base_info中添加name列标示符,值为wang put 'lagou', 'rk1', 'base_info:name', 'wang' 向lagou表中插入信息,row key为rk1,列族base_info中添加age列标示符,值为30 put 'lagou', 'rk1', 'base_info:age', 30
向lagou表中插入信息,row key为rk1,列族extra_info中添加address列标示符,值为shanghai put 'lagou', 'rk1', 'extra_info:address', 'shanghai'
查询,更新,删除:
获取表中row key为rk1的所有信息 get 'lagou', 'rk1' 获取lagou表中row key为rk1,base_info列族的所有信息 get 'lagou', 'rk1', 'base_info' 获取表中row key为rk1,base_info列族的name、age列标示符的信息 get 'lagou', 'rk1', 'base_info:name', 'base_info:age' 获取lagou表中row key为rk1,base_info、extra_info列族的信息 hbase(main):010:0> get 'lagou', 'rk1', 'base_info', 'extra_info' 或者 hbase(main):011:0> get 'lagou', 'rk1', {COLUMN => ['base_info', 'extra_info']} 或者 hbase(main):012:0> get 'lagou', 'rk1', {COLUMN => ['base_info:name', 'extra_info:address']} 获取表中row key为rk1,cell的值为wang的信息 get 'lagou', 'rk1', {FILTER => "ValueFilter(=, 'binary:wang')"} 获取表中row key为rk1,列标示符中含有a的信息 get 'lagou', 'rk1', {FILTER => " (QualifierFilter(=,'substring:a'))"} 查询lagou表中的所有信息: scan 'lagou' 查询表中列族为 base_info 的信息: hbase(main):001:0> scan 'lagou', {COLUMNS => 'base_info'} hbase(main):002:0> scan 'lagou', {COLUMNS => 'base_info', RAW => true, VERSIONS => 3} ## Scan时可以设置是否开启Raw模式,开启Raw模式会返回包括已添加删除标记但是未实际删除的数据 ## VERSIONS指定查询的最大版本数 指定多个列族与按照数据值模糊查询: 查询lagou表中列族为 base_info 和 extra_info且列标示符中含有a字符的信息 hbase(main):001:0> scan 'lagou', {COLUMNS => ['base_info', 'extra_info'], FILTER => "(QualifierFilter(=,'substring:a'))"} rowkey的范围值查询(非常重要) 查询lagou表中列族为base_info,rk范围是[rk1, rk3)的数据(rowkey底层存储是字典序) 按rowkey顺序存储。 scan 'lagou', {COLUMNS => 'base_info', STARTROW => 'rk1', ENDROW => 'rk3'} 查询lagou表中row key以rk字符开头的 hbase(main):001:0> scan 'lagou',{FILTER=>"PrefixFilter('rk')"} 更新数据值: 把lagou表中rowkey为rk1的base_info列族下的列name修改为liang put 'lagou', 'rk1', 'base_info:name', 'liang' 删除数据和表: 删除lagou表row key为rk1,列标示符为 base_info:name 的数据 > delete 'lagou', 'rk1', 'base_info:name' 指定rowkey,列名以及时间戳信息进行删除 删除lagou表row key为rk1,列标示符为base_info:name的数据 delete 'lagou', 'rk1', 'base_info:name',1600660619655 删除 base_info 列族 alter 'lagou', 'delete' => 'base_info' 删除lagou表数据 truncate 'lagou' 删除lagou表 #先disable 再drop hbase(main):036:0> disable 'lagou' hbase(main):037:0> drop 'lagou' #如果不进行disable,直接drop会报错 ERROR: Table user is enabled. Disable it first.
HBase JAVA API:
<dependencies> <dependency> <groupId>org.apache.hbase</groupId> <artifactId>hbase-client</artifactId> <version>1.3.1</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testng</groupId> <artifactId>testng</artifactId> <version>6.14.3</version> <scope>test</scope> </dependency> </dependencies>
创建连接:
package com.lagou.hbase.client; 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.Before; import org.junit.Test; import java.io.IOException; public class HbaseClientDemo { Configuration conf = null; Connection conn = null; @Before public void init() throws IOException { //获取一个配置文件对象 conf = HBaseConfiguration.create(); conf.set("hbase.zookeeper.quorum", "linux121,linux122"); conf.set("hbase.zookeeper.property.clientPort", "2181"); //通过conf获取到hbase集群的连接 conn = ConnectionFactory.createConnection(conf); } //释放连接 @After public void realse() { if (conn != null) { try { conn.close(); } catch (IOException e) { e.printStackTrace(); } } } }
创建表:
//创建一张hbase表 @Test public void createTable() throws IOException { //获取HbaseAdmin对象用来创建表 HBaseAdmin admin = (HBaseAdmin) conn.getAdmin(); //创建Htabledesc描述器,表描述器 final HTableDescriptor worker = new HTableDescriptor(TableName.valueOf("worker")); //指定列族 worker.addFamily(new HColumnDescriptor("info")); admin.createTable(worker); System.out.println("worker表创建成功!!"); }
插入数据:
//插入一条数据 @Test public void putData() throws IOException { //需要获取一个table对象 final Table worker = conn.getTable(TableName.valueOf("worker")); //准备put对象 final Put put = new Put(Bytes.toBytes("110"));//指定rowkey put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("addr"), Bytes.toBytes("beijing")); //插入数据,参数类型是put worker.put(put); //准备list<puts>,可以执行批量插入 //关闭table对象 worker.close(); System.out.println("插入数据到worker表成功!!"); }
查询数据:
//查询数据 @Test public void getData() throws IOException { //准备table对象 final Table worker = conn.getTable(TableName.valueOf("worker")); //准备get对象 final Get get = new Get(Bytes.toBytes("110")); //指定查询某个列族或者列 get.addFamily(Bytes.toBytes("info")); //执行查询 final Result result = worker.get(get); //获取到result中所有cell对象 final Cell[] cells = result.rawCells(); //遍历打印 for (Cell cell : cells) { final String rowkey = Bytes.toString(CellUtil.cloneRow(cell)); final String f = Bytes.toString(CellUtil.cloneFamily(cell)); final String column = Bytes.toString(CellUtil.cloneQualifier(cell)); final String value = Bytes.toString(CellUtil.cloneValue(cell)); System.out.println("rowkey-->" + rowkey + "--;cf-->" + f + "---;column--->" + column + "--;value-->" + value); } worker.close(); }
删除数据:
//删除一条数据 @Test public void deleteData() throws IOException { //需要获取一个table对象 final Table worker = conn.getTable(TableName.valueOf("worker")); //准备delete对象 final Delete delete = new Delete(Bytes.toBytes("110")); //执行删除 worker.delete(delete); //关闭table对象 worker.close(); System.out.println("删除数据成功!!"); }
通过Scan全表扫描:
/** * 全表扫描 */ @Test public void scanAllData() throws IOException { HTable teacher = (HTable) conn.getTable(TableName.valueOf("teacher")); Scan scan = new Scan(); ResultScanner resultScanner = teacher.getScanner(scan); for (Result result : resultScanner) { Cell[] cells = result.rawCells();//获取改行的所有cell对象 for (Cell cell : cells) { //通过cell获取rowkey,cf,column,value String cf = Bytes.toString(CellUtil.cloneFamily(cell)); String column = Bytes.toString(CellUtil.cloneQualifier(cell)); String value = Bytes.toString(CellUtil.cloneValue(cell)); String rowkey = Bytes.toString(CellUtil.cloneRow(cell)); System.out.println(rowkey + "----" + cf + "--" + column + "---" + value); } } teacher.close(); }
通过startRowKey和endRowKey进行扫描:
//指定scan 开始rowkey和结束rowkey,这种查询方式建议使用,指定开始和结束rowkey区间避免全表扫描 @Test public void scanStartEndData() throws IOException { //准备table对象 final Table worker = conn.getTable(TableName.valueOf("worker")); //准备scan对象 final Scan scan = new Scan(); //指定查询的rowkey区间,rowkey在hbase中是以字典序排序 scan.setStartRow(Bytes.toBytes("001")); scan.setStopRow(Bytes.toBytes("004")); //执行扫描 final ResultScanner resultScanner = worker.getScanner(scan); for (Result result : resultScanner) { //获取到result中所有cell对象 final Cell[] cells = result.rawCells(); //遍历打印 for (Cell cell : cells) { final String rowkey = Bytes.toString(CellUtil.cloneRow(cell)); final String f = Bytes.toString(CellUtil.cloneFamily(cell)); final String column = Bytes.toString(CellUtil.cloneQualifier(cell)); final String value = Bytes.toString(CellUtil.cloneValue(cell)); System.out.println("rowkey-->" + rowkey + "--;cf-->" + f + ";column--->" + column + "--;value-->" + value); } } worker.close(); }