Springboot 系列 (29) - Springboot+HBase 大数据存储(七)| Springboot 项目通过 Phoenix 组件使用 JDBC 访问 HBase

发布时间 2023-04-05 13:26:31作者: 垄山小站


Phoenix 是 HBase 的开源 SQL 皮肤,通过 Phoenix 可以使用标准 JDBC API 代替 HBase 客户端 API 来创建表,插入数据和查询 HBase 数据。

Phoenix 会把 SQL 编译成一系列的 Hbase 的 scan 操作,然后把 scan 结果生成标准的 JDBC 结果集,其底层由于使用了 Hbase 的API,协处理器,过滤器。Phoenix 支持的操作:SELECT, FROM、WHERE、GROUP BY、HAVING、ORDER BY 等。

Phoenix 其实就是一个客户端(或组件),它处于应用程序和 HBase 之间。Phoenix 客户端分为两种:

    (1) 瘦客户端:将用户写好的 SQL 转交给 Phoenix Query 服务,由 Phoenix Query 服务把 SQL 解析成对应的HBase 操作,并交给 HBase 执行,执行完成之后并负责把结果收集回来,并转换成二维表格返回给用户。Phoenix Query 服务只针对瘦客户端。Phoenix Query 服务需要放到 HBase 的 RegionServer 里面。
    (2) 胖客户端:不需要依赖 Phoenix Query 服务,客户端本身就可以完成 SQL 的解析和提交操作(连接 Zookeeper),建议用胖客户端,少一个需要维护的服务。

Phoenix 的特点:

    (1) Phoenix 与 HBase 连接后会自动创建一些系统表, SYSTEM.CATALOG、SYSTEM.CHILD_LINK、SYSTEM.FUNCTION、SYSTEM.LOG、SYSTEM.MUTEX、SYSTEM.SEQUENCE、SYSTEM.STATS、SYSTEM.TASK,其中 SYSTEM.CATALOG 表用于存放 Phoenix 创建表时的元数据;
    (2) Phoenix 创建表时会自动调用 HBase 客户端创建相应的表,并且在 SYSTEM.CATALOG 系统表中记录 Phoenix 创建表时的元数据,其主键的值对应 HBase 的 RowKey,非主键的列对应 HBase 的 Column(列族不指定时为0,且列会进行编码);
    (3) Phoenix 的 SQL 中如果表名、字段名不使用双引号标注那么默认转换成大写,Phoenix 中的字符串使用单引号进行标注。默认情况下,库名,表名,字段名等会自动转换为大写,若要小写,使用双引号,如 "ns1",要特别注意引号的使用方式。
    (4) 通过 Phoenix 创建的表,必须通过 Phoenix 客户端来对表进行操作,因为通过 Phoenix 创建的表其非主键的列会被编码;

Phoenix:https://phoenix.apache.org/

HBase 的安装配置,请参考 “Springboot 系列 (24) - Springboot+HBase 大数据存储(二)| 安装配置 Apache HBase 和 Apache Zookeeper”。

本文先介绍 Phoenix 组件的安装配置过程,再演示通过 Phoenix 组件使用 JDBC 访问 HBase。


1. 系统环境

    操作系统:Ubuntu 20.04
    Java 版本:openjdk 11.0.18
    Hadoop 版本:3.2.2
    Zookeeper 版本:3.6.3

    HBase 版本:2.4.4
    HBase 所在路径:~/apps/hbase-2.4.4

    本文 HBase 在 HBase + Zookeeper (独立的) 模式下运行,Zookeeper 使用端口 2182。

 

2. 下载 Phoenix

    访问 http://archive.apache.org/dist/phoenix/phoenix-5.1.2/phoenix-hbase-2.4-5.1.2-bin.tar.gz,下载 phoenix-hbase-2.4-5.1.2-bin.tar.gz 保存到 ~/apps 目录。

    $ cd ~/apps
    $ tar -zvxf phoenix-hbase-2.4-5.1.2-bin.tar.gz
    $ mv phoenix-hbase-2.4-5.1.2-bin phoenix-5.1.2

    $ cd phoenix-5.1.2
    $ cp phoenix-server-hbase-2.4-5.1.2.jar ../hbase-2.4.4/lib  # HBase 集群的 master 和 slave 都要复制该文件  

 

3. 配置 Phoenix Schema 操作权限

    $ cd ~/apps

    # 修改 HBase 配置文件,添加如下内容
    $ vim ./hbase-2.4.4/conf/hbase-site.xml

        <property>
            <name>phoenix.schema.isNamespaceMappingEnabled</name>
            <value>true</value>
        </property>
        <property>
            <name>phoenix.schema.mapSystemTablesToNamespace</name>
            <value>true</value>
        </property>


    # 修改 Phoenix 配置文件,添加如下内容
    $ vim ./phoenix-5.1.2/bin/hbase-site.xml

        <property>
            <name>phoenix.schema.isNamespaceMappingEnabled</name>
            <value>true</value>
        </property>
        <property>
            <name>phoenix.schema.mapSystemTablesToNamespace</name>
            <value>true</value>
        </property>    

 

    # 重启 HBase

        $ cd ~/apps

        # 停止 HBase
        $ ./hbase-2.4.4/bin/stop-hbase.sh

        # 启动 HBase
        $ ./hbase-2.4.4/bin/start-hbase.sh


4. Phoenix Shell (Python)

    $ cd ~/apps
    $ ./phoenix-5.1.2/bin/sqlline.py localhost:2182    # localhost:2182 是 Zookeeper 的地址和端口

        ...

        Connected to: Phoenix (version 5.1)
        Driver: PhoenixEmbeddedDriver (version 5.1)
        Autocommit status: true
        Transaction isolation: TRANSACTION_READ_COMMITTED
        sqlline version 1.9.0

        # 创建 schema (就是 HBase 中的 Namespace,相当于 RDBMS 的数据库)
        0: jdbc:phoenix:localhost:2182> CREATE schema IF NOT EXISTS "ns_test";


        # 切换 schema,如果不执行 USE "ns_test",那默认操作的是 “default” schema
        0: jdbc:phoenix:localhost:2182> USE "ns_test";


        # 删除 schema
        0: jdbc:phoenix:localhost:2182> drop schema "ns_test";


        # 创建表
        0: jdbc:phoenix:localhost:2182> CREATE TABLE IF NOT EXISTS user (
        . . . . . . . . . . . . . . .)> id varchar PRIMARY KEY,
        . . . . . . . . . . . . . . .)> name varchar,
        . . . . . . . . . . . . . . .)> age integer);


        # 查看 Phoenix 创建的所有表
        0: jdbc:phoenix:localhost:2182> !tables

            +-----------+-------------+------------+--------------+---------+-----------+--- ...
            | TABLE_CAT | TABLE_SCHEM | TABLE_NAME |  TABLE_TYPE  | REMARKS | TYPE_NAME |
            +-----------+-------------+------------+--------------+---------+-----------+---
            |           | SYSTEM      | CATALOG    | SYSTEM TABLE |         |           |   
            |           | SYSTEM      | CHILD_LINK | SYSTEM TABLE |         |           |   
            |           | SYSTEM      | FUNCTION   | SYSTEM TABLE |         |           |   
            |           | SYSTEM      | LOG        | SYSTEM TABLE |         |           |   
            |           | SYSTEM      | MUTEX      | SYSTEM TABLE |         |           |   
            |           | SYSTEM      | SEQUENCE   | SYSTEM TABLE |         |           |   
            |           | SYSTEM      | STATS      | SYSTEM TABLE |         |           |   
            |           | SYSTEM      | TASK       | SYSTEM TABLE |         |           |  
            |           | ns_test     | USER       | TABLE        |         |           |   
            +-----------+-------------+------------+--------------+---------+-----------+---


        # 插入/更新数据
        0: jdbc:phoenix:localhost:2182> UPSERT INTO user VALUES ('1', 'Tom', 12);
        0: jdbc:phoenix:localhost:2182> UPSERT INTO user(id,name,age) VALUES ('2', 'Jerry', 10);

            注: 如果主键的值重复,那么进行更新操作,否则插入一条新的记录。在使用 UPSERT 时,主键的列不能为空(包括联合主键)。


        # 查询数据
        0: jdbc:phoenix:localhost:2182> SELECT * FROM user;

            +----+-------+-----+
            | ID | NAME  | AGE |
            +----+-------+-----+
            | 1  | Tom   | 12  |
            | 2  | Jerry | 10  |
            +----+-------+-----+
            2 rows selected (0.066 seconds)
            
            注:查询支持 ORDER BY、GROUP BY、LIMIT、JOIN 等操作,同时 Phoenix 提供了 COUNT()、MAX()、MIN()、SUM() 等函数。
            
                函数列表可以查看:http://phoenix.apache.org/language/functions.html


        # 删除表
        0: jdbc:phoenix:localhost:2182> DROP TABLE user;


        # 退出 sqlline,可以运行 !exit 或 !quit 或 !q
        0: jdbc:phoenix:localhost:2182> !exit

 

    Phoenix SQL 相关文档: https://phoenix.apache.org/language/index.html


