【HBase之轨迹】(3)Apache Phoenix 5.1.2 详细使用,像 MySQL 一样使用 SQL 语句操作 HBase(Shell 操作和 SpringBoot+MyBatis 整合)


0. 前置

前一篇记录了 HBase 的 Shell 操作和 JavaAPI 操作,但操作过程还是较为繁琐
【HBase之轨迹】(2)使用 hbase命令 和 JavaAPI 操作 HBase(包括复杂查询–过滤器,HBase 工具类)

如果能像使用 MySQL 一样编写 SQL 操作数据库就好了,而 Phoenix 正是提供了这样一种支持

本篇将记录 Phoenix 的安装配置过程,Shell 使用和使用 SpringBoot+MyBatis 整合 Phoenix
配置过程中的一些常见错误已经在文中指出,有如 Phoenix 无法启动、缺少依赖、Command line too long、如何映射实体类等
避免走笔者已经走过的弯路,如果还发现新的问题欢迎留言


1. Apache Phoenix 简介

Apache Phoenix 是给予 HBase 的一个 SQL 引擎,通过提供 Jar 包拓展 HBase 的功能
支持使用 SQL 语句对 HBase 进行操作,底层是将 SQL 语言转化为 HBase 原生语句(如 scan get put 等)
同时还支持二级索引,底层基于 HBase 的协处理器,可以提升 HBase 的查询效率

Phoenix 作为一个数据源,在 Java 项目中的使用和 MySQL 一样,可以用 JDBC 进行连接,也可以使用 MyBatis 框架进行整合,像正常关系型数据库一样操作


2. 下载配置与启动

① 下载安装包

首先到官方下载页面:https://phoenix.apache.org/download.html
选择对应 HBase 版本的 Phoenix 版本进行下载
本文 Hbase 版本为 2.3.7,故选择如下:
在这里插入图片描述
在这里插入图片描述
进去第一各连接点击即可下载安装包,之后用 xftp 上传到服务器
或直接在服务器使用 wget https://... 下载

② 将 Phoenix 的 jar 包拷贝到 HBase lib 目录下

由于 Phoenix 只是作为 HBase 的拓展,而不是作为一个单独的程序
所以只需要将下载的一系列 jar 包拷贝到 HBase 的 lib 目录下,再简单配置一下就可以使用了

解压安装包后,可以先拷贝到一个服务器,再进行分发
▲ 注意:这里只拷贝 phoenix-server-hbase-2.3-5.1.2.jarphoenix-pherf-5.1.2.jar 两个就够了,把客户端也拷贝进去可能出大问题(我就被搞了可恶啊)

cp phoenix-server-hbase-2.3-5.1.2.jar /.../hbase-2.3.7/lib
cp phoenix-pherf-5.1.2.jar /.../hbase-2.3.7/lib
xsync /.../hbase-2.3.7/lib

③ 为 HBase 配置 Phoenix

修改 HBase 的 hbase-site.xml 配置文件,加上下列几个配置

<!-- 支持 HBase命名空间映射 -->
<property>
    <name>phoenix.schema.isNamespaceMappingEnabled</name>
    <value>true</value>
</property>

<!-- 支持索引预写日志编码 -->
<property>
	<name>hbase.regionserver.wal.codec</name>
	<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>

然后分发到各台服务器
如果是从前面用 docker 搭建集群来的,就不能直接分发了,因为每个容器的端口设置都不一样,如 16100 16101 16102 等,这就需要一个一个修改了

▲ 同时非常非常非常重要的
不仅仅需要修改 HBase 的 hbase-site.xml 文件
还需要修改 Phoenix bin 目录下的 hbase-site.xml 文件
Phoenix 目录下的原配置文件中只有 hbase.regionserver.wal.codec 一个配置
还需要和上边一样加上另一个配置,总之保证 Phoenix 自身的 hbase-site.xml 文件也要又以上两个配置

忘记加的后果:
启动 Phoenix sqlline.py 客户端时,卡住无反应
最后结果为 Phoenix 启动超时,HBase 的三个 HRegionServer 全部挂掉
查看 HBase logs 下的 Region 日志,可发现错误:
org.apache.phoenix.coprocessor.MetaDataEndpointImpl cannot be cast to com.google.protobuf.Service

配置完之后,重启 HBase(stop + start)

④ 启动 Phoenix

进入到 Phoenix 安装目录下的 bin 目录,执行如下

# 示例:./sqlline.py hadoop001:2181
./sqlline.py <master 服务器 IP 地址>:<Zookeeper 端口>

会进入到以下界面,先使用 !table 查看表
在这里插入图片描述
上面这些表都是 Phoenix 自动帮我们创建的,能看见就说明启动成功了
同时在 Web 端也能够看到:
在这里插入图片描述


