整合Phoenix

什么是Phoenix

现有hbase的查询工具有很多如:Hive,Tez,Impala,Shark/Spark,Phoenix等。今天主要说Phoenix。phoenix是一个在hbase上面实现的基于hadoop的OLTP技术,具有低延迟、事务性、可使用sql、提供jdbc接口的特点。 而且phoenix还提供了hbase二级索引的解决方案,丰富了hbase查询的多样性,继承了hbase海量数据快速随机查询的特点。但是在生产环境中,不可以用在OLTP中。在线事务处理的环境中,需要低延迟,而Phoenix在查询HBase时,虽然做了一些优化,但延迟还是不小。所以依然是用在OLAT中,再将结果返回存储下来。

Phoenix完全使用Java编写,作为HBase内嵌的JDBC驱动。Phoenix查询引擎会将SQL查询转换为一个或多个HBase扫描,并编排执行以生成标准的JDBC结果集。直接使用HBase API、协同处理器与自定义过滤器,对于简单查询来说,其性能量级是毫秒,对于百万级别的行数来说,其性能量级是秒。

简单的说hbase直接使用是比较麻烦的,可以像hadoop 可以使用hive简化hadoop的操作一样,对于hbase可以使用phoenix 简化hbase的相关api操作。
而且Phoenix 是可以通过标准的jdbc操作来操作hbase的数据。

Phoenix 官网 https://phoenix.apache.org/

安装

  1. 下载 连接 https://phoenix.apache.org/download.html

因为hbase使用的2.4版本,所以 下载这个文件

  1. 将文件上传到服务器,并且解压操作。
1
tar -zxvf phoenix-hbase-2.4.0-5.1.2-bin.tar.gz

得到这么几个文件

  1. 主要使用的是这3个jar 包,将 phoenix-server-hbase-2.4.0-5.1.2.jar 复制到 hbase 的lib 目录下,并且每个节点都需要复制。
1
2
cp  phoenix-server-hbase-2.4.0-5.1.2.jar   /usr/local/software/hbase/hbase-2.4.0/lib
xsync lib
  1. 重启hbase服务
1
2
./stop-hbase.sh
./start-hbase.sh
  1. 配置phoenix 环境变量

1
source /etc/profile
  1. 使用客户端连接

可以看到 phoenix 的bin 目录下又需要可指定的工具程序。

使用 sqlline.py 程序来连接hbase

1
sqlline.py hadoop1,hadoop2,hadoop3:2181

注意:连接信息里只有最后一个加端口号,前面2个不加。

phoenix Shell 基本操作

相关语法查询 https://phoenix.apache.org/language/index.html

  1. 展示所有的表,相当于 mysql 的show tables
1
!tables

TABLE_NAME 可以表示表的名称
TABLE_TYPE 表示是系统的表还是用户的表

  1. 创建一个表
    创建一个表和基础的sql语法基本相似,主键作为hbase的rowkey
1
2
3
4
5
CREATE TABLE IF NOT EXISTS user(
id VARCHAR primary key,
name VARCHAR,
age INTEGER,
gender INTEGER);

再查看表可以发现已经有了表的信息了。

同时通过hbase shell 观察下在hbase中创建的数据内容。

可以看到在hbase中创建了一个 USER 表。

  1. 写入和更新数据
1
upsert into user values('01','haha',10,1);

注意:和标准的sql不同的是,插入数据使用的是 upsert,不管是插入和更新都是使用此语句,如果根据主键已经存在那么则会覆盖。

1
upsert into user values('01','haha_update',11,0);

已经存在的执行更新

  1. 删除对应的数据

和基本sql语法一致

1
delete from user where id = '01'
  1. 删除对应的表
1
drop table user;
  1. 从命令行中退出
1
!quit

映射表

通过 phoenix 的sql 创建的表,内部已经维护好了映射关系了,但是针对在hbase中已经创建好的数据 是不能直接使用phoenix进行数据操作的,需要先执行数据的映射关系,将表的映射关联起来。

在hbase中创建数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

hbase:003:0> create 'data:employee',{NAME => 'info', VERSIONS => 1}
Created table data:employee
Took 0.6626 seconds
=> Hbase::Table - data:employee
hbase:004:0> put 'data:employee','01','info:user_name','user_01'
Took 0.0566 seconds
hbase:005:0> put 'data:employee','01','info:user_password','111111'
Took 0.0093 seconds
hbase:006:0> put 'data:employee','01','info:user_group','01_group'
Took 0.0195 seconds
hbase:007:0> put 'data:employee','02','info:user_group','02_group'
Took 0.0130 seconds
hbase:008:0> put 'data:employee','02','info:user_password','111111'
Took 0.0103 seconds
hbase:009:0> put 'data:employee','02','info:user_name','user_02'
Took 0.0080 seconds

通过视图映射

phoenix 视图的方式,视图可用来查询数据但是不能直接操作数据。

需要注意的是,默认情况下phoenix无法操作非 hbase中 default namesapce 的数据,如果需要将hbase的namespace 的概念和phoenix sechema 对应起来,需要开放一个配置。

在hbase 和 phoenix 的 2个 hbase-site.xml 都需要加上配置

配置文件分别在 hbase_home/conf 和 phoenix_home/bin 目录下

1
2
3
4
5
6
7
8
9
10

<property>
<name>phoenix.schema.isNamespaceMappingEnabled</name>
<value>true</value>
</property>
<property>
<name>phoenix.schema.mapSystemTablesToNamespace</name>
<value>true</value>
</property>

1
2
3
CREATE SCHEMA IF NOT EXISTS data;

create view "data"."employee"(id varchar primary key,"info"."user_name" varchar,"info"."user_password" varchar,"info"."user_group" varchar);

查询数据

1
select * from "data"."employee";

如果这个视图不需要了,可以使用drop 来删除这个视图。

1

通过表映射

通过表映射是可以对数据进行修改的。

建立数据

经过测试通过表映射 如果带有SCHAMA ,那么 必须是 大写的,所以hbase的 namespace 也需要是大写的才能对应上。创建视图的时候是可以的,创建表的时候不行,所以 HBASE的namsespace 和
phoenix 的 schema 最好都创建成大写的,避免出现问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
hbase:062:0> create 'TEST:employee',{NAME => 'info', VERSIONS => 1}
Created table TEST:employee
Took 0.6231 seconds
=> Hbase::Table - TEST:employee
hbase:063:0> put 'TEST:employee','01','info:user_name','user_01'
Took 0.0185 seconds
hbase:064:0> put 'TEST:employee','01','info:user_password','111111'
Took 0.0074 seconds
hbase:065:0> put 'TEST:employee','01','info:user_group','01_group'
Took 0.0049 seconds
hbase:066:0> put 'TEST:employee','02','info:user_group','02_group'
Took 0.0037 seconds
hbase:067:0> put 'TEST:employee','02','info:user_password','111111'
Took 0.0034 seconds
hbase:068:0> put 'TEST:employee','02','info:user_name','user_02'
Took 0.0063 seconds

1
2
CREATE SCHEMA IF NOT EXISTS TEST;
create table "TEST"."employee"("id" varchar primary key,"info"."user_name" varchar,"info"."user_password" varchar,"info"."user_group" varchar) column_encoded_bytes=0;

这里有一个很重要的参数 column_encoded_bytes。如果不开启此参数,那么在hbase生成的列将是一个被处理了的比较短的列明,当设置为0 的时候,表示不开启列名的编码,也就能保持两边的列名一致。

映射成功。

jdbc操作

和普通的jdbc操作基本一致。

  1. 引入对应的依赖包
1
2
3
4
5
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-client-hbase-2.4</artifactId>
<version>5.1.2</version>
</dependency>
  1. 编写jdbc基础代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

