phoenix,中文译为“凤凰”,很美的名字。Phoenix 是由 saleforce.com 开源的一个项目,后又捐给了 Apache 基金会。它相当于一个 Java 中间件,提供 jdbc 连接,操作 hbase 数据表。 Phoenix 是一个 HBase 的开源 SQL 引擎。你可以使用标准的 JDBC API 代替 HBase 客户端 API来创建表,插入数据,查询你的 HBase 数据。Phoenix 的团队用了一句话概括 Phoenix:"We put the SQL back in NoSQL" 意思是:我们把 SQL 又放回 NoSQL 去了!这边说的 NoSQL 专指 HBase ,意思是可以用 SQL 语句来查询 Hbase ,你可能会说:“Hive 和 Impala 也可以啊!”。但是 Hive 和 Impala 还可以查询文本文件,Phoenix 的特点就是,它只能查 Hbase,别的类型都不支持!但是也因为这种专一的态度,让 Phoenix 在 Hbase 上查询的性能超过了 Hive 和 Impala!
Phoenix 是构建在 HBase 之上的 SQL 引擎。你也许会存在“ Phoenix 是否会降低 HBase 的效率?”或者“ Phoenix 效率是否很低?”这样的疑虑,事实上并不会,Phoenix 通过以下方式实现了比你自己手写的方式相同或者可能是更好的性能(更不用说可以少写了很多代码):
编译你的 SQL 查询为原生 HBase 的 scan 语句。
检测 scan 语句最佳的开始和结束的 key。
精心编排你的 scan 语句让他们并行执行。
推送你的WHERE子句的谓词到服务端过滤器处理。
执行聚合查询通过服务端钩子(称为协同处理器)。
除此之外,Phoenix还做了一些有趣的增强功能来更多地优化性能
实现了二级索引来提升非主键字段查询的性能。
统计相关数据来提高并行化水平,并帮助选择最佳优化方案。
跳过扫描过滤器来优化IN,LIKE,OR查询。
优化主键的来均匀分布写压力。
安装好 zk 集群,hadoop 集群,hbase 集群。
下载安装包 并 tar -zxvf xx.gz 解压
从对应的地址下载:http://mirrors.cnnic.cn/apache/phoenix/
配置
验证是否成功
Phoenix 可以有 4 种方式调用:
创建 user_phoenix.sql 文件
CREATE TABLE IF NOT EXISTS user_phoenix ( state CHAR(2) NOT NULL, city VARCHAR NOT NULL, population BIGINT CONSTRAINT my_pk PRIMARY KEY (state, city));
创建user_phoenix.csv数据文件
NY,New York,8143197
CA,Los Angeles,3844829
IL,Chicago,2842518
TX,Houston,2016582
PA,Philadelphia,1463281
AZ,Phoenix,1461575
TX,San Antonio,1256509
CA,San Diego,1255540
TX,Dallas,1213825
CA,San Jose,912332
创建user_phoenix_query.sql文件
SELECT state as "State",count(city) as "City Count",sum(population) as "Population Sum" FROM user_phoenix GROUP BY state ORDER BY sum(population) DESC;
执行
/var/local/phoenix/bin/psql.py vm01:2181 user_phoenix.sql user_phoenix.csv user_phoenix_query.sql
这条命令同时做了三件事:创建表、插入数据、查询结果
用Hbase shell 看下会发现多出来一个 USER_PHOENIX 表,用scan 命令查看一下这个表的数据
结论:
!all | Execute the specified SQL against all the current connections |
!autocommit | Set autocommit mode on or off |
!batch | Start or execute a batch of statements |
!brief | Set verbose mode off |
!call | Execute a callable statement |
!close | Close the current connection to the database |
!closeall | Close all current open connections |
!columns | List all the columns for the specified table |
!commit | Commit the current transaction (if autocommit is off) |
!connect | Open a new connection to the database. |
!dbinfo | Give metadata information about the database |
!describe | Describe a table |
!dropall | Drop all tables in the current database |
!exportedkeys | List all the exported keys for the specified table |
!go | Select the current connection |
!help | Print a summary of command usage |
!history | Display the command history |
!importedkeys | List all the imported keys for the specified table |
!indexes | List all the indexes for the specified table |
!isolation | Set the transaction isolation for this connection |
!list | List the current connections |
!manual | Display the SQLLine manual |
!metadata | Obtain metadata information |
!nativesql | Show the native SQL for the specified statement |
!outputformat | Set the output format for displaying results(table,vertical,csv,tsv,xmlattrs,xmlelements) |
!primarykeys | List all the primary keys for the specified table |
!procedures | List all the procedures |
!properties | Connect to the database specified in the properties file(s) |
!quit | Exits the program |
!reconnect | Reconnect to the database |
!record | Record all output to the specified file |
!rehash | Fetch table and column names for command completion |
!rollback | Roll back the current transaction (if autocommit is off) |
!run | Run a script from the specified file |
!save | Save the current variabes and aliases |
!scan | Scan for installed JDBC drivers |
!script | Start saving a script to a file |
!set | Set a sqlline variable |
建立employee的映射表—数据准备
数据准备然后我们来建立一个映射表,有2个列族 company、family
create 'employee','company','family'
put 'employee','row1','company:name','ted'
put 'employee','row1','company:position','worker'
put 'employee','row1','family:tel','13600912345'
put 'employee','row2','company:name','michael'
put 'employee','row2','company:position','manager'
put 'employee','row2','family:tel','1894225698'
scan 'employee'
在建立映射表之前要说明的是,Phoenix 是大小写敏感的,并且所有命令都是大写,如果你建的表明没有用双引号括起来,那么无论你输入的是大写还是小写,建立出来的表名都是大写的,如果你需要建立出同时包含大写和小写的表名和字段名,请把表名或者字段名用双引号括起来。
你可以把建立读写的表或者只读的表,他们的区别如下:
读写表:如果你定义的列族不存在,会被自动建立出来,并且赋以空值。
只读表:你定义的列族必须事先存在。
建立映射表
0: jdbc:phoenix:bw01>CREATE TABLE IF NOT EXISTS "employee" ("no" VARCHAR(10) NOT NULL PRIMARY KEY, "company"."name" VARCHAR(30),"company"."position" VARCHAR(20), "family"."tel" VARCHAR(20), "family"."age" INTEGER);
语句有以下几个注意点:
作为 rowkey 的字段用 PRIMARY KEY 标定
列族用 columnFamily.columnName 来表示。
family.age 是新增的字段,我之前建立测试数据的时候没有建立这个字段的原因是在 hbase shell 下无法直接写入数字型,等等我用 UPSERT 命令插入数据的时候你就可以看到真正的数字型在hbase 下是如何显示的
建立好后,查询一下数据。查询映射表数据
0: jdbc:phoenix:bw01> SELECT * FROM “employee”;
±------±---------±----------±-------------±------+
| no | name | position | tel | age |
±------±---------±----------±-------------±------+
| row1 | ted | worker | 13600912345 | null |
| row2 | michael | manager | 1894225698 | null |
±------±---------±----------±-------------±------+
插入数据、更改数据
插入或者更改数据在 phoenix 中使用upsert关键字,如果表中不存在该数据则插入,否则更新。
插入:
0: jdbc:phoenix:bw01> upsert INTO "employee" VALUES ('row3','billy','worker','16974681345',33);
修改数据:
0: jdbc:phoenix:bw01:2181> UPSERT INTO "employee" ("no","tel") VALUES ('row2','13588888888');
查询:
0: jdbc:phoenix:bw01:2181> select * from "employee";
±------±---------±----------±-------------±------+
| no | name | position | tel | age |
±------±---------±----------±-------------±------+
| row1 | ted | worker | 13600912345 | null |
| row2 | michael | manager | 13588888888 | null |
| row3 | billy | worker | 16974681345 | 33 |
±------±---------±----------±-------------±------+
3 rows selected (0.06 seconds)
查询 HBase 数据
hbase(main):056:0> scan 'employee'
ROW COLUMN+CELL
row1 column=company:_0, timestamp=1484730892661, value=
row1 column=company:name, timestamp=1484730892527, value=ted
row1 column=company:position, timestamp=1484730892565, value=worker
row1 column=family:tel, timestamp=1484730892661, value=13600912345
row2 column=company:_0, timestamp=1484730892762, value=
row2 column=company:name, timestamp=1484730892702, value=michael
row2 column=company:position, timestamp=1484730892730, value=manager
row2 column=family:tel, timestamp=1484730892762, value=13588888888
row3 column=company:_0, timestamp=1484809036587, value=x
row3 column=company:name, timestamp=1484809036587, value=billy
row3 column=company:position, timestamp=1484809036587, value=worker
row3 column=family:age, timestamp=1484809036587, value=\x80\x00\x00!
row3 column=family:tel, timestamp=1484809036587, value=16974681345
squirrel下载
从网址http://www.squirrelsql.org/下载相应版本的squirrel的安装jar包,比如下载squirrel-sql-3.7-standard.jar window版本。
squirrel 安装
window 下:通过 cmd 进入 window 控制台,输入 java-jar squirrel-sql-3.7-standard.jar
显示安装界面:
解压的phoenix-4.7.0-HBase-1.1-bin.tar.gz包的主目录下将如下几个jar包拷贝到squirrel安装目录的lib下
在出现的窗口中填写如下项
Name:就是个名字任意取就可以,这里使用phoenix
Example URL :jdbc:phoenix:bw01:2181(这里是你的phonenix的jdbc地址,注意端口也可以不写,多个用逗号隔开)
Class Name:org.apache.phoenix.jdbc.PhoenixDriver
连接 Phoenix
点击 Aiiasses,点击右边的添加图标
这里还是名字随意写(这里使用 phoenix),driver 要选择刚才配置的可用的 driver,我们刚才配置的是phoenix。
url 这里就是连接 phonex 的 url 选择了 phoenix 的 driver 以后自动出现也可以改,user name 就是 phoenix 连接的主机的用户名,密码就是该机器的密码,点击自动登录
然后点击 test,显示连接成功即可(在这里最好不要直接点 OK ,先点 Test,连接成功了再 OK )
JDBC 调用方式
打开 Eclipse 建立一个简单的 Maven 项目 phoenix。
pom.xml 文件内容:
4.0.0
cn.bw
phoenix
0.0.1-SNAPSHOT
jar
phoenix
http://maven.apache.org
UTF-8
junit
junit
3.8.1
org.apache.phoenix
phoenix-core
4.7.0-HBase-1.1
建立一个类 PhoenixManager
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author lishas
*/
public class PhoenixManager {
public static void main(String[] args) throws SQLException {
Connection conn=null;
Statement state=null;
ResultSet rs=null;
try {
Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
conn = DriverManager.getConnection("jdbc:phoenix:bw01:2181");
state=conn.createStatement();
rs= state.executeQuery("select * from \"employee\"");
while(rs.next()){
System.out.println("no:"+rs.getString("no"));
System.out.println("name:"+rs.getString("name"));
System.out.println("position:"+rs.getString("position"));
System.out.println("age:"+rs.getInt("age"));
System.out.println("-------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(rs!=null)rs.close();
if(state!=null) state.close();
if(conn!=null) conn.close();
}
}
}
运行结果展现:
no:row1
name:ted
position:worker
age:0
-------------------------
no:row2
name:michael
position:manager
age:0
-------------------------
no:row3
name:billy
position:worker
age:33
-------------------------