Phoenix搭建及使用

Phoenix

Hbase适合存储大量的对关系运算要求低的NOSQL数据,受Hbase 设计上的限制不能直接使用原生的API执行在关系数据库中普遍使用的条件判断和聚合等操作。Hbase很优秀,一些团队寻求在Hbase之上提供一种更面向普通开发人员的操作方式,Apache Phoenix即是。

Phoenix 基于Hbase给面向业务的开发人员提供了以标准SQL的方式对Hbase进行查询操作,并支持标准SQL中大部分特性:条件运算,分组,分页,等高级查询语法。

1、Phoenix 搭建

Phoenix 5.1.3 HBase 2.1.9 hadoop 3.1.3

1、关闭hbase集群,在master中执行

stop-hbase.sh

2、上传解压配置环境变量

解压

tar -zxvf phoenix-hbase-2.1-5.1.3-bin.tar.gz

改名

mv phoenix-hbase-2.1-5.1.3-bin phoenix-5.1.3

cp phoenix-server-hbase-2.1-5.1.3.jar $HBASE_HOME/lib/

3、将phoenix-server-hbase-2.1-5.1.3.jar复制到所有节点的hbase lib目录下

cd $HBASE_HOME/lib

scp phoenix-server-hbase-2.1-5.1.3.jar node1:`pwd`
scp phoenix-server-hbase-2.1-5.1.3.jar node2:`pwd`

4、启动hbase , 在master中执行

stop-hbase.sh
start-hbase.sh

5、配置环境变量

vim /etc/profile


export PHONENIX_HOME=/usr/local/soft/phoenix-5.1.3
export PATH=$PHONENIX_HOME/bin:$PATH

source /etc/profile

2、Phoenix使用

1、连接sqlline

sqlline.py master,node1,node2

# 出现
163/163 (100%) Done
Done
sqlline version 1.5.0
0: jdbc:phoenix:master,node1,node2> 


2、常用命令

# 1、创建表

CREATE TABLE IF NOT EXISTS STUDENT (
 id VARCHAR NOT NULL PRIMARY KEY, 
 name VARCHAR,
 age BIGINT, 
 gender VARCHAR ,
 clazz VARCHAR
);
-- PRIMARY KEY 表示主键 有序并且全局唯一的
-- VARCHAR 由于HBASE中存储数据没有长度限制,所以不需要加上长度
-- PHOENIX中创建的表都会在HBASE中映射有一张表

CREATE TABLE IF NOT EXISTS student (
 id VARCHAR NOT NULL PRIMARY KEY, 
 name VARCHAR,
 age BIGINT, 
 gender VARCHAR ,
 clazz VARCHAR
);

CREATE TABLE IF NOT EXISTS api.student1 (
 id VARCHAR NOT NULL PRIMARY KEY, 
 name VARCHAR,
 age BIGINT, 
 gender VARCHAR ,
 clazz VARCHAR
);





# 2、显示所有表
 !table

# 3、插入数据
upsert into STUDENT values('1500100004','葛德曜',24,'男','理科三班');
upsert into STUDENT values('1500100005','宣谷芹',24,'男','理科六班');
upsert into STUDENT values('1500100006','羿彦昌',24,'女','理科三班');
upsert into STUDENT values('1500100007','羿XX',23,'女','理科三班');

-- 使用upsert添加数据时,如果ID相同,那么数据会替换更新 由于Hbase是根据RowKey添加数据的多个版本
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 group by gender;
select * from student order by gender;

# 5、删除数据
delete from STUDENT where id='1500100007';


# 6、删除表
drop table STUDENT;
 
 
# 7、退出命令行
!quit

更多语法参照官网
https://phoenix.apache.org/language/index.html#upsert_select

CREATE TABLE gender_cnt(
gender varchar NOT NULL PRIMARY KEY,
cnt BIGINT 
)

upsert into gender_cnt SELECT gender,count(*) from student group by gender

-- 查看SQL的执行计划
EXPLAIN  SELECT count(*) FROM STUDENT WHERE  clazz LIKE '%三班%';

EXPLAIN  SELECT * FROM STUDENT WHERE  id = '1500100004';
-- 对于ROWKey进行过滤时,不需要使用FULL Scan 

3、phoenix表映射

默认情况下,直接在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创建视图, primary key 对应到hbase中的rowkey

create view "test"(
empid varchar primary key,
"name"."firstname" varchar,
"name"."lastname"  varchar,
"company"."name"  varchar,
"company"."address" varchar
)column_encoded_bytes=0;
-- 注意表名添加双引号的目的是为了防止大小写转换,如果不加,那么表示当前HBASE中的表名称为大写 最好添加
-- primary key -》 HBASE中的RowKey信息
-- "name"."firstname" 表示应用HBASE中列族下的列
-- SELECT 查询时,如果建表时,表名添加了双引号 那么查询时也需要添加
-- 注意要求Hbase中的表是在default命名空间下 
-- 视图仅仅是创建了一个映射关系,当Hbase中表发生变化时,对应视图数据也会发生变化  
upsert into "test" values('002','li','si','shujia','hefei');
-- ERROR 505 (42000): Table is read only. 视图是一个只读的映射 



create 'student','info','info2'
put 'student','10001','info:name','zhangsan1'
put 'student','10001','info:age','18'
put 'student','10001','info2:gender','男'
put 'student','10001','info2:clazz','文科一班'


CREATE view "student" (
 id VARCHAR NOT NULL PRIMARY KEY, 
 "info"."name" VARCHAR,
 "info"."age" VARCHAR, 
 "info"."gender" VARCHAR,
 "info"."clazz" VARCHAR
) column_encoded_bytes=0;

# 在phoenix查询数据,表名通过双引号引起来
select * from "test";

-- 在查询具体列时,如果建表时,对应字段名添加了双引号,那么查询时也需要添加双引号
select "age" from "student";
select "info"."age" from "student";

# 删除视图
drop view "test";
-- 注意:删除视图不会影响Hbase中的数据

upsert into "student" values('1500100004','葛德曜',24,'男','理科三班');
Error: ERROR 505 (42000): Table is read only. (state=42000,code=505)
org.apache.phoenix.schema.ReadOnlyTableException: ERROR 505 (42000): Table is read only.

-- 对于Phoenix中创建视图,映射到命名空间时,无法添加映射信息
CREATE view "api.stu" (
 id VARCHAR NOT NULL PRIMARY KEY, 
 "info"."name" VARCHAR,
 "info"."age" VARCHAR, 
 "info"."gender" VARCHAR,
 "info"."clazz" VARCHAR
) column_encoded_bytes=0;

3.2、表映射

使用Apache Phoenix创建对HBase的表映射,有两类:

1) 当HBase中已经存在表时,可以以类似创建视图的方式创建关联表,只需要将create view改为create table即可。

2)当HBase中不存在表时,可以直接使用create table指令创建需要的表,并且在创建指令中可以根据需要对HBase表结构进行显示的说明。

第1)种情况下,如在之前的基础上已经存在了test表,则表映射的语句如下:


create table "test" (
empid varchar primary key,
"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');
-- 表映射是可以修改数据的



-- 创建表时 可以不加双引号 但是对应Hbase中的表名为大写
CREATE table ns1.student (
 id VARCHAR NOT NULL PRIMARY KEY, 
 "info"."name" VARCHAR,
 "info"."age" VARCHAR, 
 "info"."gender" VARCHAR ,
 "info"."clazz" VARCHAR
) column_encoded_bytes=0;

-- Phoenix中创建的表,都是指定在HBASE中的default命名空间下 

upsert into ns1.student values('1500110004','葛德曜','24','n ü','理科三班');
-- Phoenix 查询列或者表时,最好添加双引号进行查询

使用create table创建的关联表,如果对表进行了修改,源数据也会改变,同时如果关联表被删除,源表也会被删除。但是视图就不会,如果删除视图,源数据不会发生改变。

3、Phoenix二级索引

对于Hbase,如果想精确定位到某行记录,唯一比较快的办法就是通过rowkey查询。如果不通过rowkey查找数据,就必须逐行比较每一行的值,对于较大的表,全表扫描的代价是不可接受的。

1、开启索引支持

# 关闭hbase集群
stop-hbase.sh

# 在/usr/local/soft/hbase-2.1.9/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
<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
CREATE TABLE IF NOT EXISTS 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 PRIMARY KEY (mdn,start_date)
) column_encoded_bytes=0;
-- PRIMARY KEY (mdn,start_date) 以mdn和start_date作为联合索引,那么在HBASE中以这两个字段拼接作为其RowKey 并且ROWKey中以\x00作为其分隔符
-- 需要将其保存成DIANXIN.sql文件 


# 上传数据DIANXIN.csv

-- psql.py 输入导入脚本 要求数据是csv格式或者文本格式,其中数据的分隔符默认为,
# 导入数据
psql.py master,node1,node2 DIANXIN.sql DIANXIN.csv

 20180502234851\x00A50A8ED7937CFA5E63893411AAE73204228A33BD\x0020180503000051 
 20180502235040\x00471E838D9A14 column=0:_0, timestamp=1657529124623, value=\x01
 47F6A790AF8565326A4E099E37A7\x0020180503000240
 20180502235042\x0097D98FB335F2 column=0:_0, timestamp=1657529134748, value=\x01
 837C685515112D3AE34AA2CA03C9\x
 0020180503000142
 20180502235101\x007381DB4E848A column=0:_0, timestamp=1657529172331, value=\x01
 224C2E7AB6B29E0CBE8AD46663BC\x
 0020180503000001
 20180502235159\x00B1057A2046DA column=0:_0, timestamp=1657529156818, value=\x01
 5F4A06D1232853B8ACCBA3BB33C5\x
 0020180503000259



# 创建全局索引
CREATE INDEX DIANXIN_INDEX ON DIANXIN ( end_date );
-- INDEX是索引表的标记
-- DIANXIN_INDEX索引名 
-- ON 表示以哪张表创建索引

通过查询HBASE中DIANXIN_INDEX表可以看出 RowKey为 end_date + DIANXIN表中的ROWKEY,并且其Value值为DIANXIN表中的ROWKEY

# 查询数据 ( 索引未生效)
select * from DIANXIN where end_date = '20180503154014';
-- 15 rows selected (6.705 seconds)



-- 查询该表时,取的是所有字段,而DIANXIN其他字段并不存在于索引表中,所以Phoenix不知道取哪些索引表

# 强制使用索引 (索引生效) hint  -- /**/
select /*+ INDEX(DIANXIN DIANXIN_INDEX) */  * from DIANXIN where end_date = '20180503191808';
14 rows selected (0.468 seconds)
-- hint语法是在查询时指定注释信息,表示当前查询需要使用哪些信息
-- + INDEX() 表示查询时添加索引信息
-- INDEX(查询表 索引表)



select /*+ INDEX(DIANXIN DIANXIN_INDEX) */  * from DIANXIN where end_date = '20180503154014'  and start_date = '20180503154614';
2 rows selected (0.115 seconds)
-- 在索引表中有 end_date和start_date的信息,所以查询时速度快,索引生效


# 取索引列,(索引生效)
select count(*) from DIANXIN where end_date = '20180503154014';
1 row selected (0.04 seconds)
-- count(*)计算时,是需要查看DIANXIN表中对应符合end_date = '20180503154014'要求的RowKey有多少个,而DIANXIN_INDEX索引表中对应RowKey包含有 end_date数据和 DIANXIN中的RowKey信息,所以可以直接使用索引表,而不需要再去源表中做查询

select MDN from DIANXIN where end_date = '20180503154014';


# 创建多列索引
CREATE INDEX DIANXIN_INDEX1 ON DIANXIN ( end_date,COUNTY );
-- 创建多列索引就是在新建的索引表中,对应Row会添加多个列


