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
-
注意出现如下警告:

解决: 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)
更多见官方文档
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库