5. 创建 Springboot 项目

    Windows版本:Windows 10 Home (20H2)   
    IntelliJ IDEA:Community Edition for Windows 2020.1.4
    Apache Maven:3.8.1

    注:Spring 开发环境的搭建,可以参考 “ Spring基础知识(1)- Spring简介、Spring体系结构和开发环境配置 ”。

    1) 运行 IDEA 创建项目
   
        点击菜单 New 创建 Project:
        
        New Project -> Project Type: Maven -> Project SDK: 1.8 -> Check "Create from archtype" -> select "org.apache.maven.archtypes:maven-archtype-quickstart" -> Next

            Name: SpringbootExample23
            GroupId: com.example
            ArtifactId: SpringbootExample23

        -> Finish

    2) 修改 pom.xml

        <?xml version="1.0" encoding="UTF-8"?>

        <project xmlns="http://maven.apache.org/POM/4.0.0"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                                    http://maven.apache.org/xsd/maven-4.0.0.xsd">
            <modelVersion>4.0.0</modelVersion>

            <groupId>com.example</groupId>
            <artifactId>SpringbootExample23</artifactId>
            <version>1.0-SNAPSHOT</version>

            <name>SpringbootExample23</name>
            <!-- FIXME change it to the project's website -->
            <url>http://www.example.com</url>

            <properties>
                <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
                <maven.compiler.source>1.8</maven.compiler.source>
                <maven.compiler.target>1.8</maven.compiler.target>
            </properties>

            <parent>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-parent</artifactId>
                <version>2.6.6</version>
                <relativePath/> <!-- lookup parent from repository -->
            </parent>

            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-test</artifactId>
                    <scope>test</scope>
                </dependency>            
                <dependency>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                    <version>4.11</version>
                    <scope>test</scope>
                </dependency>
            </dependencies>
            
            <build>
                <plugins>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <configuration>
                            <mainClass>com.example.App</mainClass>
                            <layout>JAR</layout>
                        </configuration>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>repackage</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>

                <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
                    <plugins>
                        ...
                    </plugins>
                </pluginManagement>
            </build>
        </project>


        在IDE中项目列表 -> SpringbootExample23 -> 点击鼠标右键 -> Maven -> Reload Project

        本文选择了 spring-boot-starter-parent 2.6.6 相关依赖包,spring-boot-starter 和 spring-boot-starter-test 的版本由 spring-boot-starter-parent 控制。

    3) 修改 src/main/java/com/example/App.java 文件

        package com.example;

        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;

        @SpringBootApplication
        public class App {
            public static void main(String[] args) {
                SpringApplication.run(App.class, args);
                System.out.println("Spring boot jar empty project");
            }
        }


    4) 添加 src/main/resources/application.properties 文件
    
        spring.main.banner-mode=off

    5) 运行

        Run -> Edit configurations -> Click "+" -> Select "Maven"

            Command line: clean spring-boot:run
            Name: SpringbootExample23 [clean,spring-boot:run]

        -> OK

        Run -> Run "SpringbootExample23 [clean,spring-boot:run]"
           
            Spring boot jar empty project


