Phoenix

Apache Phoenix是构建在HBase上的SQL中间层,允许使用标准的SQL语法来查询和管理存储在HBase中的数据。

前提条件

已创建选择了PhoenixHBase服务的DataServingCustom类型的集群,详情请参见创建集群

通过命令行使用Phoenix

  1. 使用SSH方式连接集群,详情请参见登录集群

  2. 执行以下命令,使用Phoenix的命令行工具。

    /opt/apps/PHOENIX/phoenix-current/bin/sqlline.py
  3. 您可以使用SQL进行数据查询。常见操作如下:

    • 创建表

      CREATE TABLE IF NOT EXISTS example(
          my_pk bigint not null,
          m.first_name varchar(50),
          m.last_name varchar(50) 
          CONSTRAINT pk PRIMARY KEY (my_pk)
      );
    • 插入数据

      UPSERT INTO example(my_pk,m.first_name,m.last_name) VALUES(100,'Jack','Ben');
      UPSERT INTO example(my_pk,m.first_name,m.last_name) VALUES(200,'Jack3','Ben3');
    • 查询数据

      SELECT * FROM example;

      返回信息如下所示。

      +--------+-------------+------------+
      | MY_PK  | FIRST_NAME  | LAST_NAME  |
      +--------+-------------+------------+
      | 100    | Jack        | Ben        |
      | 200    | Jack3       | Ben3       |
      +--------+-------------+------------+
    • 删除表

      DROP TABLE IF EXISTS example;

通过JDBC连接Phoenix

配置Maven依赖

<dependency>
     <groupId>org.apache.phoenix</groupId>
     <artifactId>phoenix-core</artifactId>
     <version>${phoenix.version}</version>
</dependency>

其中,${phoenix.version}须与集群中的Phoenix版本保持一致。

代码示例

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.Statement;

public class TestPhoenixJdbc {

    public static void main(String[] args) throws SQLException {
        Statement stmt = null;
        ResultSet rset = null;

        Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
        Connection con = DriverManager.getConnection("jdbc:phoenix:[zookeeper quorum hosts]");
        stmt = con.createStatement();

        stmt.executeUpdate("create table test (mykey integer not null primary key, mycolumn varchar)");
        stmt.executeUpdate("upsert into test values (1,'Hello')");
        stmt.executeUpdate("upsert into test values (2,'World!')");
        con.commit();

        PreparedStatement statement = con.prepareStatement("select * from test");
        rset = statement.executeQuery();
        while (rset.next()) {
            System.out.println(rset.getString("mycolumn"));
        }
        statement.close();
        con.close();
    }
}

通过JDBC连接开启KerberosPhoenix

若您的集群启用了Kerberos认证,以下内容将为您介绍如何通过Java JDBC编写客户端程序,以安全地连接至已启用Kerberos认证的EMR集群中的Phoenix服务。客户端程序将通过包含PrincipalKeytab信息的JDBC URL进行认证,并执行基本的DDLDML操作以验证连接。

步骤一:环境与凭证准备

在编写和运行代码之前,必须完成以下环境配置和Kerberos凭证创建工作。所有操作均在集群的Master节点上执行。

  1. 使用SSH方式连接集群Master节点,详情请参见登录集群

  2. 确定Kerberos域名 (REALM)。

    每个启用了Kerberos的集群都有一个唯一的REALM。

    执行以下命令,会返回REALM信息,您需要找到它并记录下来,以便在后续步骤中使用。

    cat /etc/krb5.conf | grep default_realm

    返回以下类似信息。

    default_realm = EMR.C-4FC5FDDE3759****.COM
  3. 创建客户端Principal。

    Principal是客户端在Kerberos系统中的唯一身份标识。我们需要为Java应用程序创建一个Principal。

    1. Master节点执行以下命令,使用kadmin.local工具。

      sudo kadmin.local
    2. kadmin.local交互界面中,执行以下命令以创建Principal。

      addprinc phoenix_client@EMR.C-4FC5FDDE3759****.COM

      执行命令后,系统将提示您为该Principal设置密码。请务必牢记该密码,尽管Keytab文件允许我们实现免密登录,但在某些情况下仍可能需要使用密码。

  4. 导出Keytab文件。

    1. kadmin.local工具中,请继续执行以下命令以导出Keytab文件。

      xst -k /tmp/phoenix_client.keytab phoenix_client@EMR.C-4FC5FDDE3759****.COM
    2. 执行以下命令,退出kadmin.local。

      exit
      重要
      • 权限: 确保运行Java程序的用户对该Keytab文件有读取权限。

      • 分发: 如果您的Java程序不在Master节点上运行,请务必将此phoenix_client.keytab文件和/etc/krb5.conf文件安全地分发到运行代码的机器上,并放置在脚本可以访问的路径下。

步骤二:编写并打包Java应用程序

  • 方式一:直接使用预编译JAR(快速验证)

    hbase-phoenix-kerberos-1.0-SNAPSHOT.jar

  • 方式二:手动编译与打包(生产推荐)

    您可以参考以下代码示例以进行手动编译与打包。

    核心代码示例PhoenixKerberosDemo.java

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    /**
     * 一个通过JDBC连接到Kerberos安全认证的Phoenix集群的客户端。
     * 所有的连接参数都通过一个从命令行传入的、完整的JDBC URL来提供。
     */
    public class PhoenixKerberosDemo {
    
        /**
         * 应用程序的主入口点。
         *
         * @param args 命令行参数。程序期望接收一个参数:完整的 Phoenix JDBC URL。
         */
        public static void main(String[] args) {
            // --- 1. 验证命令行输入:期望接收一个参数,即JDBC URL ---
            if (args.length != 1) {
                System.err.println("ERROR: Invalid number of arguments.");
                System.err.println("Usage: java PhoenixKerberosDemo \"<full_jdbc_url>\"");
                System.err.println("Example: \"jdbc:phoenix:zk1,zk2:2181:/hbase:user@REALM.COM:/path/to/user.keytab\"");
                System.exit(1); // 以错误码退出
            }
    
            String jdbcUrl = args[0];
    
            System.out.println("Attempting to connect to Phoenix...");
            System.out.println("Using JDBC URL: " + jdbcUrl);
    
            try {
                // --- 2. 加载 Phoenix 驱动 ---
                Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
            } catch (ClassNotFoundException e) {
                System.err.println("FATAL ERROR: Phoenix JDBC driver not found in the classpath.");
                e.printStackTrace();
                System.exit(1);
            }
    
            // --- 3. 使用 try-with-resources 语句建立连接并执行SQL,该语法会自动关闭资源 ---
            try (Connection con = DriverManager.getConnection(jdbcUrl);
                 Statement stmt = con.createStatement()) {
    
                System.out.println("Connection established successfully.");
    
                final String tableName = "TEST";
                System.out.println("Creating table '" + tableName + "'...");
                stmt.executeUpdate("CREATE TABLE IF NOT EXISTS " + tableName + " (mykey INTEGER NOT NULL PRIMARY KEY, mycolumn VARCHAR)");
                con.commit();
    
                System.out.println("Upserting data...");
                stmt.executeUpdate("UPSERT INTO " + tableName + " VALUES (1, 'Hello')");
                stmt.executeUpdate("UPSERT INTO " + tableName + " VALUES (2, 'World-Kerberos!')");
                con.commit();
                System.out.println("Data upserted successfully.");
    
                String sql = "SELECT * FROM " + tableName;
                System.out.println("Querying for results with: " + sql);
    
                try (PreparedStatement statement = con.prepareStatement(sql);
                     ResultSet rset = statement.executeQuery()) {
    
                    System.out.println("Query results:");
                    while (rset.next()) {
                        System.out.println(rset.getInt("mykey") + " -> " + rset.getString("mycolumn"));
                    }
                }
    
                System.out.println("Cleaning up the test table...");
                stmt.executeUpdate("DROP TABLE IF EXISTS " + tableName);
                con.commit();
    
            } catch (SQLException e) {
                // 捕获SQL异常,并给出有帮助的排查提示
                System.err.println("\n--- FAILED TO EXECUTE DATABASE OPERATION ---");
                System.err.println("Please check the following:");
                System.err.println("1. The JDBC URL is correct (format, principal, keytab path).");
                System.err.println("2. Network connectivity to ZooKeeper and HBase.");
                System.err.println("3. The keytab file exists and has correct read permissions.");
                System.err.println("4. The principal has sufficient permissions on HBase tables and namespaces.");
                e.printStackTrace();
            }
    
            System.out.println("\nExecution finished.");
        }
    }
    

    Maven配置(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/maven-v4_0_0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.aliyun.emr.doctor</groupId>
        <artifactId>hbase-phoenix-kerberos</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>Archetype - hbase-phoenix-kerberos</name>
        <url>http://maven.apache.org</url>
        <properties>
            <phoenix.version>5.2.1</phoenix.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.apache.phoenix</groupId>
                <artifactId>phoenix-core</artifactId>
                <version>${phoenix.version}</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <!--  Java Compiler  -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>3.2.4</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <filters>
                                    <filter>
                                        <artifact>*:*</artifact>
                                        <excludes>
                                            <exclude>META-INF/*.SF</exclude>
                                            <exclude>META-INF/*.DSA</exclude>
                                            <exclude>META-INF/*.RSA</exclude>
                                        </excludes>
                                    </filter>
                                </filters>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    </project>

步骤三:运行应用程序

  1. 运行环境选择。

    在运行应用程序时,您可以选择以下两种运行环境:

    • 在集群节点上运行(推荐):

      • 集群节点已预装所有必要的Hadoop、HBasePhoenix依赖库,无需额外配置。网络连通性无需额外设置,环境完整且稳定。

      • 适用场景:快速验证和测试。开发调试阶段。

    • 在集群外运行

      如果需要在集群外部运行程序,请确保满足以下条件:

      • 网络互通:确保运行机器与集群的ZooKeeper、HBase MasterRegionServer网络互通。

      • Kerberos配置: 将集群的krb5.conf文件和生成的keytab文件复制到运行机器上。

      • 依赖管理: 运行命令的classpath中必须包含所有必需的Hadoop、HBasePhoenix客户端依赖JAR包。这通常比在集群节点上运行复杂得多,建议使用工具(如MavenGradle)管理依赖。

  2. 执行脚本。

    以下是一个整合了所有必要配置的kerberos-phoenix.sh脚本,便于进行修改和执行。

    #!/bin/bash
    
    # ======================= 1. 用户配置区 (请根据您的环境修改) =======================
    
    # HadoopHBase的配置文件所在目录
    HADOOP_CONF_DIR="/etc/taihao-apps/hadoop-conf"
    HBASE_CONF_DIR="/etc/taihao-apps/hbase-conf"
    
    # Phoenix客户端JAR包的路径。使用符号链接是最佳实践,能抵抗版本变化。
    # 请先通过 `ls -l /opt/apps/PHOENIX/phoenix-current/` 确认此文件存在。不同的版本这个位置需要修改。
    PHOENIX_JAR="/opt/apps/PHOENIX/phoenix-current/phoenix-client-lite-hbase-2.6.jar"
    
    # 您的应用程序JAR包文件名。
    YOUR_JAR_FILE="hbase-phoenix-kerberos-1.0-SNAPSHOT.jar"
    
    # Kerberos配置文件路径。
    KRB5_CONF_PATH="/etc/krb5.conf"
    
    # --- [核心] JDBC URL配置 ---
    # 格式:jdbc:phoenix:[ZK地址]:[ZK端口]:[HBase ZNode]:[Principal]:[Keytab绝对路径]
    # 请将下面的ZK地址,REALM,Keytab路径替换为您的真实信息。
    ZK_QUORUM="master-1-1" # 如果有多个ZK,用逗号分隔,如 "zk1,zk2,zk3"
    ZK_PORT="2181"
    HBASE_ZNODE="/hbase" # 如果是安全集群,可能是/hbase-secure
    PRINCIPAL="phoenix_client@EMR.C-4FC5FDDE3759****.COM" # 替换为您的Principal
    KEYTAB_PATH="/tmp/phoenix_client.keytab" # Keytab文件的绝对路径
    
    JDBC_URL="jdbc:phoenix:${ZK_QUORUM}:${ZK_PORT}:${HBASE_ZNODE}:${PRINCIPAL}:${KEYTAB_PATH}"
    # =================================================================================
    
    # ======================= 2. 执行区 (一般无需修改) =================================
    echo "================================================="
    echo "Starting Phoenix Kerberos JDBC Demo..."
    echo "Using JDBC URL: ${JDBC_URL}"
    echo "================================================="
    
    # 构建 Classpath。顺序:当前目录 -> 配置目录 -> 你的JAR -> 依赖JAR
    # `hbase classpath` 会自动加载Hadoop/HBase的核心依赖
    CLASS_PATH=".:${HADOOP_CONF_DIR}:${HBASE_CONF_DIR}:${YOUR_JAR_FILE}:${PHOENIX_JAR}:$(hbase classpath)"
    
    # 执行 Java 程序
    java -cp "${CLASS_PATH}" \
         -Djava.security.krb5.conf="${KRB5_CONF_PATH}" \
         PhoenixKerberosDemo "${JDBC_URL}"
    
    # 检查退出码
    if [ $? -eq 0 ]; then
        echo -e "\n[SUCCESS] Program finished successfully."
    else
        echo -e "\n[FAILED] Program terminated with an error."
    fi
    # =================================================================================
    1. 步骤二打包好的JAR包以及kerberos-phoenix.sh上传至Master节点的指定目录。

    2. 执行以下命令,赋予脚本执行权限。

      chmod +x kerberos-phoenix.sh
    3. 执行以下命令,运行脚本。

      ./kerberos-phoenix.sh

      部分返回信息如下所示。

      image

相关文档

Phoenix更多信息可以参见官方文档: