Hbase與Phoenix整合


一.簡介

​ Phoenix是HBase的開源SQL皮膚,可以理解為一個HBase的客戶端工具。

好處

​ 1)可以使用標准JDBC API代替HBase客戶端API來創建表,插入數據和查詢HBase數據

​ 2)操作簡單:DML命令以及通過DDL命令創建和操作表和版本化增量更改;

​ 3)支持HBase二級索引創建

二.安裝

1)官網地址

http://phoenix.apache.org/

2)Phoenix部署

(1)上傳並解壓tar包

[hadoop@hadoop102 module]$ tar -zxvf /opt/software/apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /opt/module
[hadoop@hadoop102 module]$ mv apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz phoenix

(2)配置環境變量,soure一下

#phoenix
export PHOENIX_HOME=/opt/module/phoenix
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin

(3)復制server包並分發到各個節點的hbase/lib

[hadoop@hadoop102 module]$ cd /opt/module/phoenix/
[hadoop@hadoop102 module]$ cd /opt/module/phoenix/
[hadoop@hadoop102 phoenix]$ cp phoenix-5.0.0-HBase-2.0-server.jar /opt/module/hbase/lib/
[hadoop@hadoop102 phoenix]$ xsync/opt/module/hbase/lib/

(4)配置文件

​ 修改%Phoenix_HOME%/bin/hbase-site.xml ,然后分發各個HBase節點

	<!-- 二級索引相關配置 -->
    <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>
   <property>
       <name>hbase.coprocessor.abortonerror</name>
       <value>false</value>
  </property>
   <property>
        <name>hbase.regionserver.wal.codec</name>
        <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
    </property>
	<!-- 開啟phoenix對hbase的表的映射 -->
   <property>
        <name>phoenix.schema.isNamespaceMappingEnabled</name>
        <value>true</value>
    </property>
   <property>
        <name>phoenix.schema.mapSystemTablesToNamespace</name>
        <value>true</value>
    </property>

​ 修改%HBASE_HOME%/conf/hbase-site.xml文件

	<!-- 開啟phoenix對hbase的表的映射 -->
   <property>
        <name>phoenix.schema.isNamespaceMappingEnabled</name>
        <value>true</value>
    </property>
   <property>
        <name>phoenix.schema.mapSystemTablesToNamespace</name>
        <value>true</value>
    </property>

(5)啟動zk,HDFS,Hbase,然后連接Phoenix

[hadoop@hadoop102 phoenix]$ bin/sqlline.py hadoop102,hadoop103,hadoop104:2181
[hadoop@hadoop102 ~]$ sqlline.py hadoop102,hadoop103,hadoop104:2181    
Setting property: [incremental, false]
Setting property: [isolation, TRANSACTION_READ_COMMITTED]
issuing: !connect jdbc:phoenix:hadoop102,hadoop103,hadoop104:2181 none none org.apache.phoenix.jdbc.PhoenixDriver
Connecting to jdbc:phoenix:hadoop102,hadoop103,hadoop104:2181
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/phoenix/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
20/07/17 18:10:47 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
Connected to: Phoenix (version 5.0)
Driver: PhoenixEmbeddedDriver (version 5.0)
Autocommit status: true
Transaction isolation: TRANSACTION_READ_COMMITTED
Building list of tables and columns for tab-completion (set fastconnect to true to skip)...
148/148 (100%) Done
Done
sqlline version 1.2.0
0: jdbc:phoenix:hadoop102,hadoop103,hadoop104>

三.Phoenix Shell操作

語法操作可參考官網:https://phoenix.apache.org/language/index.html#

在phoenix中,默認情況下,庫名,表名,字段名等會自動轉換為大寫,若要小寫,使用雙引號,如"us_population"

SCHEMA操作

Phoenix中將HBase的namespace叫做SCHEMA,想到於mysql中的庫的概念。

1.創建schema
CREATE SCHEMA IF NOT EXISTS "庫名"
2.使用schema
USE "庫名"
默認表
USE DEFAULT
3.刪除schema
DROP SCHEMA "庫名" --前提表都刪完。

表操作

創建一個新表。如果HBase表和所引用的列族不存在,則將創建它們。在創建時,為了提高查詢性能,如果沒有明確定義任何列族,則將一個空鍵值作為默認列族

1.顯示所有表
!table

2.創建表

Hbase中不存在表名相同的表,不然就成了映射HBase的表。

