整合Phoenix
定义
Phoenix 官网地址 https://phoenix.apache.org/
Phoenix作为一款OLTP和Apache Hadoop的操作分析,是面向HBase的开源 SQL 皮肤,其通过 JDBC API 代替繁重的 HBase 客户端 API来创建表,插入数据和查询 HBase 数据。目前最新版本为5.1.2
Apache Phoenix通过结合两者的优点,在Hadoop中为低延迟应用提供OLTP和操作分析,标准SQL和JDBC api的强大功能与完整的ACID事务功能,以及通过利用HBase作为其后备存储,采用NoSQL使得读写数据更加灵活性。Apache Phoenix与其他Hadoop产品(如Spark、Hive、Pig、Flume和Map Reduce)完全集成。
为何要使用
官方原文:Doesn’t putting an extra layer between my application and HBase just slow things down? Actually, no. Phoenix achieves as good or likely better performance than if you hand-coded it yourself (not to mention with a heck of a lot less code) by:
翻译为在 Client 和 HBase 之间放一个 Phoenix 中间层不会减慢速度,因为用户编写的数据处理代码和 Phoenix 编写的没有区别(更不用说你写的垃圾的多),不仅如此Phoenix 对于用户输入的 SQL 同样会有大量的优化手段(就像 hive 自带 sql 优化器一样)。
Phoenix 在 5.0 版本默认提供有两种客户端使用(瘦客户端和胖客户端),在 5.1.2 版本安装包中删除了瘦客户端,本文也不再使用瘦客户端。而胖客户端和用户自己写 HBase 的API 代码读取数据之后进行数据处理是完全一样的。
安装
wget --no-check-certificate https://dlcdn.apache.org/phoenix/phoenix-5.1.2/phoenix-hbase-2.4.0-5.1.2-bin.tar.gz
tar -xvf phoenix-hbase-2.4.0-5.1.2-bin.tar.gz
cp phoenix-hbase-2.4.0-5.1.2-bin/phoenix-server-hbase-2.4.0-5.1.2.jar hbase-2.5.0/lib/
scp hbase-2.5.0/lib/phoenix-server-hbase-2.4.0-5.1.2.jar hadoop2:/home/commons/hbase-2.5.0/lib/
scp hbase-2.5.0/lib/phoenix-server-hbase-2.4.0-5.1.2.jar hadoop3:/home/commons/hbase-2.5.0/lib/
export PHOENIX_HOME=/home/commons/phoenix-hbase-2.4.0-5.1.2-bin
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
source /etc/profile
bin/stop-hbase.sh
bin/start-hbase.sh
cd phoenix-hbase-2.4.0-5.1.2-bin
bin/sqlline.py zk1
!table
!tables
!quit
SHELL操作
CREATE TABLE IF NOT EXISTS student(
id VARCHAR primary key,
name VARCHAR,
age BIGINT,
addr VARCHAR);
CREATE TABLE IF NOT EXISTS student1 (
id VARCHAR NOT NULL,
name VARCHAR NOT NULL,
age BIGINT,
addr VARCHAR
CONSTRAINT my_pk PRIMARY KEY (id, name));
注:Phoenix 中建表,会在 HBase 中创建一张对应的表。为了减少数据对磁盘空间的占用,Phoenix 默认会对 HBase 中的列名做编码处理。具体规则可参考官网链接:https://phoenix.apache.org/columnencoding.html,若不想对列名编码,可在建表语句末尾加上 COLUMN_ENCODED_BYTES = 0;
upsert into student values('1001','hefangzhou', 10, 'shanghai');
select * from student;
select * from student where id='1001';
delete from student where id='1001';
drop table student;
表的映射
表的关系在默认情况下, HBase 中已存在的表,通过 Phoenix 是不可见的。如果要在 Phoenix 中操作 HBase 中已存在的表,可以在 Phoenix 中进行表的映射。映射方式有两种:视图映射和表映射。
- 视图映射:Phoenix 创建的视图是只读的,所以只能用来做查询,无法通过视图对数据进行修改等操作。在 phoenix 中创建关联 table1 表的视图
create 'table1' ,'info1','info2'
put 'table1','100001','info1:name','libaishan'
put 'table1','100001','info2:address','nanjing'
create view "table1"(
id varchar primary key,
"info1"."name" varchar,
"info2"."address" varchar);
select * from "table1";
drop view "table1";
- 视图映射:在 Pheonix 创建表去映射 HBase 中已经存在的表,是可以修改删除 HBase 中已经存在的数据的。而且,删除 Phoenix 中的表,那么 HBase 中被映射的表也会被删除。注:进行表映射时,不能使用列名编码,需将 column_encoded_bytes 设为 0。
create table "table2"(
id varchar primary key,
"info1"."name" varchar,
"info2"."address" varchar)
column_encoded_bytes=0;
- 数字类型说明:HBase 中的数字,底层存储为补码,而 Phoenix 中的数字,底层存储为在补码的基础上,将符号位反转。故当在 Phoenix 中建表去映射 HBase 中已存在的表,当 HBase 中有数字类型的字段时,会出现解析错误的现象。
create 'test_number','info'
put 'test_number','1001','info:number',Bytes.toBytes(1000)
scan 'test_number',{COLUMNS => 'info:number:toLong'}
create view "test_number"(id varchar primary key,"info"."number" bigint);
select * from "test_number";
解决上述问题的方案有以下两种:
- Phoenix 种提供了 unsigned_int,unsigned_long 等无符号类型,其对数字的编码解码方式和 HBase 是相同的,如果无需考虑负数,那在 Phoenix 中建表时采用无符号类型是最合适的选择。
- phoenix 演示:
drop view "test_number";
create view "test_number"(id varchar primary key,"info"."number" unsigned_long);
select * from "test_number";
- 如需考虑负数的情况,则可通过 Phoenix 自定义函数,将数字类型的最高位,即符号位反转即可,自定义函数可参考如下接:https://phoenix.apache.org/udf.html。
简易JDBC示例
这里演示一个标准的 JDBC 连接操作,实际开发中会直接使用别的框架内嵌的Phoenix 连接,添加maven 依赖
<dependency> <groupId>org.apache.phoenix</groupId> <artifactId>phoenix-client-hbase-2.4</artifactId> <version>5.1.2</version> </dependency>
重新执行phoenix shell操作关于student表创建和插入数据,测试代码PhoenixDemo.java
package cn.itxs.phoenixdemo;
import java.sql.*;
import java.util.Properties;
public class PhoenixDemo {
public static void main(String[] args) throws SQLException {
String url = "jdbc:phoenix:zk1,zk2,zk3:2181";
Properties properties = new Properties();
Connection connection = DriverManager.getConnection(url, properties);
String sql = "select * from student ";
String s = sql.toLowerCase();
PreparedStatement preparedStatement = connection.prepareStatement(s);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println(resultSet.getString(1) + ":" +
resultSet.getString(2) + ":" + resultSet.getString(3) +
":" + resultSet.getString(4));
}
connection.close();
System.out.println("hello");
}
}
运行程序,打印出前面phoenix shell插入的数据
二级索引
二级索引配置文件
添加如下配置到 HBase 的 HRegionserver 节点的 hbase-site.xml,之后重启Hbase集群
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
全局索引
Global Index 是默认的索引格式,创建全局索引时,会在 HBase 中建立一张新表。也就是说索引数据和数据表是存放在不同的表中的,因此全局索引适用于多读少写的业务场景。
写数据的时候会消耗大量开销,因为索引表也要更新,而索引表是分布在不同的数据节点上的,跨节点的数据传输带来了较大的性能消耗。在读数据的时候 Phoenix 会选择索引表来降低查询消耗的时间。
创建单个字段的全局索引。
CREATE INDEX my_index ON my_table (my_col);
create index my_index on student(age);
explain select id,age from student where age = 10;
explain select id,name from student where age = 10;
DROP INDEX my_index ON my_table
drop index my_index on student;
若想解决上述问题,可采用如下方案:
包含索引
包含索引(covered index)创建携带其他字段的全局索引(本质还是全局索引)
CREATE INDEX my_index ON my_table (v1) INCLUDE (v2);
drop index my_index on student1;
create index my_index on student(age) include (name);
explain select id,name from student where age = 10;
本地索引(local index)
本地索引(local index) 适用于写操作频繁的场景。索引数据和数据表的数据是存放在同一张表中(且是同一个 Region),避免了在写操作的时候往不同服务器的索引表中写索引带来的额外开销。
CREATE LOCAL INDEX my_index ON my_table (my_column);
drop index my_index on student;
CREATE LOCAL INDEX my_index ON student (age,name);
explain select id,name from student where age = 10;
HBase与 Hive 的集成
使用场景
如果大量的数据已经存放在 HBase 上面,需要对已经存在的数据进行数据分析处理,那么 Phoenix 并不适合做特别复杂的 SQL 处理,此时可以使用 hive 映射 HBase 的表格,之后写 HQL 进行分析处理。
集成方法
在 hive-site.xml 中添加 zookeeper 的属性,如下:
<property>
<name>hive.zookeeper.quorum</name>
<value>zk1,zk2,zk3</value>
</property>
<property>
<name>hive.zookeeper.client.port</name>
<value>2181</value>
</property>
示例
接下来先建立 Hive 表,关联 HBase 表,插入数据到 Hive 表的同时能够影响 HBase 表
CREATE TABLE hive_emp(
empno INT,
empname string,
deptno INT
) STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' WITH SERDEPROPERTIES (
"hbase.columns.mapping" = ":key,info:empname,info:deptno"
) TBLPROPERTIES ("hbase.table.name" = "hbase_emp");
完成之后,可以分别进入 Hive 和 HBase 查看,都生成了对应的表。
- 在 Hive 中创建临时中间表,用于 load 文件中的数据。(不能将数据直接 load 进 Hive 所关联 HBase 的那张表中)
CREATE TABLE emp_mid( empno int, ename string, deptno int)row format delimited fields terminated by ' ';
1001 zhangsan 1001002 lisi 1001003 hangong 1001004 xieren 1011005 lili 1011006 guojia 102
- 往Hive 中间表emp_mid中 load 数据
load data local inpath '/home/commons/data/emp.txt' into table emp_mid;
- 通过 insert 命令将中间表中的数据导入到 Hive 关联 Hbase 的那张表中。
insert into table hive_emp select * from emp_mid;
- 查看 Hive 以及关联的 HBase 表中是否已经成功的同步插入了数据。
整合已有HBase表示例
上个示例中在 HBase 中已存储了表 hbase_emp,接着我们在 Hive 中创建一个外部表hive_emp_external来关联 HBase 中的 hbase_emp 这张表,使之可以借助 Hive 来分析 HBase 这张表中的数据。
- 在 Hive 中创建外部表hive_emp_external
CREATE EXTERNAL TABLE hive_emp_external( empno INT, empname string, deptno INT) STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler' WITH SERDEPROPERTIES ( "hbase.columns.mapping" = ":key,info:empname,info:deptno") TBLPROPERTIES ("hbase.table.name" = "hbase_emp");
- 关联后就可以使用 Hive 函数进行一些分析操作了
select deptno,count(empname) from hive_emp_external group by deptno;
**本人博客网站 **IT小神 www.itxiaoshen.com
|