Phoenix
Hbase適合存儲大量的對關系運算要求低的NOSQL數據,受Hbase 設計上的限制不能直接使用原生的API執行在關系數據庫中普遍使用的條件判斷和聚合等操作。Hbase很優秀,一些團隊尋求在Hbase之上提供一種更面向普通開發人員的操作方式,Apache Phoenix即是。
Phoenix 基於Hbase給面向業務的開發人員提供了以標准SQL的方式對Hbase進行查詢操作,並支持標准SQL中大部分特性:條件運算,分組,分頁,等高級查詢語法。
1、Phoenix搭建
Phoenix 4.15 HBase 1.4.6 hadoop 2.7.6
1、關閉hbase集群,在master中執行
stop-hbase.sh
2、上傳解壓配置環境變量
解壓
tar -xvf apache-phoenix-4.15.0-HBase-1.4-bin.tar.gz -C /usr/local/soft/
改名
mv apache-phoenix-4.15.0-HBase-1.4-bin phoenix-4.15.0
3、將phoenix-4.15.0-HBase-1.4-server.jar復制到所有節點的hbase lib目錄下
scp /usr/local/soft/phoenix-4.15.0/phoenix-4.15.0-HBase-1.4-server.jar master:/usr/local/soft/hbase-1.4.6/lib/
scp /usr/local/soft/phoenix-4.15.0/phoenix-4.15.0-HBase-1.4-server.jar node1:/usr/local/soft/hbase-1.4.6/lib/
scp /usr/local/soft/phoenix-4.15.0/phoenix-4.15.0-HBase-1.4-server.jar node2:/usr/local/soft/hbase-1.4.6/lib/
4、啟動hbase , 在master中執行
start-hbase.sh
5、配置環境變量
vim /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
);
# 2、顯示所有表
!table
# 3、插入數據
upsert into STUDENT values('1500100004','葛德曜',24,'男','理科三班');
upsert into STUDENT values('1500100005','宣谷芹',24,'男','理科六班');
upsert into STUDENT values('1500100006','羿彥昌',24,'女','理科三班');
# 4、查詢數據,支持大部分sql語法,
select * from STUDENT ;
select * from STUDENT where age=24;
select gender ,count(*) from STUDENT group by gender;
select * from student order by gender;
# 5、刪除數據
delete from STUDENT where id='1500100004';
# 6、刪除表
drop table STUDENT;
# 7、退出命令行
!quit
更多語法參照官網
https://phoenix.apache.org/language/index.html#upsert_select
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
);
CREATE view "students" (
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";
# 刪除視圖
drop view "test";
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');
CREATE table "students" (
id VARCHAR NOT NULL PRIMARY KEY,
"info"."name" VARCHAR,
"info"."age" VARCHAR,
"info"."gender" VARCHAR ,
"info"."clazz" VARCHAR
) column_encoded_bytes=0;
upsert into "students" values('1500110004','葛德曜','24','n ü','理科三班');
使用create table創建的關聯表,如果對表進行了修改,源數據也會改變,同時如果關聯表被刪除,源表也會被刪除。但是視圖就不會,如果刪除視圖,源數據不會發生改變。
3、Phoenix二級索引
對於Hbase,如果想精確定位到某行記錄,唯一的辦法就是通過rowkey查詢。如果不通過rowkey查找數據,就必須逐行比較每一行的值,對於較大的表,全表掃描的代價是不可接受的。
1、開啟索引支持
# 關閉hbase集群
stop-hbase.sh
# 在/usr/local/soft/hbase-1.4.6/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.sql master,node1,node2
2、創建索引
2.1、全局索引
全局索引適合讀多寫少的場景。如果使用全局索引,讀數據基本不損耗性能,所有的性能損耗都來源於寫數據。數據表的添加、刪除和修改都會更新相關的索引表(數據刪除了,索引表中的數據也會刪除;數據增加了,索引表的數據也會增加)
注意: 對於全局索引在默認情況下,在查詢語句中檢索的列如果不在索引表中,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;
# 上傳數據DIANXIN.csv
# 導入數據
psql.py master,node1,node2 DIANXIN.sql DIANXIN.csv
# 創建全局索引
CREATE INDEX DIANXIN_INDEX ON DIANXIN ( end_date );
# 查詢數據 ( 索引未生效)
select * from DIANXIN where end_date = '20180503154014';
# 強制使用索引 (索引生效) hint
select /*+ INDEX(DIANXIN DIANXIN_INDEX) */ * from DIANXIN where end_date = '20180503154014';
select /*+ INDEX(DIANXIN DIANXIN_INDEX) */ * from DIANXIN where end_date = '20180503154014' and start_date = '20180503154614';
# 取索引列,(索引生效)
select end_date from DIANXIN where end_date = '20180503154014';
# 創建多列索引
CREATE INDEX DIANXIN_INDEX1 ON DIANXIN ( end_date,COUNTY );
# 多條件查詢 (索引生效)
select end_date,MDN,COUNTY from DIANXIN where end_date = '20180503154014' and COUNTY = '8340104';
# 查詢所有列 (索引未生效)
select * from DIANXIN where end_date = '20180503154014' and COUNTY = '8340104';
# 查詢所有列 (索引生效)
select /*+ INDEX(DIANXIN DIANXIN_INDEX1) */ * from DIANXIN where end_date = '20180503154014' and COUNTY = '8340104';
# 單條件 (索引未生效)
select end_date from DIANXIN where COUNTY = '8340103';
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);
# 索引生效
select grid_id from dianxin where grid_id='117285031820040';
# 索引生效
select * from dianxin where grid_id='117285031820040';
2.3、覆蓋索引
覆蓋索引是把原數據存儲在索引數據表中,這樣在查詢時不需要再去HBase的原表獲取數據就,直接返回查詢結果。
注意:查詢是 select 的列和 where 的列都需要在索引中出現。
# 創建覆蓋索引
CREATE INDEX DIANXIN_INDEX_COVER ON DIANXIN ( x,y ) INCLUDE ( county );
# 查詢所有列 (索引未生效)
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 中的列可以放在INCLUDE (將數據保存在索引中)
select /*+ INDEX(DIANXIN DIANXIN_INDEX_COVER) */ x,y,count(*) from dianxin group by x,y;
4、Phoenix JDBC
# 導入依賴
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-core</artifactId>
<version>4.15.0-HBase-1.4</version>
</dependency>
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();