String url = "jdbc:phoenix:hadoop1,hadoop2,hadoop3:2181";
Properties properties = new Properties();
properties.put("phoenix.schema.isNamespaceMappingEnabled","true");
Connection connection = DriverManager.getConnection(url, properties);
PreparedStatement preparedStatement = connection.prepareStatement(" select * from \"TEST\".\"employee\"");
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println(resultSet);
while (resultSet.next()){
System.out.println("================>");
for (int i = 1; i < 4; i++) {
String str = resultSet.getString(i);
System.out.println(str);
}
}
connection.close();


指定的打印结果

Phoenix 二级索引

默认情况下,hbase的数据只能通过 row key 做相关的查询和操作,而如果要能够对其他的字段做高效的查询,需要开启二级索引。

  1. 修改hbase的配置,为hbase开启二级索引的相关配置。
1
2
3
4
5
6

<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>

同步多个节点机器,并重启服务。

全局索引

直接根据一个字段不使用索引查询,显示走全表扫描。

通过 phoenix 创建一个索引。

1
CREATE INDEX index_1 ON  "TEST"."employee" ("info"."user_name");

当只查询特定的字段的时候,会有范围扫描。

创建的全局索引会在hbase上创建对应的一个外部索引表。这里创建了一个 TEST:INDEX_1 的表,表的row key 为要创建索引的字段信息。

可以看到内部用索引列的信息来创建索引表的rowkey。

索引的删除

1
drop index index_1 on "TEST"."employee";

全局索引适合读多,写少的场景,因为在操作数据的时候需要同步修改索引数据。

包含索引

包含索引是携带其他字段的索引。全局索引只能有一个字段,包含可以有其他的字段,适合在多条件查询的时候使用。

1
explain select "info"."user_name","info"."user_password"  from "TEST"."employee" where "info"."user_name" = 'user_01' and "info"."user_password" = '111111';

创建索引

1
CREATE INDEX index_2 ON  "TEST"."employee" ("info"."user_name") INCLUDE ("info"."user_password");
1
explain select "info"."user_name","info"."user_password"  from "TEST"."employee" where "info"."user_name" = 'user_01' and "info"."user_password" = '111111';

再次查询

这种有2个字段查询的,如果只用一个字段的全局索引是无法走索引的。

删除此索引,再创建一个单值索引,下图,无法走索引.

本地索引

全局索引的索引表和数据表是分开的,有可能存在不同的region。而本地索引的索引数据是存在于数据表中的一个列上的,所以相对来说是本地的索引。优点是在写操作的时候能够降低写入的开销,不过缺点是没有全局索引的速度快。

创建一个本地索引

1
CREATE LOCAL INDEX index_3 ON    "TEST"."employee"("info"."user_name","info"."user_password");
1
explain select "info"."user_name","info"."user_password"  from "TEST"."employee" where "info"."user_name" = 'user_01' and "info"."user_password" = '111111';

查看生效

错误: 在创建的时候出现如下错误

org.apache.hadoop.hbase.DoNotRetryIOException: org.apache.hadoop.hbase.DoNotRetryIOException: ERROR 102 (08001): ERROR 102 (08001): Malformed connection url. :hadoop1:2181,hadoop2:2181,hadoop3:2181:2181:/hbase; TEST.INDEX_3
at org.apache.phoenix.util.ServerUtil.createIOException(ServerUtil.java:106)
at org.apache.phoenix.coprocessor.MetaDataEndpointImpl.createTable(MetaDataEndpointImpl.java:2178)
at org.apache.phoenix.coprocessor.generated.MetaDataProtos$MetaDataService.callMethod(MetaDataProtos.java:17317)
at org.apache.hadoop.hbase.regionserver.HRegion.execService(HRegion.java:8773)

需要修改hbase的配置文件,将前面2个的端口号去掉。

修改hbase配置文件 hbase-site.xml

1
2
3
4
5
6
7
  <property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop1:2181,hadoop2:2181,hadoop3:2181</value>
<description>The directory shared by RegionServers.
</description>
</property>

修改为

1
2
3
4
5
6
7
  <property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop1,hadoop2,hadoop3:2181</value>
<description>The directory shared by RegionServers.
</description>
</property>

hbase服务重启,然后重新连接建立索引即可。