HBase的Phoenix框架
Hbase适合存储大量的对关系运算要求低的NOSQL数据,受Hbase 设计上的限制不能直接使用原生的API执行在关系数据库中普遍使用的条件判断和聚合等操作。Hbase很优秀,一些团队寻求在Hbase之上提供一种更面向普通开发人员的操作方式,Apache Phoenix即是。
Phoenix 基于Hbase给面向业务的开发人员提供了以标准SQL的方式对Hbase进行查询操作,并支持标准SQL中大部分特性:条件运算,分组,分页,等高级查询语法。
文章目录
1、Phoenix 搭建
phoenix-hbase-2.2-5.1.3-bin.tar.gz HBase 2.2.7 hadoop 3.1.1
1、关闭hbase集群,在master中执行
stop-hbase.sh
2、上传解压配置环境变量
# 解压tar -zxvf phoenix-hbase-2.2-5.1.3-bin.tar.gz -C /usr/local/soft/
# 改名cd /usr/local/soft
mv phoenix-hbase-2.2-5.1.3-bin phoenix-5.1.3
# 配置环境变量vim /etc/profile
# 加入以下内容exportPHOENIX_HOME=/usr/local/soft/phoenix-5.1.3
exportPATH=$PATH:$PHOENIX_HOME/bin
3、分发Phoenix的Jar包
将phoenix-server-hbase-2.2-5.1.3.jar复制到所有节点的hbase lib目录下
# 在master上执行cd /usr/local/soft/phoenix-5.1.3
# 先放入Master的${HBASE_HOME}/lib目录下cp phoenix-server-hbase-2.2-5.1.3.jar $HBASE_HOME/lib/
# 切换目录cd${HBASE_HOME}/lib
# 分发至node1、node2scp phoenix-server-hbase-2.2-5.1.3.jar node1:`pwd`scp phoenix-server-hbase-2.2-5.1.3.jar node2:`pwd`
4、启动hbase
start-hbase.sh
2、Phoenix使用
1、连接sqlline
第一次启动稍慢
sqlline.py master
# 无报错并出现以下日志且正常进入phoenix客户端即表示搭建成功
Connected to: Phoenix (version 5.1)
Driver: PhoenixEmbeddedDriver (version 5.1)
Autocommit status: true
Transaction isolation: TRANSACTION_READ_COMMITTED
sqlline version 1.9.0
0: jdbc:phoenix:master,node1,node2>
2、常用命令
# 1、创建表CREATETABLEIFNOTEXISTS STUDENT (
id VARCHARNOTNULLPRIMARYKEY,
name VARCHAR,
age BIGINT,
gender VARCHAR,
clazz VARCHAR);-- PRIMARY KEY 表示主键 有序并且全局唯一的-- VARCHAR 由于HBASE中存储数据没有长度限制,所以不需要加上长度-- PHOENIX中创建的表都会在HBASE中映射有一张表# 2、显示所有表showtables;# 或者是!table# 3、插入数据
upsert into STUDENT values('1500100004','葛德曜',24,'男','理科三班');
upsert into STUDENT values('1500100005','宣谷芹',24,'男','理科六班');
upsert into STUDENT values('1500100006','羿彦昌',24,'女','理科三班');-- 使用upsert添加数据时,如果ID相同,那么数据会替换更新
upsert into STUDENT values('1500100004','吴天诺',18,'男','bigdata');-- 数据插入后,在HBASE中会对应有数据,其中RowKey是Phoenix表中的ID-- 其ID在建表时添加了 PRIMARY KEY修饰-- 其他列对应的列族为0列名称为建表时的表名 # 4、查询数据,支持大部分sql语法,select*from STUDENT ;select*from STUDENT where age=24;select gender,count(*)as num from STUDENT groupby gender;select*from student orderby gender;# 5、删除数据deletefrom STUDENT where id='1500100006';# 6、删除表droptable STUDENT;# 7、退出命令行!quit
# 更多语法参照官网
https://phoenix.apache.org/language/index.html-- 其他使用示例CREATETABLE gender_cnt(
gender varcharNOTNULLPRIMARYKEY,
cnt BIGINT);
upsert into gender_cnt SELECT gender,count(*)from STUDENT groupby gender;-- 查看SQL的执行计划EXPLAINSELECT*FROM STUDENT WHERE name ='宣谷芹';-- 对于ROWKey进行过滤时,不需要使用FULL Scan EXPLAINSELECT*FROM STUDENT WHERE id ='1500100004';-- 聚合操作同样触发FULL SCAN,慎用!EXPLAINSELECTcount(*)FROM STUDENT WHERE id ='1500100004'and clazz LIKE'%三班%';
3、表映射
默认情况下,直接在hbase中创建的表,通过phoenix是查看不到的
如果需要在phoenix中操作直接在hbase中创建的表,则需要在phoenix中进行表的映射。
映射方式分为两种:视图映射和表映射
3.1、视图映射
Phoenix创建的视图是只读的,所以只能用来做查询,无法通过视图对源数据进行修改等操作
# hbase shell 进入hbase命令行
hbase shell
# 创建hbase表create'test','name','company'# 插入数据
put 'test','001','name:firstname','zhangsan1'
put 'test','001','name:lastname','zhangsan2'
put 'test','001','company:name','数加'
put 'test','001','company:address','合肥'-- 在phoenix创建视图-- 注意表名添加双引号的目的是为了防止大小写转换,如果不加,那么表示当前HBASE中的表名称为大写 最好添加-- primary key -》 HBASE中的RowKey信息-- "name"."firstname" 对应HBASE中name列族下的firstname列-- SELECT 查询时,如果建表时,表名添加了双引号 那么查询时也需要添加 -- 视图仅仅是创建了一个映射关系,当Hbase中表发生变化时,对应视图数据也会发生变化createview"test"(
empid varcharprimarykey,"name"."firstname"varchar,"name"."lastname"varchar,"company"."name"varchar,"company"."address"varchar);-- 在phoenix查询数据,表名通过!!!双引号!!!引起来select*from"test";-- ERROR 505 (42000): Table is read only. -- 视图是一个只读的映射,无法对数据进行变更
upsert into"test"values('002','li','si','shujia','hefei');-- 删除视图不会影响Hbase中的数据dropview"test";-- students:通过HBase Java API写入的学生表-- 通过视图方式映射CREATEview"students"(
id VARCHARNOTNULLPRIMARYKEY,"info"."name"VARCHAR,"info"."age"BIGINT,"info"."gender"VARCHAR,"info"."clazz"VARCHAR);-- 在查询具体列时,如果建表时,对应字段名添加了双引号,那么查询时也需要添加双引号select"age"from"students";select"info"."age"from"students";
3.2、表映射
使用Apache Phoenix创建对HBase的表映射,有两类:
1) 当HBase中已经存在表时,可以以类似创建视图的方式创建关联表,只需要将create view改为create table即可。
2)当HBase中不存在表时,可以直接使用create table指令创建需要的表,并且在创建指令中可以根据需要对HBase表结构进行显示的说明。
第1)种情况下,如在之前的基础上已经存在了test表,则表映射的语句如下:
-- 需要先把上一个步骤的test视图先drop view-- Phoenix中创建的表,默认都在HBASE中的default命名空间下-- column_encoded_bytes=0表示不对HBase原有表的列名进行重新编码-- 如果表及数据是先在HBase中创建并加载好的,则必须加上该参数,否则无法映射上数据createtable"test"(
empid varcharprimarykey,"name"."firstname"varchar,"name"."lastname"varchar,"company"."name"varchar,"company"."address"varchar)column_encoded_bytes=0;-- 表映射是可以修改数据的
upsert into"test"values('1','2','3','4','5');
使用create table创建的关联表,如果对表进行了修改,源数据也会改变,同时如果关联表被删除,源表也会被删除。但是视图就不会,如果删除视图,源数据不会发生改变。
3、Phoenix二级索引
对于Hbase,如果想精确定位到某行记录,唯一比较快的办法就是通过rowkey查询。如果不通过rowkey查找数据,就必须逐行比较每一行的值,对于较大的表,全表扫描的代价是不可接受的。
1、开启索引支持
# 关闭hbase集群
stop-hbase.sh
# 在/usr/local/soft/hbase-2.2.7/conf/hbase-site.xml中增加如下配置
<property><name>hbase.regionserver.wal.codec</name><value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value></property><property><name>hbase.rpc.timeout</name><value>60000000</value></property><property><name>hbase.client.scanner.timeout.period</name><value>60000000</value></property><property><name>phoenix.query.timeoutMs</name><value>60000000</value></property>
# 同步到所有节点
scp hbase-site.xml node1:`pwd`
scp hbase-site.xml node2:`pwd`
# 修改phoenix目录下的bin目录中的hbase-site.xml
cd /usr/local/soft/phoenix-5.1.3/bin
vim hbase-site.xml
<property><name>hbase.rpc.timeout</name><value>60000000</value></property><property><name>hbase.client.scanner.timeout.period</name><value>60000000</value></property><property><name>phoenix.query.timeoutMs</name><value>60000000</value></property>
# 启动hbase
start-hbase.sh
# 重新进入phoenix客户端
sqlline.py master,node1,node2
2、创建索引
2.1、全局索引
(全局索引创建完成后,默认在hbase中是可以看到的,可以操作,如scan、count等,可以获取数据)
全局索引适合读多写少的场景。如果使用全局索引,读数据基本不损耗性能,所有的性能损耗都来源于写数据。
数据表的添加、删除和修改都会更新相关的索引表(数据删除了,索引表中的数据也会删除;数据增加了,索引表的数据也会增加)
注意: 对于全局索引在默认情况下,在查询语句中检索的列如果不在索引表中,Phoenix不会使用索引表,除非使用hint。
-- 创建DIANXIN.sql-- PRIMARY KEY (mdn,start_date) 以mdn和start_date作为联合索引,那么在HBASE中以这两个字段拼接作为其RowKey 并且ROWKey中以\x00作为其分隔符-- 需要将其保存成DIANXIN.sql文件CREATETABLEIFNOTEXISTS DIANXIN (
mdn VARCHAR,
start_date VARCHAR,
end_date VARCHAR,
county VARCHAR,
x DOUBLE,
y DOUBLE,
bsid VARCHAR,
grid_id VARCHAR,
biz_type VARCHAR,
event_type VARCHAR,
data_source VARCHAR,CONSTRAINT PK PRIMARYKEY(mdn,start_date)) column_encoded_bytes=0;-- 上传数据DIANXIN.csv-- psql.py 输入导入脚本 要求数据是csv格式或者文本格式,其中数据的分隔符默认为","-- 导入数据
psql.py master,node1,node2 DIANXIN.sql DIANXIN.csv
-- 创建全局索引-- INDEX是索引表的标记-- DIANXIN_INDEX索引名 -- ON 表示以哪张表创建索引-- 通过查询HBASE中DIANXIN_INDEX表可以看出 RowKey为end_date + DIANXIN表中的ROWKEYCREATEINDEX DIANXIN_INDEX ON DIANXIN (end_date);-- 查询数据 ( 索引未生效)-- 查询该表时,取的是所有字段,而DIANXIN其他字段并不存在于索引表中,所以Phoenix不知道取哪个索引表select*from DIANXIN where end_date ='20180503154014';select*from DIANXIN where start_date ='20180503211049';-- 15 rows selected (6.705 seconds)-- 强制使用索引 (索引生效) -- hint语法是在查询时指定注释信息,表示当前查询需要使用哪些信息 /*+*/select/*+ INDEX(DIANXIN DIANXIN_INDEX) */*from DIANXIN where end_date ='20180503191808';-- 14 rows selected (0.468 seconds)-- 查询条件中包含了了end_date,并且在索引表中有 end_date,所以查询时速度快,索引生效select/*+ INDEX(DIANXIN DIANXIN_INDEX) */*from DIANXIN where end_date ='20180503154014'and start_date ='20180503154614';-- 2 rows selected (0.115 seconds)-- 取索引列,(索引生效)-- count(*)计算时,是需要查看DIANXIN表中对应符合end_date = '20180503154014'要求的RowKey有多少个,而DIANXIN_INDEX索引表中对应RowKey包含有 end_date数据和 DIANXIN中的RowKey信息,所以可以直接使用索引表,而不需要再去源表中做查询selectcount(*)from DIANXIN where end_date ='20180503154014';-- 1 row selected (0.04 seconds)-- 取非索引列,但为RK列,索引生效select MDN from DIANXIN where end_date ='20180503154014';-- Phoenix也支持创建多列索引-- 创建多列索引就是在新建的索引表中,对应Rowkey会添加多个列CREATEINDEX DIANXIN_INDEX1 ON DIANXIN ( end_date,COUNTY );-- 注意:查询的时候,如果前面出现的某一列既不在rowkey中,也不在索引列中,默认是不会生效的。-- 多条件查询 (索引生效)select end_date,MDN,COUNTY from DIANXIN where end_date ='20180503154014'and COUNTY ='8340104';-- 索引未生效:因为添加的COUNTY字段是在索引表RowKey的中间,当HBASE查询数据时,不能直接按位置比较select end_date,MDN,COUNTY from DIANXIN where COUNTY ='8340104'AND START_DATE='20180503154014'-- 7 rows selected (1.939 seconds)-- ROWKEY => end_date,COUNTY,MDN,START_DATE-- ROWKEY按照字典序排序(需要从第一个位置进行依次比较) => 字典序排序不能从中间位置开始比较需要从第一个位置 -- where 条件过滤时 必须要给上字典序的第一个位置信息 -- 创建多列索引时,对索引列的顺序也有要求,经常查询的列放在第一位 -- 查询所有列 (索引未生效)-- * 是取DIANXIN表中所有列,不能单从索引表中获取,所以需要添加描述信息select*from DIANXIN where end_date ='20180503154014'and COUNTY ='8340104';-- 7 rows selected (2.496 seconds)-- 查询所有列 (索引生效)select/*+ INDEX(DIANXIN DIANXIN_INDEX1) */*from DIANXIN where end_date ='20180503154014'and COUNTY ='8340104';-- 7 rows selected (0.082 seconds)-- 单条件 (索引未生效)select end_date from DIANXIN where COUNTY ='8340103';-- 单条件 (索引生效) end_date 在前select COUNTY from DIANXIN where end_date ='20180503154014';-- 删除索引dropindex DIANXIN_INDEX on DIANXIN;
2.2、本地索引
本地索引适合写多读少的场景,或者存储空间有限的场景。
和全局索引一样,Phoenix也会在查询的时候自动选择是否使用本地索引。
本地索引因为索引数据和原数据存储在同一台机器上,避免网络数据传输的开销,所以更适合写多的场景。
由于无法提前确定数据在哪个Region上,所以在读数据的时候,需要检查每个Region上的数据从而带来一些性能损耗。
注意:对于本地索引,查询中无论是否指定hint或者是查询的列是否都在索引表中,都会使用索引表。
-- 创建本地索引-- 以网格ID 创建本地索引CREATELOCALINDEX DIANXIN_LOCAL_INDEX ON DIANXIN(grid_id);-- 索引生效select grid_id from dianxin where grid_id='117285031820040';--1,179 rows selected (0.179 seconds)-- 索引生效-- 因为本地索引建立时,索引的信息和源表中的数据都在一个节点中,那么查询数据时可以直接获取源表当前节点中的数据select*from dianxin where grid_id='117285031820040';--1,179 rows selected (0.418 seconds)
2.3、覆盖索引
覆盖索引是把原数据存储在索引数据表中,这样在查询时不需要再去HBase的原表获取数据就,直接返回查询结果。(相当于全局索引+数据)
注意:查询是 select 的列和 where 的列都需要在索引中出现。
-- 创建覆盖索引CREATEINDEX DIANXIN_INDEX_COVER ON DIANXIN ( x,y ) INCLUDE ( county );-- 创建完成后:0:X |0:Y | :MDN | :START_DATE |0:COUNTY
其中:MDN 和 START_DATE 为原先DIANXIN表中的ROWKEY,X Y是索引表中后添加的ROWKey信息
DIANXIN_INDEX_COVER 的ROWKEY信息 X,y,MDN,START_DATE,而county为索引表中的Value# 查询所有列 (索引未生效)select*from dianxin where x=117.288and y =31.822;select*from DIANXIN where x=117.288and y =31.822;# 强制使用索引 (索引生效)select/*+ INDEX(DIANXIN DIANXIN_INDEX_COVER) */*from dianxin where x=117.288and y =31.822;# 查询索引中的列 (索引生效) mdn是DIANXIN表的RowKey中的一部分select x,y,county from dianxin where x=117.288and y =31.822;select mdn,x,y,county from dianxin where x=117.288and y =31.822;-- 直接从索引表中取对应的字段数据SELECT"0:COUNTY"FROM DIANXIN_INDEX_COVER limit10;# 查询条件必须放在索引中 select 中的列可以放在INCLUDE (将数据保存在索引中)select/*+ INDEX(DIANXIN DIANXIN_INDEX_COVER) */ x,y,count(*)from dianxin groupby x,y;
使用场景:
如果HBASE中原先已经有对应表存在,表中也有对应数据,当有业务需求需要对表中的字段,进行过滤取值时,速度较慢,那么可以使用Phoenix创建对应的表,之后再去做索引表,通过索引表再去对数据进行快速查询
版权归原作者 小钱没有钱 所有, 如有侵权,请联系我们删除。