3. Phoenix 命令行使用

① DDL 表的增删改查

(1)—— 创建表

▲ 注意:原 HBase 中的表不会自动映射到 Phoenix 中去,并不会在 Phoenix 中显示出来
但在 Phoenix 中创建相同名字的表,原表数据会导入到表中相应的列(列名一模一样),可以看到原先的数据

其中必须有一项设置为 PRIMARY KEY,表示该项作为 ROWKEY
且在 Phoenix 中,所有名字都会被自动转化成大写,如果需要保留小写,则需加上双引号
语法如下:

CREATE TABLE IF NOT EXISTS 表名 (
	ROWKEY名称  数据类型  PRIMARY KEY,
	列族名.列名  数据类型  NOT NULL,
	列族名.列名  数据类型,
	...
);
# 下面为可选设置,直接加在后边即可
CONPRESSION='xxx'(可以指定数据压缩方式,如 GZ)
SPLIT ON ('5', '10', '15') (预分区设置方法1,对于 rowkey 使用直接划分)
SALT_BUCKETS=10  (预分区设置方法2,直接设置 n 各预分区,这里是 10 个)

建表示例(直接客户端命令行运行)

CREATE TABLE IF NOT EXISTS "stardust" (
    "id"            VARCHAR PRIMARY KEY,
    "info"."name"   VARCHAR(20),
    "info"."kind"   VARCHAR(20),
    "info"."belong" VARCHAR(20),
    "info"."x"      VARCHAR(20),
    "info"."y"      VARCHAR(20),
    "info"."width"  VARCHAR(20),
    "info"."height" VARCHAR(20),
    "info"."store"  VARCHAR(255),
    "info"."modify" VARCHAR(255),
    "info"."check"  VARCHAR(255),
    "info"."clock"  VARCHAR(255)
)
CONPRESSION='GZ', SALT_BUCKETS=10;

再次查看表 !table ,发现创建成功且表名为小写
在这里插入图片描述
(2)—— 查看表结构

!desc <表名>

使用 !desc "stardust"(注意小写的要加双引号),结果如下:
在这里插入图片描述
(3)—— 删除表

drop table if exists "stardust";

② DML 表数据的增删改查

(1)—— 更改表数据

在 HBase 中,新增数据和更新数据都是使用 put
而在 Phoenix 中,则都使用 upsert(insert + update)

▲ 注意:
如果想赋值字符串,需要使用单引号而不是双引号,双引号专门用于表名,字段名
且每次更改数据必须带上主键,否则不会报错

upsert into 表名(列族.列名, ...) values(, ...)
示例:upsert into "stardust"("id", "info"."name") values('1001', 'test')

(2)—— 删除表数据

删除数据和标准 sql 一样

delete from "stardust" where "info"."name" = 'test'

(3)—— 查询表数据

查询语句也和标准 sql 一样,查全表如下:

select * from "stardust";

+------+------+------+--------+---+---+-------+--------+-------+--------+-------+-------+
|  id  | name | kind | belong | x | y | width | height | store | modify | check | clock |
+------+------+------+--------+---+---+-------+--------+-------+--------+-------+-------+
| 1001 | test |      |        |   |   |       |        |       |        |       |       |
+------+------+------+--------+---+---+-------+--------+-------+--------+-------+-------+

但是这里注意的是,Phoenix 插入的数据
和原生 HBase 的对应关系是不一样的,后续会讨论这个问题

在 hbase shell 客户端查到的数据:
 \x80\x00\x03\xE9	column=info:\x00\x00\x00\x00, timestamp=2022-02-25T16:06:42.472, value=x                      
 \x80\x00\x03\xE9	column=info:\x80\x0B, timestamp=2022-02-25T16:06:42.472, value=test

在 Java 中查到的数据:
info:     => x
info:� => test
# 分页查询
select * from "stardust" limit <每页数量> offset <偏移量>

③ 视图的使用

上面提到过,HBase 中原先有的数据,是不会自动在 Phoenix 中显示出来的
那么对于原有的数据,怎么才能映射到 Phoenix 中参与操作呢,这里就要使用视图了

(1)—— 创建视图

可以为原 HBase 中的表创建视图,也可以为 Phoenix 中的表创建
创建的要求是,表名和列名必须一一对应,语法如下:

create view if not exists <命名空间>.<表名> (
	ROWKEY名称  数据类型  PRIMARY KEY,
	列族名.列名  数据类型  NOT NULL,
	列族名.列名  数据类型,
	...
);

如果名字存在小写,也都需要使用双引号括起来
创建成功后,就可以使用 Phoenix 语法操作视图了
操作方法和上边的 DML 完全一样


4. Phoenix + SpringBoot + MyBatis 整合与使用

前边简介提到了,Phoenix 在 Java 中作为数据源存在,可以使用 JDBC 进行连接与操作
这里就直接使用 SpringBoot+MyBatis 进行整合,和整合 MySQL 的步骤相似,只不过需要多做一些操作(还有一些坑)

① 所需依赖

这里边使用的 HBase 和 Phoenix 的版本要和服务器上的对应上

▲ 踩坑点1:依赖不全
而且可能由于是 Phoenix 版本太新,导致 maven 上一些依赖找不到
所以在只导 phoenix-core 的情况下,会出现某些类不存在的报错从而启动失败,需要加上 phoenix-hbase-compat
如果读者是其他更低的版本,可以试试只导入 phoenix-core
▲ 踩坑点2:启动失败
这里还有一个问题(可能也是 Phoenix 版本太新的锅)
在引入 Phoenix 依赖后,启动项目时会提示 Command line is too long 导致项目无法启动
起初以为是配置出错了,后来才发现只是启动命令太长了并无大碍,修改 IDEA 的配置就行
配置位置:Run → Edit Configurations... → Configuration → Environment → Short command line
将其的值勾选为 JAR manifest 即可解决该问题

在这里插入图片描述
全部依赖:

<!-- HBase -->
<dependency>
    <groupId>org.apache.hbase</groupId>
    <artifactId>hbase-client</artifactId>
    <version>2.3.7</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </exclusion>
        <exclusion>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- phoenix -->
<dependency>
    <groupId>org.apache.phoenix</groupId>
    <artifactId>phoenix-core</artifactId>
    <version>5.1.2</version>
    <exclusions>
        <exclusion>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.el</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!-- phoenix-hbase-compat -->
<dependency>
    <groupId>org.apache.phoenix</groupId>
    <artifactId>phoenix-hbase-compat-2.4.1</artifactId>
    <version>5.1.2</version>
</dependency>

② 加入 hbase-site.xml 配置

该配置非常重要,在使用 Phoenix 的地方一定要有它
一开始就是忘记配置了,导致一直报莫名其妙的错,最后是在看到报错说没有设置 isNamespaceMappingEnabled 为 true 时,才醒悟过来是少了一个配置文件(其实在上边 shell 使用 phoenix 时也需要注意这个问题,就是 phoenix 目录下的 hbase-site.xml 一定要维护好)

配置文件命名为 hbase-site.xml,放置在项目的 resource 目录下:

<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>hbase.regionserver.wal.codec</name>
        <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
    </property>

    <!-- 支持 HBase命名空间映射 -->
    <property>
        <name>phoenix.schema.isNamespaceMappingEnabled</name>
        <value>true</value>
    </property>
</configuration>

③ 整合 SpringBoot 配置

application.yml 配置文件中,需要加入 MyBatis 和 Phoenix 配置,如下:
其中 Phoenix 的 url 格式为:jdbc:phoenix:zookeeper,zookeeper 需要填入服务器 IP 和对应的端口号
如果有多台 zookeeper 服务器,端口只需要写一个(下边演示有多台的情况)
最后 Phoenix 的 username 和 password 默认都为空,可以不填

# 驼峰命名,可选
mybatis:
  configuration:
    map-underscore-to-camel-case: true

# Phoenix 配置,必须
spring:
  datasource:
    driver-class-name: org.apache.phoenix.jdbc.PhoenixDriver
    url: jdbc:phoenix:hadoop001,hadoop002,hadoop003:2181
    username:
    password:

④ 简单使用

配置完毕之后,就可以将 Phoenix 当成 MySQL 看了,下边的操作和普通整合 MySQL 一样

创建 Mapper 接口
这里演示了查询和插入操作,其他操作和上边提到的 sql 操作一样
依旧需要注意,如果表名是小写的,需要加上双引号,而这里本身在双引号中,故需加上转义字符

@Mapper
public interface PhoenixMapper {
    @Select("select * from \"stardust\" limit 1 offset 1")
    Phoenix getAll();

    @Insert("upsert into \"stardust\"(\"id\", \"info\".\"name\") values('1001', 'test')")
    boolean insert();
}
▲ 查询特别注意
在为 Phoenix 表创建实体类时,发现有多个列族时,实体类字段无法和表中字段映射
主要原因还是不知道如何在实体类中表示列族,下面给出解决思路:

当使用 SQL 语句查询 Phoenix 表时,如果只有一个列族
那么 select * 可以将所有有数据的列查询出来,而且不显示列族名,如下为 selectAll 的查询结果
{"name": "test", "id": "1001"} 可以看到插入时的两个列值都查出来了,而没有插入列不显示

但如果是多列族的,会发现查询出来的只有 id,这时我们要查询时,就必须使用字段全称进行查询了,如:
select "info"."name", "struct"."x" from "stardust"(假设有列 struct:x)那么此时的查询结果如下:
{"name": "test", "x": "200px"} 可以发现列族名并没有显示出来
所以如果要以实体类接收的话,属性名只需要写 name 和 x,不需要加上列族名!

当然了,如果不同列族下有列名相同的,实在没办法就可以为查询的列重命名,比如:
select "info"."name" as infoName, "struct"."name" as structName from "stardust"
这样,实体类中就可以使用 infoName 和 structName 进行接收拉~

创建实体类:
可以看到,这里实体类中省略了列族名,因为查询结果中的字段名是没有列族名的,所以这里不需要写入列族名

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Phoenix {
    private String id;
    private String name;
    private String kind;
    private String belong;
    private String x;
    private String y;
    private String width;
    private String height;
    private String store;
    private String modify;
    private String check;
    private String clock;
}

之后在其他地方使用就可以了,简单示例:

@Autowired
PhoenixMapper phoenixMapper;

@GetMapping("/getAll")
public Object getAll() {
    System.out.println("插入数据:" + phoenixMapper.insert());
    return phoenixMapper.getAll();
}

5. Phoenix 索引

默认 Phoenix 的 rowkey 已经有索引了,但也可以为其他列添加索引,即二级索引
Phoenix 的二级索引有:全局索引、本地索引、覆盖索引和函数索引

① 全局索引

  • 创建全局索引的本质上是在 HBase 中创建一张索引表,而全局索引就是指这些表分布在 HBase 的各个 Region 中,和数据所在的位置无直接联系
  • 适用于读多写少的业务,因为在写入时,需要维护各台服务器各个 Region 的索引表,开销较大。而在读取时,Phoenix 只需选择需要的索引表,就能快速查询出数据了
  • 注意:如果在 select 语句中引用了其他非索引列,索引将失效
  • 一般情况下全局索引会搭配覆盖索引使用,将需要查询的列放入索引中,保证索引生效,但写入效率会受到影响
CREATE INDEX <索引名称> ON <表名> (列名1, 列名2, 列名3...)

② 本地索引

  • 本地索引和全局索引的区别就在于,本地索引不创建索引表,而是直接将索引创建在原数据表中,这会使查询时效率更高,因为不需要在查到 rowkey 后,继续往其他 Region 上查询需要的数据
  • 但创建本地索引时,就不能再使用原生的 HBase API 查询数据了,因为 Phoenix 对该表进行了修改(称为影子索引),所以原数据不能直接显示出来了,只能通过 Phoenix API 进行查询
  • 当然,如果删除了本地索引,那 HBase API 就又可以查询到原数据了
  • 注意:当使用 SALT_BUCKET 创建表时,本地索引将失效
  • 注意:即时在 select 语句中引用了非索引字段,索引也会生效(区别于全局索引)
CREATE local INDEX <索引名称> ON <表名> (列名1, 列名2, 列名3...)

③ 覆盖索引

覆盖索引并不是单独的索引,而是指将需要查询的字段绑定到其他索引中(如全局索引、本地索引),使得在使用索引查询数据时,能顺便将需要的数据查询出来而不需要回表

CREATE INDEX <索引名称> ON <表名> (列名1, 列名2, 列名3...) INCLUDE(列名4, 列名5...)

④ 函数索引

函数索引是指基于任意表达式创建的索引,这里笔者了解不多就不多说了 >·<

CREATE INDEX <索引名称> ON <表名> (<函数名>())

6. 后置

一点点思考:
其实在学习 Phoenix 之前,我非常期待它如何将 HBase 中动态增加列的特性引入到 sql 语句中,又是如何将动态的列使用实体类接收的。一开始我想了挺多,可能是将实体类用一个像 Map 一样的东西来接收(毕竟每一行数据,可以写成 key 为 String,Value 为 Map 的大 Map,列族名作为 key,而列名和值存放到小 Map 中作为大 Map 的 Value)

但实际情况却是,使用 Phoenix 后,HBase 动态增加列的特性没了,虽然它依旧能支持动态列但是成本实在是太高,这点有点失望(一开始对 HBase 的兴趣很大一部分来自于它的可动态增加列)

虽然 Phoenix 往大方面讲也有很多优势,官网也说了很多。但确实在使用了 Phoenix 之后,很多操作变得有些不方便了,如不能像 MySQL 一样可视化查看表中数据,无法直接在 IDEA 中修改数据,数据在 HBase 和 Phoenix 之间的转化很可能出问题等等,可能是笔者学的还不够深,但目前看来还算是比较鸡肋的

下一篇就是总集篇了,会将 HBase 的工作原理、底层架构等重新梳理一遍,然后按时序整合这几篇文章


红绿深丛,布满蓝色印记,那是旅者蹒跚(IceClean)

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寒冰小澈IceClean

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值