CREATE TABLE IF NOT EXISTS "student"(
id VARCHAR primary key, 
name VARCHAR,			
age VARCHAR);           
3.表數據的增刪改查
upsert into "student" values('1001','zhangsan','20'); 
upsert into "student" values('1002','lisi','22');
select * from "student" ;
delete from "student" where id='1002';

注意

1)upsert:表中的主鍵不存在就是插入,存在就是更新

2)where的字段值要加單引號 ' ', 字段名的小寫是加雙引號,別弄混了

對比Hbase中的表結構看一下

4.刪除表
 drop table student;
5.退出命名行
!quit

表映射

默認情況下,直接在Hbase中創建的表,通過phoenix是查看不到的。如果需要在phoenix中操作直接在hbase中創建的表,則需要在phoenix中進行表的映射。映射方式有兩種:視圖映射表映射

在Hbase中創建測試表:(命名空間:bigdata , 表名:map_test)

表結構

info1 info2
rowkey name address
1001 zhangsan BeiJing
1002 lisi ShenZhen
$ cd /home/hadoop/hbase/bin
$ ./hbase shell 進入hbase命令行
hbase(main):008:0> create 'bigdata:map_test','info1','info2'
hbase(main):011:0> put 'bigdata:map_test','1001','info1:name','zhangsan'
hbase(main):012:0> put 'bigdata:map_test','1001','info1:address','BeiJing'
hbase(main):013:0> put 'bigdata:map_test','1002','info1:name','lisi'
hbase(main):014:0> put 'bigdata:map_test','1002','info2:address','ShenZhen'
#Scan驗證一下
hbase(main):015:0> scan 'bigdata:map_test'
ROW        COLUMN+CELL                                                     
1001       column=info1:address, timestamp=1594985273813, value=BeiJing    
1001       column=info1:name, timestamp=1594985243947, value=zhangsan      
1002       column=info1:name, timestamp=1594985290799, value=lisi          
1002       column=info2:address, timestamp=1594985311918, value=ShenZhen  
1.視圖映射

Phoenix創建的視圖是只讀的,所以只能用來做查詢,無法通過視圖對源數據進行修改等操作。

1)創建視圖

create view"bigdata"."map_test"(
"empid" varchar primary key,
"info1"."name" varchar,
"info2"."adress"varchar);

2)刪除視圖

drop view "bigdata"."map_test";

注意=:創建視圖前,如果HBase不是在默認命名空間,需要在Phoenix創建對應的Schema。

2.表映射

使用Apache Phoenix創建對HBase的表映射,有兩種方法:

a)當HBase中不存在表時,可以直接使用create table指令創建需要的表,系統將會自動在Phoenix和HBase中創建表,並會根據指令內的參數對表結構進行初始化。

b)當HBase中已經存在表時,可以以類似創建視圖的方式創建關聯表,只需要將create view改為create table即可。

注意后面加column_encoded_bytes=0,不然Phoenix用自己的編碼列的值就無法查看。

錯誤示例

正確示例

比較坑,如果創建錯了,重新映射的話,只能刪除映射表,會把HBase的表也刪 了。所以不要創建錯......

create table "bigdata"."map_test"(
"empid" varchar primary key,
"info1"."name" varchar,
"info2"."adress"varchar)column_encoded_bytes=0;

四.Phoenix Java API 操作

pom依賴

    <dependencies>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>2.0.5</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.phoenix</groupId>
            <artifactId>phoenix-queryserver-client</artifactId>
            <version>5.0.0-HBase-2.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>2.0.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.6</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>1.10.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>make-assembly</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

操作Phoenix

package com.bigdata.phoenix;

import org.apache.phoenix.queryserver.client.Driver;
import org.apache.phoenix.queryserver.client.ThinClientUtil;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class PhoenixDemo {


    @Test
    public void createTable() throws Exception{
        //1、加載驅動
        Class.forName("org.apache.phoenix.queryserver.client.Driver");
        //2、獲取連接
        String url = ThinClientUtil.getConnectionUrl("hadoop102", 8765);
        System.out.println(url);
        Connection connection = DriverManager.getConnection(url);
        //3、創建Statement對象
        String sql = "create table xxx(" +
                "id varchar primary key," +
                "name varchar," +
                "age varchar)COLUMN_ENCODED_BYTES=0";
        PreparedStatement statement = connection.prepareStatement(sql);
        //4、執行sql操作
        statement.execute();
        //5、關閉
        statement.close();
        connection.close();
    }

    /**
     *
     * @throws Exception
     */
    @Test
    public void insert() throws Exception{
        //1、加載驅動
        Class.forName("org.apache.phoenix.queryserver.client.Driver");
        //2、獲取連接
        String url = ThinClientUtil.getConnectionUrl("hadoop102", 8765);
        System.out.println(url);
        Connection connection = DriverManager.getConnection(url);
        //connection.setAutoCommit(true);
        //3、獲取statement對象
        PreparedStatement statement = connection.prepareStatement("upsert into xxx values(?,?,?)");

        //4、給參數賦值
        statement.setString(1,"1001");
        statement.setString(2,"zhangsan");
        statement.setString(3,"20");

        //5、執行插入
        statement.execute();
        connection.commit();
        //6、關閉
        statement.close();
        connection.close();
    }

    @Test
    public void query() throws Exception{
        //1、加載驅動
        Class.forName("org.apache.phoenix.queryserver.client.Driver");
        //2、獲取連接
        String url = ThinClientUtil.getConnectionUrl("hadoop102", 8765);
        System.out.println(url);
        Connection connection = DriverManager.getConnection(url);
        //connection.setAutoCommit(true);
        //3、獲取statement對象
        PreparedStatement statement = connection.prepareStatement("select * from xxx");

        ResultSet resultSet = statement.executeQuery();
        while (resultSet.next()){
            String id = resultSet.getString("id");
            String name = resultSet.getString("name");
            String age = resultSet.getString("age");
            System.out.println("id="+id+",name="+name+",age="+age);
        }

        statement.close();
        connection.close();
    }
}

五.二級索引

思考:為啥建立二級索引?

現在有一個map_test表

0: jdbc:phoenix:hadoop102,hadoop103,hadoop104> select * from  "map_test";
+--------+-----------+------------+
| empid  |   name    |   adress   |
+--------+-----------+------------+
| 1001   | zhangsan  | BeiJing    |
| 1002   | lisi      | ShenZhen   |
| 1003   | wangwu    | GuangZhou  |
+--------+-----------+------------+

​ 對於Hbase,如果想精確定位到某行記錄,唯一的辦法就是通過rowkey查詢。如果不通過rowkey查找數據,就必須逐行比較每一行的值,對於較大的表,全表掃描的代價是不可接受的。

沒建立索引前:

當我們針對某一列的值進行查詢的話,如name=“zhangsan”,沒建立二級索引之前只能全表掃描過濾。

建立索引后:

那么我們可以針對map_test表建議對應得索引表。

create index "map_test_index" on "bigdata"."map_test" ("info1"."name");

全局索引

Global Index是默認的索引格式,創建全局索引時,會在HBase中建立一張新表。也就是說索引數據和數據表是存放在不同的表中的,因此全局索引適用於多讀少寫的業務場景。

寫數據的時候會消耗大量開銷,因為索引表也要更新,而索引表是分布在不同的數據節點上的,跨節點的數據傳輸帶來了較大的性能消耗。

在讀數據的時候Phoenix會選擇索引表來降低查詢消耗的時間。

1)創建單個字段的全局索引

CREATE INDEX my_index ON my_table (my_col);
示例:
create index "map_test_index" on "bigdata"."map_test" ("info1"."name");

2)創建攜帶其他字段的全局索引

CREATE INDEX my_index ON my_table (v1) INCLUDE (v2);
示例:
create index "map_test_index" on "bigdata"."map_test" ("info1"."name") include ("info2"."adress");

3)刪除索引表

示例:
drop index map_test_index on "bigdata"."map_test";

去Hbase中看一下

可以發現創建了兩張表,索引表的的rowkey=索引字段的值+原表的rowkey,Value是inclue的列值

本地索引

Local Index適用於寫操作頻繁的場景。

索引數據和數據表的數據是存放在同一張表中(且是同一個Region),避免了在寫操作的時候往不同服務器的索引表中寫索引帶來的額外開銷。查詢的字段不是索引字段索引表也會被使用,這會帶來查詢速度的提升。

創建:local關鍵字

create index "map_test_index" on "bigdata"."map_test" ("info1"."name");

去Hbase中看一下

可以發現在Hbase中並沒有單獨的創一個索引表,在原表中為每條數據增加一個索引。

總結

二級索引其實可以理解就是在原表的基礎上,再建立一張索引表,索引包的rowkey=索引字段+原表rowkey。當查詢的列值的時候,會先走索引表找到列值對應得原表中的rowkey,然后根據rowkey再去原表中查對應得數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM