由于blog各种垃圾评论太多,而且本人审核评论周期较长,所以懒得管理评论了,就把评论功能关闭,有问题可以直接qq骚扰我

Hbase整合Phoenix之介绍部署和使用

大数据 西门飞冰 3635℃
[隐藏]

1.Phoenix定义

Phoenix 是HBASE的一个加分项,往往一些考虑使用HBASE的场景还是因为有着Phoenix的加持。如果只是单纯的考虑把数据存到HBASE,然后做一些简单的查询,Hbase一定是可以满足的。但是要对HBASE的数据做一些分析,这个时候HBASE 就出现问题了,因为HBASE本身没有分析的能力,HBASE只能做基于rowkey的简单查询,要通过HBASE做分析和统计是很难的。

我们要是既想通过HBASE存,又想通过HBASE分析,那么Phoenix就是一个必不可少的组件,它可以帮助HBASE扩展不少功能,带来很多新的特性,比如让HBASE具有分析能力,而且Phoenix使用起来也比较简单。

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

优点:使用简单,直接能写sql,降低了HBASE的使用门槛。

缺点:因为多了一个中间层进行封装,所以效率没有自己设计rowKey时使用API高,性能较差。

官网地址:https://phoenix.apache.org/

2.Phoenix架构

从Phoenix的架构图可以比较直观的体现出来,它就是一个客户端而已,底层还是HBASE。Phoenix客户端分为两种:

瘦客户端:将用户写好的sql转交给Phoenix Query服务,由Phoenix Query服务把sql解析成对应的HBASE操作,并交给HBASE执行,执行完成之后并负责把结果收集回来,并转换成二维表格返回给用户。Phoenix Query服务只针对瘦客户端。Phoenix Query服务需要放到Hbase的RegionServer里面。

胖客户端:不需要依赖Phoenix Query服务,客户端本身就可以完成sql的解析和提交操作(连接zookeeper)。

两个客户端在实际用起来的时候,没有区别,但是建议用胖客户端,毕竟少了一个需要维护的服务。

image-20221226203001445

3.Phoenix安装

环境说明:

Hbase Version:2.0.5

Phoenix Version:5.0.0

(1)下载并解压安装包

wget http://mirror.bit.edu.cn/apache/phoenix/apache-phoenix-5.0.0-HBase-2.0/bin/apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz
tar xf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /opt/module/
cd /opt/module/
mv apache-phoenix-5.0.0-HBase-2.0-bin/ phoenix

(2)配置环境变量

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

(3)复制相关jar包到hbase/lib下

cp phoenix-5.0.0-HBase-2.0-server.jar /opt/module/hbase/lib/

(4)重启Hbase

(5)连接Phoenix

瘦客户端:

1、启动queryserver

/opt/module/phoenix/bin/queryserver.py start

2、执行客户端连接命令

/opt/module/phoenix/bin/sqlline-thin.py 

胖客户端(需要连接Hbase的zookeeper):

/opt/module/phoenix/bin/sqlline.py hadoop01:2181

4.shell 操作Phoenix

如下命令是shell客户端可以进行的所有操作

0: jdbc:phoenix:thin:url=http://localhost:876> !
!quit            !done            !exit            !connect         !open            !describe        !indexes         !primarykeys     !exportedkeys    !manual          !importedkeys    !procedures      
!tables          !typeinfo        !columns         !reconnect       !dropall         !history         !metadata        !nativesql       !dbinfo          !rehash          !verbose         !run             
!batch           !list            !all             !go              !#               !script          !record          !brief           !close           !closeall        !isolation       !outputformat    
!autocommit      !commit          !properties      !rollback        !help            !?               !set             !save            !scan            !sql             !call    

1、创建schema 对应Hbase中的namespace

使用Phoenix创建schema需要额外在Hbase中添加一个配置(重启Hbase生效)

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

还需要在Phoenix添加对应的配置

vim /opt/module/phoenix/bin/hbase-site.xml 
<property>
    <name>phoenix.schema.isNamespaceMappingEnabled</name>
    <value>true</value>
</property>

在phoenix中,创建schema和表等操作会自动转换为大写,若要小写,使用双引号,如”us_population”。

phoenix创建schema命令:

0: jdbc:phoenix:hadoop01:2181> create schema "mydb";

验证:phoenix创建schema后,hbase出现了同名的namespace说明创建成功

hbase(main):002:0> list_namespace
NAMESPACE                                                                                                                                                                                                             
SYSTEM                                                                                                                                                                                                                
default                                                                                                                                                                                                               
hbase                                                                                                                                                                                                                 
mydb   

2、创建表:

在Phoenix创建表一定要有一个主键,这个主键是和hbase的rowkey做对应的。

create table "emp"(
	id varchar(20) primary key,	// 主键对应hbase的rowkey
	name varchar(20),
	addr varchar(20)
);

指定多个列的联合作为rowkey(列族)

create table "us_population" (
	state char(2) not null,
	city varchar not null,
	population bigint
constraint my_pk primary key (state, city));

3、插入数据(phoenix提供了upsert支持,相当于update+insert)

upsert into "emp" values('1001','zhangsan','beijing');

4、查询记录

0: jdbc:phoenix:hadoop01:2181> select * from "emp";
+-------+-----------+----------+
|  ID   |    AME    |   ADDR   |
+-------+-----------+----------+
| 1001  | zhangsan  | beijing  |
+-------+-----------+----------+
1 row selected (0.087 seconds)
0: jdbc:phoenix:hadoop01:2181> select * from "emp" where id = '1001';
+-------+-----------+----------+
|  ID   |    AME    |   ADDR   |
+-------+-----------+----------+
| 1001  | zhangsan  | beijing  |
+-------+-----------+----------+

Hbase中查询对应数据

hbase(main):009:0> scan 'mydb:emp''
ROW                                 COLUMN+CELL                                                                                                                                                    
 1001                               column=0:\x00\x00\x00\x00, timestamp=1672062328984, value=x                                                                                                    
 1001                               column=0:\x80\x0B, timestamp=1672062328984, value=zhangsan                                                                                                     
 1001                               column=0:\x80\x0C, timestamp=1672062328984, value=beijing  

特别说明:

column后面的0是列族,因为列族是冗余存储的,所以要尽可能的简洁一些

\x00\x00\x00\x00 这些是列名,为了减少数据对磁盘空间的占用,Phoenix默认会对HBase中的列名做编码处理。具体规则可参考官网链接:https://phoenix.apache.org/columnencoding.html,若不想对列名编码,可在建表语句末尾加上COLUMN_ENCODED_BYTES = 0;

第一行数据的value=x,这个没有任何含义,主要是为了保证rowkey的存在

5、删除记录

delete from "emp" where id = '1001';

Hbase 中验证对应的数据删除

hbase(main):010:0> scan 'mydb:emp',{RAW=>true,VERSIONS=>3}
ROW                                                    COLUMN+CELL                                                                                                                                                    
 1001                                                  column=0:, timestamp=1672061971367, type=DeleteFamily                                                                                                          
 1001                                                  column=0:\x00\x00\x00\x00, timestamp=1672061865458, value=x                                                                                                    
 1001                                                  column=0:\x80\x0B, timestamp=1672061865458, value=zhangsan                                                                                                     
 1001                                                  column=0:\x80\x0C, timestamp=1672061865458, value=beijing 

5.数字编码问题

创建一个测试表,并插入一行数据

create table emp2(
		id varchar(20) primary key,
		name varchar(20),
		addr varchar(20),
		salary bigint
)
COLUMN_ENCODED_BYTES = 0;

upsert into emp2 (id, name, addr, salary) values('1001','zhangsan','beijing',123456);

数据在phoenix显示是正常的

0: jdbc:phoenix:hadoop01:2181> select * from emp2;
+-------+-----------+----------+---------+
|  ID   |   NAME    |   ADDR   | SALARY  |
+-------+-----------+----------+---------+
| 1001  | zhangsan  | beijing  | 123456  |
+-------+-----------+----------+---------+

但是SALARY字段数据在Hbase存储却被编码了,这是因为底层数据都是通过字节存储的,scan的时候需要显示成可以看懂的数据,phoenix在转的时候使用的是toString字符串的格式,这样把底层的数字(long类型)转换成字符串肯定是看不懂的错误数据。

image-20221226231128660

要显示成正确的格式,需要在scan的时候在对应列添加指定配置的格式为long类型,但是这个时候格式转换正确了,但是结果错误了

image-20221226232728591

但是通过HBASE写入的数据是正常的

image-20221226233428041

通过phoenix客户端查询,Hbase写入的数据结果不正确

image-20221226233626860

那么现在问题来了,通过phoenix和Hbase写入的数据两边识别结果互相错误,怎么回事?怎么解决?

这个问题就涉及到了0和1谁大的问题,在数学中,1一定大于0,但是在二进制中0一定大于1,因为二进制中0是正数,1是负数。就是因为在0和1谁大的问题上,phoenix和hbase各自认为的观点不同,所以才导致了这个问题。

解决办法:phoenix和Hbase不能穿插进行操作,用一个就行了。要是之前在Hbase就存了数据需要通过phoenix进行查询,就需要在phoenix自己写函数了,通过函数的方式进行转换。

6.JDBC操作

注意:瘦/胖客户端需要分开写,因为有依赖冲突,同时只能用一个。

6.1.瘦客户端

(1) 导入Maven依赖

        <dependency>
            <groupId>org.apache.phoenix</groupId>
            <artifactId>phoenix-core</artifactId>
            <version>5.0.0-HBase-2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.glassfish</groupId>
                    <artifactId>javax.el</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.hadoop</groupId>
                    <artifactId>hadoop-common</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.el</artifactId>
            <version>3.0.1-b06</version>
        </dependency>

        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.8.4</version>
        </dependency>

(2)编写代码

public class PhoenixThinClient {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        testSelect();
    }

    public static void testSelect() throws ClassNotFoundException, SQLException {
        // 注册驱动
        Class.forName("org.apache.phoenix.queryserver.client.Driver");
        // 获取连接
        String url = "jdbc:phoenix:thin:url=http://hadoop01:8765;serialization=PROTOBUF";
        Connection connection = DriverManager.getConnection(url);
        // 编写sql
        String sql = "select id , name , addr from mydb.emp where id = ?" ;
        // 预编译sql
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 设置参数
        preparedStatement.setString(1,"1001");
        // 执行SQL
        ResultSet resultSet = preparedStatement.executeQuery();
        // 处理结果
        if (resultSet.next()){
            String id = resultSet.getString("id");
            String name = resultSet.getString("name");
            String addr = resultSet.getString("addr");
            System.out.println(id + ":" + name + ":" + addr);
        }
        // 关闭连接
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

6.2.胖客户端

(1)导入Maven依赖

    <dependency>
        <groupId>org.apache.phoenix</groupId>
        <artifactId>phoenix-core</artifactId>
        <version>5.0.0-HBase-2.0</version>
        <exclusions>
            <exclusion>
                <groupId>org.glassfish</groupId>
                <artifactId>javax.el</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.apache.hadoop</groupId>
                <artifactId>hadoop-common</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>javax.el</artifactId>
        <version>3.0.1-b06</version>
    </dependency>

    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-common</artifactId>
        <version>2.8.4</version>
    </dependency>

(2)编写代码

public class PhoenixThickClient {
    public static void main(String[] args) throws Exception {
        testSelect();
    }

    //查询数据
    public static void testSelect() throws Exception {
        // 注册驱动
        Class.forName("org.apache.phoenix.jdbc.PhoenixDriver");
        // 获取连接
        String url = "jdbc:phoenix:hadoop01:2181" ;
        Properties properties = new Properties();
        properties.put("phoenix.schema.isNamespaceMappingEnabled" , "true") ;
        Connection connection = DriverManager.getConnection(url ,properties);
        // 编写sql
        String sql = "select id , name , addr from mydb.emp where id = ?" ;
        // 预编译参数
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 设置参数
        preparedStatement.setString(1, "1001");
        // 执行sql
        ResultSet resultSet = preparedStatement.executeQuery();
        // 处理结果
        if(resultSet.next()){
            String id = resultSet.getString("id");
            String name = resultSet.getString("name");
            String addr = resultSet.getString("addr");
            System.out.println(id + " : " + name + " : " + addr) ;
        }
        // 关闭连接
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

转载请注明:西门飞冰的博客 » Hbase整合Phoenix之介绍部署和使用

喜欢 (0)or分享 (0)