Fork me on GitHub

hbase

hbase

1.介绍

  • base适合存储PB级别的海量数据,在PB级别的数据以及采用廉价PC存储的情况下,能在几十到百毫秒内返回数据。这与Hbase的极易扩展性息息相关。正是因为Hbase良好的扩展性(存储在HDFS),才为海量数据的存储提供了便利。

  • 特点

    1.海量存储:适合存储PB级别的海量数据
    2.列式存储:列族存储,Hbase是根据列族来存储数据的。列族下面可以有非常多的列,列族在创建表的时候就必须指定。
    3.极易扩展:Hbase的扩展性主要体现在两个方面,一个是基于上层处理能力(RegionServer)的扩展,一个是基于存储的扩展(HDFS)(通过横向添加RegionSever的机器,进行水平扩展,提升Hbase上层的处理能力,提升Hbsae服务更多Region的能力。)
    4.高并发:由于目前大部分使用Hbase的架构,都是采用的廉价PC,因此单个IO的延迟其实并不小,一般在几十到上百ms之间。这里说的高并发,主要是在并发的情况下,Hbase的单个IO延迟下降并不多。能获得高并发、低延迟的服务。
    5.稀疏:稀疏主要是针对Hbase列的灵活性,在列族中,你可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间的。
    
  • HBASE的应用场景

    不适合:需要数据分析,比如报表。 因为会存在行数据不完整情况
    	   单表数据在千万级别以下
    适合: 单表唱过千万,并发量很高,数据分析需求弱,或者不需要那么灵活和实时。
    

2.HBASE架构

HBASE: 弱化HMaster功能,让HMaster管理多个RegionServer.元数据在HMaster中。将元数据注册到zookepeer中,客户端访问时候直接访问zookepeer。如果HMaster宕机,我们从zookepeer中拿取元数据,然后通过元数据去RegionServer拿到数据。所以当HMaster宕机是不影响整个过程。HBASE安装需要zookepeer工具提供。

3.安装

  • 安装前提:hdfs zookepeer已经安装完毕。

  • 下载hbase

  • 解压

    tar -zxvf hbase-1.2.6-bin.tar.gz 
    
  • 更改conf下hbase-env.sh环境

    cd hbase-1.2.6/conf/
    vim hbase-env.sh
    	export JAVA_HOME=/usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/
    	# 将内部的zookepeer关掉,使用自己的
    	export HBASE_MANAGES_ZK=false
    
  • 配置hbase-site.xml 文件

    vim hbase-site.xml 
    
    <configuration>
    <!-- 指定hbase在HDFS上存储的路径 -->
    <property>
    <name>hbase.rootdir</name>
    <value>hdfs://linux01:9000/hbase</value>
    </property>
    <!-- 指定hbase是分布式的 -->
    <property>
    <name>hbase.cluster.distributed</name>
    <value>true</value>
    </property>
    <!-- 指定zk的地址,多个用“,”分割 -->
    <property>
    <name>hbase.zookeeper.quorum</name>
    <value>linux01:2181,linux02:2181,linux03:2181</value>
    </property>
    </configuration>
    
  • 配置regionserver 集群的机器

    linux01
    linux02
    linux03
    
  • 时间同步,HBASE数据同步,时间要求一致

    
    
  • 配置HBASE环境变量

    vim /etc/profile
    export HBASE_HOME=/opt/hdp/hbase-1.2.6
    export PATH=$HADOOP_HOME/sbin:$HBASE_HOME/bin
    source /etc/profile
    
  • 安装完的hbase分发linux02 linux03

    scp -r hbase-1.2.6 linux02:$PWD
    scp -r hbase-1.2.6 linux03:$PWD
    
  • 启动zookepeer

    /opt/zookeeper-3.4.6/bin/zkServer.sh start /opt/zookeeper-3.4.6/conf/zoo.cfg
    
  • 启动dfs

    [root@linux01 bin]# start-dfs.sh
    
  • linux01---->master启动hbase

    hbase-daemon.sh start master
    
  • 启动linux01 regionserver

    hbase-daemon.sh start regionserver
    
  • 输入网址http://10.0.0.134:16010/master-status如下图启动完毕

  • 在linux02启动master,会将其作为备用master

    hbase-daemon.sh start master
    

  • 单节点启动

    # region停止
    hbase-daemon.sh stop regionserver
    # region启动
    hbase-daemon.sh start regionserver
    # master停止
    hbase-daemon.sh stop master
    # master启动
    hbase-daemon.sh start master
    
  • 一键启停

    会加载regionservers文件配置的节点启动resionserver
    start-hbase.sh
    stop.hbase.sh
    
  • 注意出现如下警告:

    ![image-20210115182522469](C:\Users\Xu jk\AppData\Roaming\Typora\typora-user-images\image-20210115182522469.png)

    解决:
    vim /opt/hdp/hbase-1.2.6/conf/hbase-env.sh 
    
    删除如下两行:
    export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"
    export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"
    # 仅仅是针对JDK7    JDK8中做了优化。所以这两行可以删除
    
  • 如果启动失败:1检查时间是否同步,2检查zookepeer和hdfs是否启动成功

