hbase学习之整合Phoenix与hive
Phoenix
Phoenix简介
定义
Phoenix是HBase的开源SQL皮肤。可以使用标准JDBC API代替HBase客户端API来创建表,插入数据和查询HBase数据。
特点
1)容易集成:如Spark,Hive,Pig,Flume和Map Reduce;
2)操作简单:DML命令以及通过DDL命令创建和操作表和版本化增量更改;
3)支持HBase二级索引创建。
架构
Phoenix快速入门
部署安装
#1.官网地址
http://phoenix.apache.org/
#2.Phoenix部署,上传并解压tar包
[atguigu@hadoop102 module]$ tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /opt/module/
[atguigu@hadoop102 module]$ mv apache-phoenix-5.0.0-HBase-2.0-bin phoenix
#3.复制server包并拷贝到各个节点的hbase/lib
[atguigu@hadoop102 module]$ cd /opt/module/phoenix/
[atguigu@hadoop102 phoenix]$ cp /opt/module/phoenix/phoenix-5.0.0-HBase-2.0-server.jar /opt/module/hbase/lib/
[atguigu@hadoop102 phoenix]$ xsync /opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-server.jar
#4.配置环境变量
#phoenix
export PHOENIX_HOME=/opt/module/phoenix
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
#5.重启HBase
[atguigu@hadoop102 ~]$ stop-hbase.sh
[atguigu@hadoop102 ~]$ start-hbase.sh
#6.连接Phoenix(使用重量级的连接),后面如果不加zookeeper地址会默认使用本机的
[atguigu@hadoop101 phoenix]$ sqlline.py hadoop102,hadoop103,hadoop104:2181
#7.连接Phoenix(使用轻量级的连接,还要先启动一个queryserver的服务queryserver.py)
[atguigu@hadoop102 ~]$ queryserver.py start
[atguigu@hadoop102 ~]$ sqlline-thin.py hadoop102:8765 # 默认找本机的8765端口
phoenix shell操作
schema(库)
默认情况下,在phoenix中不能直接创建schema。需要将如下的参数添加到Hbase中conf目录下的hbase-site.xml 和 phoenix中bin目录下的 hbase-site.xml中
<property>
<name>phoenix.schema.isNamespaceMappingEnabled</name>
<value>true</value>
</property>
# 重新启动Hbase和连接phoenix客户端.
[atguigu@hadoop102 ~]$ stop-hbase.sh
[atguigu@hadoop102 ~]$ start-hbase.sh
[atguigu@hadoop102 ~]$ sqlline.py hadoop102,hadoop103,hadoop104:2181
# 创建命令,注意这里创建的mydb,到hbase中都是大写的,如果想使用小写的加上双引号
create schema if not exists mybd;
create schema if not exists "mybd2";
# 删除
drop schema if exists "mybd2";
table
-- 1.显示所有表
!table 或 !tables
-- 2.创建表(指定单个列作为RowKey)
CREATE TABLE IF NOT EXISTS student(
id VARCHAR primary key,
name VARCHAR,
addr VARCHAR);
-- 3.插入或者数据
upsert into student(id,name,addr) values('1001','pihao','shenzhen');
-- 4.查询数据
select id,name,addr from student;
-- 5.删除数据
delete from student where id = '1001';
-- 6.创建表(使用联合主键)在hbase中会将联合主键的字段拼接作为rowkey
CREATE TABLE IF NOT EXISTS us_population (
State CHAR(2) NOT NULL,
City VARCHAR NOT NULL,
Population BIGINT
CONSTRAINT my_pk PRIMARY KEY (state, city));
upset into us_population('CA','LOGSJ',1000000);
hbase查看:
value=x的解释:
在hbase中,不允许存在只有rowkey,没有column的数据
在phoenix中,允许存在只有主键的数据(只存在主键,其余字段都为null)
那么问题来了,这种数据在hbase中是怎么存的呢?就使用到了这个value=x。可以理解为空数据,与phoenix对应
表的映射
1) Hbase中没有表,phoenix中创建表会同时在hbase中也创建表
2) Hbase中有表, 可以在phoenix中创建视图(只读)进行映射
create 'emp','info'
put 'emp','1001','info:name','zhangsan'
put 'emp','1001','info:addr','beijing'
create view "emp"(
id varchar primary key ,
"info"."name" varchar ,
"info"."addr" varchar
)
# 正常查询出,没问题
select * from "emp" ;
select id , "name","addr" from "emp" ;
#对视图只能做查询操作,不能增删
upsert into "emp" values('1002','lisi','shanghai');
drop view "emp";
3) Hbase中有表, 可以在phoenix中创建表进行映射,
#phoenix在创建表的时候会给你的字段进行编码储存,但是hbase那边如果先有表的话,那么就是存在字段映射不上的问题,比如,select name,底层给你转化为了 select 'xxx' ,这样就查询不出数据,解决办法:在见表的时候添加上COLUMN_ENCODED_BYTES = NONE;表示不适用编码。这样字段就映射上了,还要注意字段大写的问题,phoenix中的小写到hbase中都会转化为大写。
create table "emp"(
id varchar primary key ,
"info"."name" varchar ,
"info"."addr" varchar
)
COLUMN_ENCODED_BYTES = NONE;
select * from "emp" ;
select id , "name","addr" from "emp" ;
drop table "emp";
数值问题
在phoenix中,存数字的话,然后在hbase中会给你数字格式话,默认使用的是‘toStringBinary’,我们可以自定编码方式
# 现象
phoenix存,phoenix查.没有问题
phoenix存,hbase查.有问题
hbase存,hbase查,没有问题
hbase存,phoenix查,有问题
# 建表
create table test (
id varchar primary key ,
name varchar ,
salary integer
)
COLUMN_ENCODED_BYTES = NONE;
# phoenix插入
upsert into test values('1001','zs',123456);
然后使用hbase查看发现数字变成二进制了,
scan 'TEST',{COLUMNS=>['0:SALARY:toInt']}
就算使用toInt编码也变成负数的了。
delete from test where id = '1001';
# hbase插入
put 'TEST','1002','0:NAME','ls'
put 'TEST','1002','0:SALARY',Bytes.toBytes(456789) -- Long类型
scan 'TEST',{COLUMNS=>['0:SALARY:toLong']} -- hbase查看正常
使用phoenix查看,又变成负数的了。
-- 解决办法:使用无服务的UNSIGNED_INT,或者hbase和phoenix不要交叉使用。
create table test1 (
id varchar primary key ,
name varchar ,
salary UNSIGNED_INT -- 无符号的int
)
COLUMN_ENCODED_BYTES = NONE;
phoenix api操作
添加依赖 thin client
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-queryserver-client</artifactId>
<version>5.0.0-HBase-2.0</version>
</dependency>
编写java代码
// 测试前启动queryserver.py 服务
package com.pihao;
import java.sql.*;
import org.apache.phoenix.queryserver.client.ThinClientUtil;
public class PhoenixTest {
public static void main(String[] args) throws SQLException {
//1.queryserver的连接地址
String connectionUrl = ThinClientUtil.getConnectionUrl("hadoop102", 8765);
//2.获取连接
Connection connection = DriverManager.getConnection(connectionUrl);
//3.编写sql语句
PreparedStatement preparedStatement = connection.prepareStatement("select * from student");
//4.执行
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
resultSet.getString("id");
resultSet.getString("name");
resultSet.getString("addr");
}
//关闭
connection.close();
}
}
添加依赖 thick client
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-core</artifactId>
<version>5.0.0-HBase-2.0</version>
<exclusions>
<exclusion>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.el</artifactId>
<version>3.0.1-b06</version>
</dependency>
编写java代码
package com.atguigu.phoenix.thin;
import java.sql.*;
import java.util.Properties;
public class TestThick {
public static void main(String[] args) throws SQLException {
String url = "jdbc:phoenix:hadoop102,hadoop103,hadoop104:2181";
Properties props = new Properties();
props.put("phoenix.schema.isNamespaceMappingEnabled","true");
Connection connection = DriverManager.getConnection(url,props);
PreparedStatement ps = connection.prepareStatement("select * from \"test\"");
ResultSet rs = ps.executeQuery();
while(rs.next()){
System.out.println(rs.getString(1)+":" +rs.getString(2));
}
}
}
Phoenix二级索引
为啥一上来就是二级索引呢?其实可以这样理解:rowkey是hbase的以及索引,二级索引是给除rowkey外的其他列来创建的
二级索引配置文件
添加如下配置到HBase的HRegionserver节点的hbase-site.xml
<!-- phoenix regionserver 配置参数-->
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
<property>
<name>hbase.region.server.rpc.scheduler.factory.class</name>
<value>org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
<property>
<name>hbase.rpc.controllerfactory.class</name>
<value>org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory</value>
<description>Factory to create the Phoenix RPC Scheduler that uses separate queues for index and metadata updates</description>
</property>
全局二级索引
所谓的全局二级索引,意味着建索引会创建一张索引表.
在索引表中, 将索引列与原表中的rowkey组合起来作为索引表的rowkey.
CREATE TABLE IF NOT EXISTS student(
id VARCHAR primary key,
name VARCHAR,
addr VARCHAR);
upsert into student values('1001','pihao','shanghai');
upsert into student values('1002','zhangsan','shenzhen');
# 没建索引之前测试explain
explain select id from student ; // FULL SCAN全表扫描
explain select id from student where id = '1001' ; //POINT LOOKUP (主键唯一)
explain select id from student where name = 'pihao' ; // FULL SCAN 全表扫描
-- 给name字段建索引
create index idx_student_name on student(name);
explain select id from student where name = 'lixiaosi' ; // RANGE SCAN
# 给name添加索引之后测试explain
explain select id ,name from student where id ='1001' ; // POINT LOOKUP
explain select id ,name from student where name ='pihao' ; //RANGE SCAN,现在走索引了
# 新的问题,多查询了一个addr,这个addr没有创建索引的
explain select id ,name ,addr from student where name ='pihao' ; //FULL SCAN
# 解决办法:创建复合索引
drop index idx_student_name on student;
create index idx_student_addr_name on student(name,addr);
# 测试
-- RANGE SCAN
explain select id ,name ,addr from student where name ='lixiaosi' ;
-- RANGE SCAN
explain select id ,name ,addr from student where name ='lixiaosi' and addr = 'beijing';
-- FULL SCAN(带头大哥不能死)
explain select id ,name ,addr from student where addr = 'beijing';
-- RANGE SCAN (sql被优化)
explain select id ,name ,addr from student where addr = 'beijing' and name ='lixiaosi'
# 给name列建索引包含addr列
drop index idx_student_addr_name on student;
create index idx_student_name on student(name) include(addr); -- 使用include关键字
explain select id ,name ,addr from student where name ='pihao' ; //RANGE SCAN
# 强制使用索引(目前没有了,官方不推荐使用)
select id ,name ,addr from student where name ='pihao' ;
就拿这个sql来说,目前只创建了name索引,让你强制使用name的索引,先找到id,然后再使用id去原表查找,相当于查了两次表
本地二级索引
-- 只创建name索引
create local index idx_student_name on student(name);
-- 还查询了addr
explain select id ,name ,addr from student where name ='pihao' ; //range scan
# 这种方式会在原表中增加数据,将name字段和rowkey拼接作为新的rowkey插入hbase,不会创建新的表
整合hive
hbase作为一个数据库,缺乏强大的数据分析能力,这可以整合hive来使用
整合步骤
在hive-site.xml中添加zookeeper的属性,如下:
<property>
<name>hive.zookeeper.quorum</name>
<value>hadoop102,hadoop103,hadoop104</value>
</property>
<property>
<name>hive.zookeeper.client.port</name>
<value>2181</value>
</property>
启动hive,测试新的表
1) 在hive中建表,对应着在hbase中也建表
CREATE TABLE hive_hbase_emp_table(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" = ":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");
-- 提示:完成之后,可以分别进入Hive和HBase查看,都生成了对应的表
-- 提示:不能将数据直接load进Hive所关联HBase的那张表中
# 再准备一个hive的中间表
CREATE TABLE emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
row format delimited fields terminated by '\t';
# 在hive中执行命令
load data local inpath '/opt/module/hive/data/emp.txt' into table emp;
# 将数据从中间表写入hbase的表
load data local inpath '/home/module/hive/data/emp.txt' into table emp;
# 在hive以及hbase中查询
hbase中又表,hive关联那张表
# 注意:建表语句和之前的一样,唯一的区别是只能创建外部表EXTERNAL
CREATE EXTERNAL TABLE relevance_hbase_emp(
empno int,
ename string,
job string,
mgr int,
hiredate string,
sal double,
comm double,
deptno int)
STORED BY
'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ("hbase.columns.mapping" =
":key,info:ename,info:job,info:mgr,info:hiredate,info:sal,info:comm,info:deptno")
TBLPROPERTIES ("hbase.table.name" = "hbase_emp_table");