-- 注意:查询的时候,如果前面出现的某一列既不在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 条件过滤时 必须要给上字典序的第一个位置信息 => 创建多列索引时,对索引列的顺序也有要求,经常查询的列放在第一位 



# 查询所有列 (索引未生效)
select * from DIANXIN where end_date = '20180503154014'  and COUNTY = '8340104';
7 rows selected (2.496 seconds)
* 是取DIANXIN表中所有列,不能单从索引表中获取,所以需要添加描述信息 


# 查询所有列 (索引生效)
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';

# 删除索引
drop index DIANXIN_INDEX on DIANXIN;
2.2、本地索引

本地索引适合写多读少的场景,或者存储空间有限的场景。和全局索引一样,Phoenix也会在查询的时候自动选择是否使用本地索引。本地索引因为索引数据和原数据存储在同一台机器上,避免网络数据传输的开销,所以更适合写多的场景。由于无法提前确定数据在哪个Region上,所以在读数据的时候,需要检查每个Region上的数据从而带来一些性能损耗。

注意:对于本地索引,查询中无论是否指定hint或者是查询的列是否都在索引表中,都会使用索引表。

# 创建本地索引
CREATE LOCAL INDEX DIANXIN_LOCAL_IDEX ON DIANXIN(grid_id);
-- 以网格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 的列都需要在索引中出现。

# 创建覆盖索引
CREATE INDEX 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.288 and y =31.822;
select * from DIANXIN where x=117.288 and y =31.822;

# 强制使用索引 (索引生效)
select /*+ INDEX(DIANXIN DIANXIN_INDEX_COVER) */ * from dianxin where x=117.288 and y =31.822;

# 查询索引中的列 (索引生效) mdn是DIANXIN表的RowKey中的一部分
select x,y,county from dianxin where x=117.288 and y =31.822;
select mdn,x,y,county from dianxin where x=117.288 and y =31.822;


-- 直接从索引表中取对应的字段数据
SELECT "0:COUNTY" FROM DIANXIN_INDEX_COVER limit 10;


# 查询条件必须放在索引中  select 中的列可以放在INCLUDE (将数据保存在索引中)
select /*+ INDEX(DIANXIN DIANXIN_INDEX_COVER) */ x,y,count(*) from dianxin group by x,y;

使用场景:

​ 如果HBASE中原先已经有对应表存在,表中也有对应数据,当有业务需求需要对表中的字段,进行过滤取值时,速度较慢,那么可以使用Phoenix创建对应的表,之后再去做索引表,通过索引表再去对数据进行快速查询

4、Phoenix JDBC


# 导入依赖

<!-- https://mvnrepository.com/artifact/org.apache.phoenix/phoenix-core -->
        <dependency>
            <groupId>org.apache.phoenix</groupId>
            <artifactId>phoenix-core</artifactId>
            <version>5.1.3</version>
        </dependency>

<!-- https://mvnrepository.com/artifact/org.apache.phoenix/phoenix-hbase-compat-2.3.0 -->
        <dependency>
            <groupId>org.apache.phoenix</groupId>
            <artifactId>phoenix-hbase-compat-2.3.0</artifactId>
            <version>5.1.3</version>
        </dependency>


<dependency>
     <groupId>com.lmax</groupId>
     <artifactId>disruptor</artifactId>
     <version>3.3.11</version>
</dependency>


 // 无需注册驱动 只需要导入jar包 
Connection conn = DriverManager.getConnection("jdbc:phoenix:master,node1,node2:2181");
        PreparedStatement ps = conn.prepareStatement("select /*+ INDEX(DIANXIN DIANXIN_INDEX) */ * from DIANXIN where end_date=?");
        ps.setString(1, "20180503212649");
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            String mdn = rs.getString("mdn");
            String start_date = rs.getString("start_date");
            String end_date = rs.getString("end_date");
            String x = rs.getString("x");
            String y = rs.getString("y");
            String county = rs.getString("county");
            System.out.println(mdn + "\t" + start_date + "\t" + end_date + "\t" + x + "\t" + y + "\t" + county);
        }
        ps.close();
        conn.close();


  • 25
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值