6. 添加 Phoenix 依赖

    1) 修改 pom.xml,添加如下内容

        <project ... >

            ...

            <dependencies>

                ...

                <dependency>
                    <groupId>org.apache.phoenix</groupId>
                    <artifactId>phoenix-core</artifactId>
                    <version>5.1.2</version>
                    <exclusions>
                        <exclusion>
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-log4j12</artifactId>
                        </exclusion>
                    </exclusions>
                </dependency>
                <dependency>
                    <groupId>org.apache.phoenix</groupId>
                    <artifactId>phoenix-hbase-compat-2.4.1</artifactId>
                    <version>5.1.2</version>
                    <scope>runtime</scope>
                </dependency>

            </dependencies>

        </project>


        注:phoenix-core、phoenix-hbase-compat 和 HBase 三者之间的版本必须兼容,phoenix-core 和 phoenix-hbase-compat 的版本要一致(或兼容),phoenix-hbase-compat-2.4.1 匹配的是 HBase 2.4.1+ (本文使用 HBase 2.4.4,在匹配范围内)。

        在IDE中项目列表 -> SpringbootExample23 -> 点击鼠标右键 -> Maven -> Reload Project

    2) 复制 hbase-site.xml 文件

        把 phoenix-5.1.2/bin/hbase-site.xml 文件复制到项目 src/main/resources/hbase-site.xml,文件内容如下:

        <configuration>
            <property>
                <name>hbase.regionserver.wal.codec</name>
                <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
            </property>
            <property>
                <name>phoenix.schema.isNamespaceMappingEnabled</name>
                <value>true</value>
            </property>
            <property>
                <name>phoenix.schema.mapSystemTablesToNamespace</name>
                <value>true</value>
            </property>
        </configuration>

 


7. Phoenix JDBC 示例

    在上文 Phoenix Shell 部分,我们在 HBase 里创建了一个 user 表,示例将使用 Phoenix JDBC 访问 user 表。  

    1) 修改 src/test/com/example/App.java 文件

        package com.example;

        import java.sql.*;

        import org.springframework.boot.SpringApplication;
        import org.springframework.boot.autoconfigure.SpringBootApplication;

        @SpringBootApplication
        public class App {

            public static void main(String[] args) {
                SpringApplication.run(App.class, args);

                Connection conn = null;

                try {

                    Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
                    conn = DriverManager.getConnection("jdbc:phoenix:localhost:2182");

                    String sql = "SELECT * FROM user";
                    PreparedStatement preparedStatement = conn.prepareStatement(sql);
                    ResultSet rs = preparedStatement.executeQuery();

                    System.out.println(sql);

                    while (rs.next()) {
                        System.out.println("name: " + rs.getString("name") + ", age: " + rs.getString("age"));
                    }

                    rs.close();
                    conn.close();

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (SQLException e) {
                    e.printStackTrace();
                } finally {
                    if (conn != null) {
                        try {
                            conn.close();
                        } catch (SQLException e) {

                        }
                    }
                }

            }
        }


    2) 打包 jar

        菜单 View -> Tool Windows -> Maven -> SpringbootExample23 -> Lifecycle -> Clean & Package

        jar 包生成在目录 target/ 里

            SpringbootExample23-1.0-SNAPSHOT.jar
            SpringbootExample23-1.0-SNAPSHOT.jar.original  

        SpringbootExample23.jar  包含依赖包,可以直接运行。 SpringbootExample23.jar.original 里不包含依赖的包(要手动配置依赖环境),运行前要把文件名上的 “.original” 去掉。

    3) 运行 jar

        本文 HBase 2.4.4 在 Ubuntu 20.04 主机的 ~/apps 目录。
        
        把 SpringbootExample23-1.0-SNAPSHOT.jar 文件复制到 ~/apps 目录下,运行如下命令:

            $ java -jar SpringbootExample23-1.0-SNAPSHOT.jar

                ...

                SELECT * FROM user
                name: Tom, age: 12
                name: Jerry, age: 10
                2023-04-05 12:07:28.415  INFO 25762 --- [2182@0x386f0da3] org.apache.zookeeper.ZooKeeper           : Session: 0x100013e3e800013 closed
                2023-04-05 12:07:28.415  INFO 25762 --- [da3-EventThread] org.apache.zookeeper.ClientCnxn          : EventThread shut down for session: 0x100013e3e800013