4.HBase表模型

  • hbase的表模型跟mysql之类的关系型数据库的表模型差别巨大.

  • hbase的表模型种有:行的概念,列族的概念,但没有字段的概念.

  • 行种村的都是key-value对,每行种的key-value对中的key可以是各种各样的,每行中的key-value对的数量也可以是各种各样.

  • Hbase表模型要点:

1.一个表,有表名
2.一个表可以分为多个列族.(不同列族的数据会存储在不同文件中)
3.表中的每一行有一个 "行键rowkey" 行键在表中不能重复(类似mysql主键),它是定长的.
4.表中每一对kv数据称位一个cell
5.hbase可以对数据存储多个历史版本(历史版本数量可配置)
6.整张表由于数据量过大,会被横向切分成若干个region(用rowkey范围标识),不同region数据也存储在不同文件中.
7.hbase会对插入数据按顺序存储.
存储示例:
	rowkey|columnFamily|key|value
而且hbase会对存入的keyvalue数据自动排序,排序规则如下:
	先按行键,再按列族名,再按key名 进行排序(字典顺序逐,字节比较)
  • 如下图:

  • hbase连接客户端

    hbase shell
    
  • xshell配置(xshell操作客户端无法删除,需要对xshell配置)

    文件 -->属性-->终端-->键盘 
    在 DELETE键序列  和 BACKSPACE键序列 中都选择  ASCII 127
    

5.Hbase存储数据类型

  • 全是二进制

6.shell命令

  • 基本命令
1.查看版本
version
2.查看hbase的状态
staus
3.查看当前用户信息
whoami
4.查看表的帮助信息
table_help
  • 创建表(必须指定列族)
# 创建tb_user表,列族为info
create 'tb_user' , 'info'
create 'tb_user2' , 'info1', 'info2'
  • list查看所有表名
hbase(main):010:0> list
TABLE                 
tb_user                      
1 row(s) in 0.0530 seconds
=> ["tb_user"]

# 正则查询(匹配tb开头的表)
list "tb.*"
  • 查看表详情
hbase(main):012:0> desc 'tb_user'
Table tb_user is ENABLED     #表示可用的            
tb_user                         
COLUMN FAMILIES DESCRIPTION                       
{NAME => 'info', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED
_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE',
 MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE => '65536', REPLICATION_SCOPE => '0'
}                       
1 row(s) in 4.2550 seconds

# 字段意义
NAME 列族名
TTL 过期时间
BLOCKSIZE 大小
  • 插入数据(更新)
put 'tb_user' , 'rowkey001' , 'info:age', 25
put 'tb_user' , 'rowkey001' , 'info:name' , 'ming'
	 表名		    行键	    列族:属性(列) 	值
  • 扫描表的内容
# 扫描全表的内容
hbase(main):019:0> scan "tb_user"
ROW                     COLUMN+CELL                                                       
 rowkey001              column=info:age, timestamp=1610722955208, value=25                
 rowkey001              column=info:name, timestamp=1610722885973, value=ming             
1 row(s) in 0.0250 seconds
# 显示tb_user前2行数据
scan "tb_user" , {LIMIT=>2}
# 显示tb_user前2行数据 从rowkey002开始
scan "tb_user" , {STARTROW=>'rowkey002',LIMIT=>2}
# 显示tb_user 从 rowkey002 到 rowkey005 数据 不包含rowkey005
scan "tb_user" , {STARTROW=>'rowkey002',STOPROW=>"rowkey005"}

  • 内存数据刷新到磁盘:(当内存到128M也会自动刷入数据)
hbase(main):027:0> flush 'tb_user'
  • get获取数据
# 获取表tb_user 键为rowkey001数据
get "tb_user" , "rowkey001"
# 获取表tb_user 键为rowkey001的name数据
get "tb_user" , "rowkey001" , "info:name"
# 获取表tb_user 键为rowkey001,列族info的数据
get "tb_user" , "rowkey001" , "info"
  • 添加列族
# tb_user表下新增info2列族
alter "tb_user" , "info2"
  • 删除列族
alter "tb_user" ,"delete"=> "info2"
  • 删除某个value
delete "tb_user2", "rowkey002" , "info2:name"
  • 清空表
disable "tb_user2"
  • 停用表
disable "tb_user2"
  • 查看表是否停用
is_disabled "tb_user2"
  • 删除所有表
# 删除tb开头表
drop_all "tb.*"
  • 停用多个表
# 停用tb开头表
disable_all "tb.*"
  • 删除表
drop "tb_user2"
# 删除表前先将表停用
  • 启用一张表
enable "tb_user2"
  • 创建快照
# 将tb_user2 新建名为info快照
snapshot "tb_user2", "info"
  • 查看快照
list_snapshots
  • 恢复快照
# 先禁用
disable "tb_user"
restore_snapshot "info"   #info为快照名
enable "tb_user"
scan "tb_user"
  • 克隆数据到新表
clone_snapshot "info" , "t_test2"
			  快照名     表名(新表没有创建过的)
  • 显示表有多少个数据
count "tb_user2"
  • 判断表是否存在
exists "tb_user2"
  • 查看列族存储数据region所在机器
locate_region "tb_user", "info"

7.hfile store flush

  • 原理
我们知道数据存储在RegionServer 中,当一个RegionServer存储数据是有限制的,一旦超出限制会对数据进行拆分,存储到其他机器中(Regionserver)中,当然一个RegionServer内有多个Region (比如有user表,order表...),数据真正存储在Store(Store可以认为是一个列族)中,刚开始数据不到128M会存储在内存中(memStore,memStore可以有效帮助我们提高查询速度),而数据在内存中一旦宕机可能会引起数据丢失,通过日志(logfile)可解决此问题。它会先写在一个叫Write-Ahead logfile的文件中,然后再写到内存中。一旦数据占用空间到128M会flush刷入文件(HFile)中.当然你也可以人为去flush,所有的关键在于rowKey范围,rowKey范围决定了我们数据存储在不同机器上,如果rowkey设计不合理,则会导致数据倾向。



其内部优化:当你执行大量flush会形成大量小文件,它的内部会进行文件合并,同样当你进行大量删除,它也会进行相同操作。

8.Hbase JavaApi

package xjk.com.hbase;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Delete;
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;

public class TestClient {
	static Connection conn = null;
	static {
		try {
			// 创建初始化的配置对象
			Configuration conf = HBaseConfiguration.create();
			// 设置ZK集群位置
			conf.set("hbase.zookeeper.quorum", "10.0.0.134:2181,10.0.0.131:2181,10.0.0.132:2181");
			// 获取连接对象
			conn  = ConnectionFactory.createConnection(conf);
			// 管理对象  对表操作
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	/*
	 *  判断表是否存在
	 * @param tbName 表名
	 * */
	public static boolean tableExists(String tbName) throws Exception {
		Admin admin = conn.getAdmin();
		return admin.tableExists(TableName.valueOf(tbName));
	}
	/*
	 * 创建表
	 *   @param tbName 表名
	 * 	  @param cfs  String ... 方法可变参数:列族
	 * */
	public static void createTable(String tbName, String ...cfs) throws Exception {
		Admin admin = conn.getAdmin();
		if (!tableExists(tbName)) {
			// 创建表描述对象
			HTableDescriptor tbd = new HTableDescriptor(TableName.valueOf(tbName));
			// 遍历创建列族
			for (String cf : cfs) {
				HColumnDescriptor family = new HColumnDescriptor(cf.getBytes());
				// 添加列族
				tbd.addFamily(family);
			}
			// 生成表
			admin.createTable(tbd);
			System.out.println(tbName + "创建成功");
		} else {
			System.out.println(tbName + "表已经存在");
		}
	}
	/*
	 * 删除表
	 *   @param tbName 表名
	 * */
	public static void deleteTable(String tbName) throws Exception {
		Admin admin = conn.getAdmin();
		if (tableExists(tbName)) {
			// 禁用表
			admin.disableTable(TableName.valueOf(tbName));
			admin.deleteTable(TableName.valueOf(tbName));
			System.out.println("删除" + tbName + "成功");
		} else {
			System.out.println("删除" + tbName + "失败");
		}
	}
	/*
	 * 添加一行数据 (windows本地要配置hosts文件否则插入数据阻塞)
	 *   @param tbName 表名
	 *   @param rk 行键
	 *   @param cf 列族
	 *   @param key 键
	 *   @param value 值
	 * */
	public static void addValue(String tbName, String rk, String cf, String key, String value) throws Exception {
		Table table = conn.getTable(TableName.valueOf(tbName));
		Put put = new Put(rk.getBytes());;
		// 参数1:列族, 参数2:key  参数3:value
		put.addColumn(cf.getBytes(), key.getBytes(), value.getBytes());
		table.put(put);
		System.out.println("创建数据成功");
	}
	/*
	 * 查询获取表数据(按照行范围)
	 *   @param tbName 表名
	 * */
	public static void getAllData(String tbName, String start, String end) throws Exception {
		Table table = conn.getTable(TableName.valueOf(tbName));
		// 创建Scan对象
		Scan scan = new Scan();
		// 设置起始行
		scan.setStartRow(Bytes.toBytes(start));
		// 设置结束行(不包含当前行)
		scan.setStopRow(Bytes.toBytes(end));
		// 将数据转换成字节
		// byte[] bytes = Bytes.toBytes("");
		// 全表扫描
		ResultScanner scanner = table.getScanner(scan);
		// 遍历每行单元格
		for (Result result : scanner) {
			// 获取行单元格 行的单元格个数不一定一致
			Cell[] rawCells = result.rawCells();
			for (Cell cell: rawCells) {
				// 行键
				System.out.print(new String(CellUtil.cloneRow(cell)) + "--");
				//  列族
				System.out.print(new String(CellUtil.cloneFamily(cell)) + "--");
				//  键(属性)
				System.out.print(new String(CellUtil.cloneQualifier(cell)) + "--");
				//  值
				System.out.print(new String(CellUtil.cloneValue(cell)));
				System.out.println();
			}
		}
	}
	public static void addValues(String tbName, String rk, String cf, String key, String value) throws Exception {
		Table table = conn.getTable(TableName.valueOf(tbName));
		Put put2 = new Put(rk.getBytes());
		// 参数1:列族, 参数2:key  参数3:value
		put2.addColumn(cf.getBytes(), key.getBytes(), value.getBytes());
		List<Put> list = new ArrayList<>();
		list.add(put2);
		table.put(list);
		System.out.println("创建数据成功");
	}
	/*
	 * 查询表的某个行的列族数据
	 *   @param tbName 表名
	 *   @param rk 行键
	 *   @param cf 列族
	 * */
	public static void getDataByRkAndCf(String tbName, String rk, String cf) throws Exception {
		Table table = conn.getTable(TableName.valueOf(tbName));
		// 获取行get对象
		Get get = new Get(rk.getBytes());
		// 指定列族
		get.addFamily(cf.getBytes());
		Result result = table.get(get);
		Cell[] rawCells = result.rawCells();
		// 遍历
		for (Cell cell : rawCells) {
			// 行键
			System.out.print(new String(CellUtil.cloneRow(cell)) + "--");
			//  列族
			System.out.print(new String(CellUtil.cloneFamily(cell)) + "--");
			//  键(属性)
			System.out.print(new String(CellUtil.cloneQualifier(cell)) + "--");
			//  值
			System.out.print(new String(CellUtil.cloneValue(cell)));
			System.out.println();
		}
	}
	/*
	 * 查询某个具体属性值
	 *   @param tbName 表名
	 *   @param rk 行键
	 *   @param cf 列族
	 *   @param cf 键
	 * */
	public static void getDataByKey(String tbName, String rk, String cf, String key) throws Exception {
		Table table = conn.getTable(TableName.valueOf(tbName));
		Get get = new Get(rk.getBytes());
		// 设置列族和属性
		get.addColumn(cf.getBytes(), key.getBytes());
		Result result = table.get(get);
		Cell[] rawCells = result.rawCells();
		// 遍历
		for (Cell cell : rawCells) {
			// 行键
			System.out.print(new String(CellUtil.cloneRow(cell)) + "--");
			//  列族
			System.out.print(new String(CellUtil.cloneFamily(cell)) + "--");
			//  键(属性)
			System.out.print(new String(CellUtil.cloneQualifier(cell)) + "--");
			//  值
			System.out.print(new String(CellUtil.cloneValue(cell)));
			System.out.println();
		}
	}
	/*
	 * 删除某个行数据
	 *   @param tbName 表名
	 *   @param rk 行键
	 * */
	public static void deleteByRow(String tbName,String rk) throws Exception {
		Table table = conn.getTable(TableName.valueOf(tbName));
		Delete d = new Delete(rk.getBytes());
		table.delete(d);
	}
	/*
	 * 删除多个行数据
	 *   @param tbName 表名
	 *   @param rk 行键
	 * */
	public static void deleteByRows(String tbName,String... rks) throws Exception {
		Table table = conn.getTable(TableName.valueOf(tbName));
		List <Delete> list = new ArrayList<>();
		for (String rk : rks) {
			Delete d = new Delete(rk.getBytes());
			list.add(d);
		}
		table.delete(list);
	}
	public static void main(String[] args) throws Exception {
		// 判断表是否存在
		//	 System.out.println(tableExists("tb_user"));
		// 创建表
        //	 createTable("demo2", "info1","info2");
		// 删除表
        //	 deleteTable("demo2");
		// 创建数据
		// addValue("demo2", "rowkey005", "info1", "address", "beijing");
		// 获取全表数据
		 // getAllData("demo2", "rowkey002", "rowkey004");
		 // 查询表的某个行的列族数据
		 // getDataByRkAndCf("demo2", "rowkey002", "info1");
		// 查询某个具体属性值
		 // getDataByKey("demo2", "rowkey002", "info1", "name");
		// 删除行数据
		// deleteByRow("demo2", "rowkey005");
		// 删除多个行数据
		deleteByRows("demo2", "rowkey004", "rowkey003");
	}
}

9.过滤器

  • 基础API中查询操作在面对大数据时候比较苍白,这里Hbase提供高级的查询方法:Filter,它可以根据簇,列,版本等更多的条件来对数据进行过滤,基于Hbase本身提供三维有序(主键有序,列有序,版本有序),这些Filter可以高效完成查询过滤的任务,带有Filter条件的RPC查询请求会把Filter分发各个RegionServer,是一个服务端的过滤器,这样可以降低网络传输的压力。

  • 要完成一个过滤的操作,至少需要两个参数,一个是抽象的操作复,Hbase提供了枚举类型的变量来标识这些抽象操作符:LESS/LESS_OR_EQUAL/EQUAL/NOT_EQUAL等。另外一个就是具体的比较器。代表具体的比较逻辑,如可以提高字节级的比较,字符串级比较等,有了这两个参数,我们就可以清除的定义筛选的条件,过滤数据。

  • 抽象操作符(比较运算符)

    LESS <
    LESS_OR_EQUAL <=
    EQUAL =
    NOT_EQUAL <>
    GREATER_OR_EQUAL >=
    GREATER >
    NO_OP 排除所有
    
  • 比较器(指定比较机制)

    BinaryComparator 按字节索引顺序比较指定字节数组,采用 Bytes.compareTo(byte[])
    BinaryPrefixComparator 跟前面相同,只是比较左端的数据是否相同
    NullComparator 判断给定是否为空
    BitComparator 按位比较
    RegexStringComparator 提供一个正则的比较器 仅支持EQUAL 和 非EQUAL
    SubstringComparator 判断提供的子串是否出现在value中
    
  • 过滤器种类:

    • 行过滤器
    过滤 判断子串行键是否包含rk
    Filter filter = new RowFilter(CompareOp.EQUAL, new SubstringComparator("rk"));
    
    • 列族过滤器
    Filter filter = new FamilyFilter(CompareOp.EQUAL, new BinaryComparator("info1".getBytes()));
    
    • 列过滤器(属性 key过滤)
    Filter filter = new QualifierFilter(CompareOp.EQUAL, new BinaryComparator("age".getBytes()));
    
    • 值过滤器
    Filter filter = new ValueFilter(CompareOp.EQUAL, new BinaryComparator("xjk".getBytes()));
    
    • 单列过滤器
      • 返回符合条件的整行数据 和没有此属性的行
    Filter filter = new SingleColumnValueFilter("info2".getBytes(), "name".getBytes(),CompareOp.EQUAL, new BinaryComparator("xjk".getBytes()));
    
    • 针对行键
    //rk开头
    Filter filter = new PrefixFilter("rk".getBytes());
    
    • 列前缀
    // 所有有name的列(key)
    Filter filter = new ColumnPrefixFilter("name".getBytes());
    

10.读写数据流程

  • 读取数据

    master注册元数据到zookpeer,客户端请求去zookepeer找到元数据,然后根据元数据去找到对应存储数据的region,在region中先从内存中找(memStore),没有找到会再去缓存中找(CacheStore),缓存中没有才会去文件中找(HFile),当从HFile找到数据时候,它会将找到数据再放到CacheStore,下次找的时候更快。当然缓存中保存数据方式是堆外内存,因为堆内内存会存在溢出现象,但堆外内存不好管理,它内部做了一个优化-->(堆外内存+固态磁盘内存)。
    
  • 写数据

    向hbase表中添加数据,首先master将元数据注册到zookepeer中,master管理着多个RegionServer,master中有一个meta它记录每张表存储范围。  当客户端向zookepeer请求写操作,会返回meta,meta告诉往哪写,客户端会往region写数据,而region会有Hlog,Hlog会操作HDFS客户端进行持久化操作。同时数据也往内存中写(memstore),当你文件到达128M时候会触发flush。flush会生成一个HFile,生成HFile操作HDFS客户端进行持久化。当HDFS将数据持久化后,它会将刷新成功后数据的Hlog记录删掉。将当然一个Region有多个列族(Store),
    
    因HDFS不支持随机访问,所有数据都是写操作(追加),当你执行删除,也是执行添加操作,
    	# put rk001 "abc"
    	# 删除: rk001 "" [delete]
    ***当我们执行查询时候,发现会根据墓碑标记的数据,进行合并。修改操作特使一样会维护内部一个版本。
    

11 row设计的基本思路

  • 设计要根据维度查询频率进行设计,设计rowkey要定长且唯一。比如如下:

    我们对rowkey设计 (userID(6位) + time(8位) + fileID(6位))
    000001 20180501 000001
    000001 20180501 000002
    000001 20180502 000003
    000001 20180503 000003
    000002 20180501 000001
    000002 20180501 000002
    ...
    当用户id相同我们可以根据用户 + 时间进行查询排序,当用户和时间相同我们可以进行用户+时间+文件id进行查询排序
    

    ​ 在建立一个scan对象后,我们setStartRow(0000012018050 000001),setEndRow(000001 20180503 000003).这样,scan时只扫描userID=1的数据,且时间范围限定在这个指定时间段内,满足按用户以及按时间范围对结果的筛选,并且由于记录集中存储,性能会很好。

    ​ 然后使用SingleColumnValueFilter 分别约束name的上下限,于category的上下限,满足按同时按文件名以及分类名的前缀匹配。

12.Phoenix整合hbase

  • Phoenix由saleforce.com开源的一个项目,后又捐给了Apache。它相当于一个Java中间件,帮助开发者像使用JDBC访问关系型数据库一样,访问NoSQL数据库HBase.

  • 下载

    http://phoenix.apache.org/download.html
    
  • 安装:

    tar -zxvf apache-phoenix-4.14.0-HBase-1.2-bin.tar.gz
    
  • 将phoenix-4.14.0-HBase-1.2-server.jar赋值hbase 的lib下

    cp phoenix-4.14.0-HBase-1.2-server.jar /opt/hdp/hbase-1.2.6/lib
    
  • 将phoenix-4.14.0-HBase-1.2-server.jar 包发送给其他节点的lib目录下

    scp phoenix-4.14.0-HBase-1.2-server.jar linux02:/opt/hdp/hbase-1.2.6/lib
    scp phoenix-4.14.0-HBase-1.2-server.jar linux03:/opt/hdp/hbase-1.2.6/lib
    
  • 重启HBASE

    stop-hbase.sh
    start-hbase.sh
    
  • 连接phoenix

    # 到 /root/apache-phoenix-4.14.0-HBase-1.2-bin/bin 目录下
    # 连接
    ./sqlline.py 10.0.0.134,10.0.0.131,10.0.0.132:2181
    

    这样你可以像操作mysql一样操作hbase了

  • 常用指令

    1.显示所有表
    !table
    2. 退出
    !quit
    3.查看表结构
    !describe "表名"
    4.创建表
    create table user(name varchar not null primary key, age  varchar)salt_buckets=16;
    5.插入数据
    upsert into user values('liutao','25');
    6.查询
    select * from user;
    7.删除
    delete from user where name = 'liutao';
    

    查询数据支持:union all, group by, order by, limit 都支持

13.Python操作hbase

  • Linux安装Thrift

  • yum安装依赖

    yum install automake bison flex g++ git libboost1.55 libevent-dev libssl-dev libtool make pkg-config
    
  • 解压thrift

    tar -zxvf thrift-0.11.0.tar.gz
    cd thrift-0.11.0
    
  • 编译安装

    ./configure --with-cpp --with-boost --with-python --without-csharp --with-java --without-erlang --without-perl --with-php --without-php_extension --without-ruby --without-haskell  --without-go
    make
    make install
    
  • 在Master中Hbase安装目录下bin目录启动thrift服务、

    ./hbase-daemon.sh start thrift
    
  • 安装happybase包

    pip install happybase
    
  • 连接操作

    # 9090端口是 hbase thrift启动默认端口
    connection = happybase.Connection('10.0.0.134',port=9090) 
    

DDL

  • 简单示例

    # 打印所有表名
    import happybase
    connection = happybase.Connection('10.0.0.134',port=9090)
    # 获取所有表
    table_name_list = connection.tables()
    print(table_name_list)
    connection.close()
    # [b'SYSTEM.CATALOG', b'SYSTEM.FUNCTION', b'SYSTEM.LOG', b'SYSTEM.MUTEX', b'SYSTEM.SEQUENCE', b'SYSTEM.STATS', b'USER', b'demo2', b't_test1', b't_test2', b'tb_user', b'user2']
    
  • 获取一个表对象

    table = connection.table(name) 
    # name表名
    # user_prefix 是否使用表前缀,默认True
    
  • 禁用表:删除前需要禁用表

    connection.disable_table("USER")
    
  • 启用表

    connection.enable_table("USER")
    
  • 创建表

    family = {
    	"info1": dict(),
    	"info2": dict()
    }
    #参数1:表名   参数2:列族
    connection.create_table("pyTest", family)
    
  • 删除表

    # 参数1:表名   参数2:是否先禁用
    connection.delete_table("pyTest",disable=False)
    

DML

  • 获取表实例:

    connection.open()
    table = connection.table("tb_user")
    
  • 获取单元格cells

    cells(row, column, versions=None, timestamp=None, include_timestamp=False)  # 获取单元格数据,返回一个byte的list
    row:行
    column:列
    versions:获取的最大版本数量,默认None,即获取所有
    timestamp:时间戳,默认None,即获取所有时间戳版本的数据。可指定一个时间戳,获取小于此时间戳版本的所有数据
    include_timestamp:是否返回时间戳,默认False
    
    
    e.g.:
    connection.open()
    table = connection.table("tb_user")
    content = table.cells("rowkey001", "info:name")
    print(content) # [b'ming']
    
  • 删除指定行数据

    delete(row, columns=None, timestamp=None, wal=True):删除指定行数据,无返回值
    row:行
    columns:列,默认为None,即删除所有列,可传入一个list或tuple来指定删除列
    timestamp:时间戳,默认为None,即删除所有,可传入一个时间戳来删除小于等于此时间戳的所有数据
    wal:是否写入wal,默认为True
    
    e.g.:
    connection.open()
    table = connection.table("tb_user")
    # info为 列族  pwd为字段属性(key)
    table.delete("rowkey001", columns=["info:pwd"])
    connection.close()
    
  • 插入数据

    put(row, data, timestamp=None, wal=True):插入数据,无返回值
    row: 行
    data: 数据,dict类型,{列:值}构成,列与值皆为str类型
    timestamp:时间戳,默认None,即写入当前时间戳
    wal:是否写入wal,默认为True
    
    e.g.:
    table.put("rowkey001", {"info:pwd":"123", "info:sex": "man"})
    
  • 获取一行数据

    row(row, columns=None, timestamp=None, include_timestamp=False):获取一行数据,返回一个dict
    row:行
    columns: 列,默认为None,即获取所有列,可传入一个list或tuple来指定获取列
    timestamp:时间戳。默认为None,即返回最大的那个时间戳的数据。可传入一个时间戳来获取小于此时间戳的最大时间戳的版本数据
    include_timestamp:是否返回时间戳数据,默认为False
    
    e.g.:
    msg = table.row("rowkey001",columns=None,  include_timestamp=False)
    
  • 获取多行数据

    rows(rows, columns=None, timestamp=None, include_timestamp=False):获取多行数据,返回一个list
    rows:行,可传入一个list或tuple来指定获取
    columns: 列,默认为None,即获取所有列,可传入一个list或tuple来指定获取列
    timestamp:时间戳。默认为None,即返回最大的那个时间戳的数据。可传入一个时间戳来获取小于此时间戳的最大时间戳的版本数据
    include_timestamp:是否返回时间戳数据,默认为False
    
    e.g.:
        msg = table.rows(["rowkey001","rowkey005"],columns=("info:name",))
    
  • 扫描器

    scan(row_start=None, row_stop=None, row_prefix=None, columns=None, filter=None, timestamp=None, include_timestamp=False, batch_size=1000, scan_batching=None, limit=None, sorted_columns=False, reverse=False):获取一个扫描器,返回一个generator
    row_start:起始行,默认None,即第一行,可传入行号指定从哪一行开始
    row_stop:结束行,默认None,即最后一行,可传入行号指定到哪一行结束(不获取此行数据)
    row_prefix:行号前缀,默认为None,即不指定前缀扫描,可传入前缀来扫描符合此前缀的行
    columns:列,默认为None,即获取所有列,可传入一个list或tuple来指定获取列
    filter:过滤字符串
    timestamp:时间戳。默认为None,即返回最大的那个时间戳的数据。可传入一个时间戳来获取小于此时间戳的最大时间戳的版本数据
    include_timestamp:是否返回时间戳数据,默认为False
    batch_size:用于检索结果的批量大小
    scan_batching:服务端扫描批处理
    limit:数量
    sorted_columns:是否返回排序的列(根据行名称排序)
    reverse:是否执行反向扫描
    
    e.g.:
        # 筛选:rk 包含 rowkey
        query = "RowFilter ( =,  \'substring:{0}\')".format("rowkey")
    msg = table.scan("rowkey001", "rowkey005",columns=["info:name"],
    filter=query, include_timestamp=False, batch_size=1000, limit=10, sorted_columns=False, reverse=False
                     )
    # print(msg)
    for k,v in msg:
        print(k,v)
    

    更多见官方文档

posted @ 2021-01-16 21:37  是阿凯啊  阅读(527)  评论(0编辑  收藏  举报