Phoenix二級索引

真好吃啊發表於2021-12-06

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 "student" (
 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  "student" (
 id VARCHAR NOT NULL PRIMARY KEY, 
 "info"."name" VARCHAR,
 "info"."age" VARCHAR, 
 "info"."gender" VARCHAR ,
 "info"."clazz" VARCHAR
) column_encoded_bytes=0;

upsert into "student" 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';
# 索引未生效就還是去原始表查,不是去索引表查
# 執行超過1秒,不算是實時的

# 強制使用索引 (索引生效) hint   
# /*+ INDEX(DIANXIN DIANXIN_INDEX) */ 算是提示
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';

#CREATE INDEX DIANXIN_INDEX1 ON DIANXIN ( end_date,COUNTY );
#end_date 在前,row字首過濾器,效率快

# 單條件  (索引生效) end_date 在前  返回快
select COUNTY from DIANXIN where end_date = '20180503154014';

# 刪除索引
drop index DIANXIN_INDEX on DIANXIN;

#單獨啟動regionServer
./hbase-daemon.sh start regionserver

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';
# 因為DIANXIN表資料大,所以速度比較慢

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>


package com.gym.HBase;

import java.sql.*;

/**
 * @
 * @create 2021-12-04-21:11
 * @create 明天吃烤肉
 */
public class Demo7PhoenixJDBC {
    public static void main(String[] args) throws SQLException {

        //1.建立連線
        Connection conn = DriverManager.getConnection("jdbc:phoenix:master,node1,node2:2181");

        //
        PreparedStatement ps = conn.prepareStatement("select /*+ INDEX(DIANXIN DIANXIN_INDEX_COVER) */ * from DIANXIN where x=? and y =?");

        //
        ps.setDouble(1, 117.233);
        ps.setDouble(2, 31.833);

        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 county = rs.getString("county");

            System.out.println(mdn + "," + start_date + "," + end_date + "," + county);


        }
        ps.close();
        conn.close();
    }
}

相關文章