一、简介、概述
![](https://img.haomeiwen.com/i13587608/27fb754d6a5b98ca.png)
Phoenix官方网址:https://phoenix.apache.org/
安装包下载:https://phoenix.apache.org/download.html
Phoenix 最早是 saleforce 的一个开源项目,后来成为 Apache 的顶级项目。
Phoenix 构建在 HBase 之上的开源 SQL 层。能够让我们使用标准的 JDBC API 去建表,插入数据和查询 HBase 中的数据,从而可以避免使用 HBase 的客户端 API。
Apache Phoenix 通过结合两个方面的优点,在Hadoop中为低延迟应用提供了OLTP和运营分析:
- 具有完全ACID事务功能的标准SQL和JDBC api的强大功能
- 通过利用HBase作为后台存储,从NoSQL获得后期绑定的、读时模式功能的灵活性
- Phoenix通过协处理器在服务器端执行操作,最小化客户机/服务器数据传输
Apache Phoenix与Spark、Hive、Pig、Flume、Map Reduce等Hadoop产品完全集成。
通过定义良好的行业标准api,成为OLTP和Hadoop操作分析的可信数据平台。
在我们的应用和 HBase 之间添加了 Phoenix,并不会降低性能,而且我们也少写了很多代码。
1.1 phoenix 特点
- 将 SQL 查询编译为 HBase 扫描
- 完美支持 HBase 二级索引创建
- 支持完整的ACID事务、UDF、分页查询
- 确定扫描 Rowkey 的最佳开始和结束位置、扫描并行执行
- 将 where 子句推送到服务器端的过滤器
- 通过协处理器进行聚合操作
- DML 命令以及通过 DDL 命令创建和操作表和版本化增量更改。
- 容易集成:如Spark,Hive,Pig,Flume 和 Map Reduce。
- 支持java、python的Driver
1.2 哪些公司在使用Phoenix
![](https://img.haomeiwen.com/i13587608/12f5e820fc9aa690.png)
1.3 存储结构
HBase 的数据模型映射为关系型数据模型
![](https://img.haomeiwen.com/i13587608/faa71f7980e9101c.png)
对于Phoenix来说,HBase的rowkey会被转换成primary key,column family如果不指定则为0否则字段名会带上,qualifier转换成表的字段名。
1.4 Phoenix 与 Hbase 之间的表映射关系
![](https://img.haomeiwen.com/i13587608/470380bda444ba1e.png)
1.5 Phoenix数据类型
数据类型 | Java Map | 占用大小 (byte) |
---|---|---|
INTEGER | java.lang.Integer | 4 |
UNSIGNED_INT | java.lang.Integer | 4 |
BIGINT | java.lang.Long | 8 |
UNSIGNED_LONG | java.lang.Long | 8 |
TINYINT | java.lang.Byte | 1 |
UNSIGNED_TINYINT | java.lang.Byte | 1 |
SMALLINT | java.lang.Short | 2 |
UNSIGNED_SMALLINT | java.lang.Short | 2 |
FLOAT | java.lang.Float | 4 |
UNSIGNED_FLOAT | java.lang.Float | 4 |
DOUBLE | java.lang.Double | 8 |
UNSIGNED_DOUBLE | java.lang.Double | |
DECIMAL | java.math.BigDecimal | DECIMAL(p.,s) |
BOOLEAN | java.lang.Boolean | |
TIME | java.sql.Time | |
DATE | java.sql.Date | 8 |
TIMESTAMP | java.sql.Timestamp | 12 |
UNSIGNED_TIME | java.sql.Time | 8 |
UNSIGNED_DATE | java.sql.Date | 8 |
UNSIGNED_TIMESTAMP | java.sql.Timestamp | 12 |
VARCHAR | java.lang.String | VARCHAR(n) |
CHAR | java.lang.String | CHAR(n) |
BINARY | byte[] | BINARY(n) |
VARBINARY | byte[] | VARBINARY |
二、Phoniex架构
Phoenix结构上划分为客户端和服务端两部分:
- 客户端包括应用程序开发,将SQL进行解析优化生成QueryPlan,进而转化为HBase Scans,调用HBase API下发查询计算请求,并接收返回结果;
-
服务端主要是利用HBase的协处理器,处理二级索引、聚合及JOIN计算等。
Phoiex的旧版架构采用重客户端的模式,在客户端执行一系列的parser、query plan的过程,如下图所示:
这种架构存在使用上的缺陷:
应用程序与Phoenix core绑定使用,需要引入Phoenix内核依赖,一个单独Phoenix重客户端集成包已达120多M;
运维不便,Phoenix仍在不断优化和发展,一旦Phoenix版本更新,那么应用程序也需要对应升级版本并重新发布;
仅支持Java API,其他语言开发者不能使用Phoenix。
因此,社区进行改造,引入了新的“轻客户端”模式:
![](https://img.haomeiwen.com/i13587608/c4441b6619bd2a8e.png)
轻客户端架构将Phoenix分为两部分:
- 客户端是用户最小依赖的JDBC驱动程序,与Phoenix依赖进行解耦,支持Java、Python、Go等多种语言客户端;
- 将QueryServer部署为一个独立的的HTTP服务,接收轻客户端的请求,对SQL进行解析、优化、产生执行计划;
三、快速使用
安装包下载:https://phoenix.apache.org/download.html
3.1、安装
- 1、上传安装包到Linux系统,并解压
tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /usr/local/
- 2、环境变量
sudo vim /etc/profile.d/my_env.sh
#PHOENIX
export PHOENIX_HOME=/usr/local/phoenix-5.0.0
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
source /etc/profile
- 3、将phoenix的所有jar包添加到所有HBase RegionServer和Master的复制到HBase的lib目录
# 拷贝jar包到hbase lib目录
cp /export/server/apache-phoenix-5.0.0-HBase-2.0-bin/phoenix-*.jar /export/server/hbase-2.1.0/lib/
# 进入到hbase lib 目录
cd /export/server/hbase-2.1.0/lib/
# 分发jar包到每个HBase 节点
scp phoenix-*.jar node2.itcast.cn:$PWD
scp phoenix-*.jar node3.itcast.cn:$PWD
- 4、修改配置文件
cd /export/server/hbase-2.1.0/conf/
vim hbase-site.xml
- 4.1 将以下配置添加到 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>
- 4.2 将hbase-site.xml分发到每个节点
scp hbase-site.xml node2.itcast.cn:$PWD
scp hbase-site.xml node3.itcast.cn:$PWD
- 5、将配置后的hbase-site.xml拷贝到phoenix的bin目录
cp /export/server/hbase-2.1.0/conf/hbase-site.xml /export/server/apache-phoenix-5.0.0-HBase-2.0-bin/bin/
- 6、重新启动HBase
stop-hbase.sh
start-hbase.sh
- 7、启动Phoenix客户端,连接Phoenix Server
注意:第一次启动Phoenix连接HBase会稍微慢一点。
cd /usr/local/apache-phoenix-5.0.0-HBase-2.0-bin/bin/sqlline.py node1.itcast.cn:2181
# 输入!table查看Phoenix中的表
!table
-
8、查看HBase的Web UI,可以看到Phoenix在default命名空间下创建了一些表,而且该系统表加载了大量的协处理器。
-
9、图形化界面工具
DBeaver,SQuirrel等
3.2 快速入门
- 1、创建表语法
在Phoenix中,我们可以使用类似于MySQL DDL的方式快速创建表。例如:
CREATE TABLE IF NOT EXISTS 表名 (
ROWKEY名称 数据类型 PRIMARY KEY
列族名.列名1 数据类型 NOT NULL,
列族名.列名2 数据类型 NOT NULL,
列族名.列名3 数据类型);
订单明细建表语句:
create table if not exists ORDER_DTL(
ID varchar primary key,
C1.STATUS varchar,
C1.MONEY float,
C1.PAY_WAY integer,
C1.USER_ID varchar,
C1.OPERATION_TIME varchar,
C1.CATEGORY varchar
);
通过HBase的Web UI,我们可以看到Phoenix帮助我们自动在HBase中创建了一张名为 ORDER_DTL 的表格,可以看到里面添加了很多的协处理器。
'ORDER_DTL', {TABLE_ATTRIBUTES => {coprocessor$1 => '|org.apache.phoenix.coprocessor.ScanRegionObserver|805306366|', coprocessor$2 => '|org.apache.phoenix.coprocessor.UngroupedAggregateRegionObserver|805306366|', coprocessor$3 => '|org.apache.phoenix.coprocessor.GroupedAggregateRegionObserver|805306366|', coprocessor$4 => '|org.apache.phoenix.coprocessor.ServerCachingEndpointImpl|805306366|', coprocessor$5 => '|org.apache.phoenix.hbase.index.Indexer|805306366|index.builder=org.apache.phoenix.index.PhoenixIndexBuilder,org.apache.hadoop.hbase.index.codec.class=org.apache.phoenix.index.PhoenixIndexCodec'}}, {NAME => '0', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'FAST_DIFF', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'NONE', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}
同时,我们也看到这个表格默认只有一个Region,也就是没有分区的。
![](https://img.haomeiwen.com/i13587608/60190b060fba0ea8.png)
- 2、查看表的信息
!desc ORDER_DTL
注意:一定要加上 !
- 3、删除表语法
drop table if exists ORDER_DTL;
- 4、大小写问题
在HBase中,如果在列族、列名没有添加双引号。Phoenix会自动转换为大写。
例如:
create table if not exists ORDER_DTL(
id varchar primary key,
C1.status varchar,
C1.money double,
C1.pay_way integer,
C1.user_id varchar,
C1.operation_time varchar,
C1.category varchar
);
![](https://img.haomeiwen.com/i13587608/3f2e856465edc01f.png)
如果要将列的名字改为小写,需要使用双引号,如下:
![](https://img.haomeiwen.com/i13587608/cb6bba798ca65538.png)
注意:一旦加了小写,后面都得任何应用该列的地方都得使用双引号,否则将报以下错误:
Error: ERROR 504 (42703): Undefined column. columnName=ORDER_DTL.ID
- 5、插入数据
在Phoenix中,插入并不是使用insert来实现的。而是 「upsert 」命令。它的功能为insert + update,与HBase中的put相对应。如果不存在则插入,否则更新。列表是可选的,如果不存在,值将按模式中声明的顺序映射到列。这些值必须计算为常量。
UPSERT INTO ORDER_DTL VALUES('000001', '已提交', 4070, 1, '4944191', '2020-04-25 12:09:16', '手机;');
- 6、查询数据
与标准SQL一样,Phoenix也是使用select语句来实现数据的查询。
SELECT * FROM ORDER_DTL;
- 7、更新数据
在Phoenix中,更新数据也是使用UPSERT。语法格式如下:
UPSERT INTO 表名(列名, …) VALUES(对应的值, …);
UPSERT INTO ORDER_DTL("id", C1."status") VALUES ('000001', '已付款');
- 8、根据ID查询数据
SELECT * FROM ORDER_DTL WHERE "id" = '000001';
- 9、根据ID删除数据
DELETE FROM ORDER_DTL WHERE "id" = '000001';
- 10、分页查询
使用limit和offset可以快速进行分页。
limit表示每页多少条记录,offset表示从第几条记录开始查起。
-- 第一页
select * from ORDER_DTL limit 10 offset 0;
-- 第二页
-- offset从10开始
select * from ORDER_DTL limit 10 offset 10;
-- 第三页
select * from ORDER_DTL limit 10 offset 20;
![](https://img.haomeiwen.com/i13587608/96ccbe03eb2748d4.png)
更多语法:http://phoenix.apache.org/language/index.html#delete
Phoenix所支持的语法
目前Phoenix已经支持关系型数据库的大部分语法,如下图所示:
![](https://img.haomeiwen.com/i13587608/cf61b8fcff56ec8c.png)
四、基于Phoenix实现hbase的预分区操作
默认创建表的方式只有一个region,则HBase顺序写入可能会受到RegionServer热点的影响。对行键进行加盐可以解决热点问题。在HBase中,可以使用两种方式:
- 1.ROWKEY预分区
- 2.加盐指定数量分区
4.1 ROWKEY预分区
按照用户ID来分区,一共4个分区。并指定数据的压缩格式为GZ。
drop table if exists ORDER_DTL;
create table if not exists ORDER_DTL(
"id" varchar primary key,
C1."status" varchar,
C1."money" float,
C1."pay_way" integer,
C1."user_id" varchar,
C1."operation_time" varchar,
C1."category" varchar
)
CONPRESSION='GZ'
SPLIT ON ('3','5','7');
4.2 加盐指定数量分区
drop table if exists ORDER_DTL;
create table if not exists ORDER_DTL(
"id" varchar primary key,
C1."status" varchar,
C1."money" float,
C1."pay_way" integer,
C1."user_id" varchar,
C1."operation_time" varchar,
C1."category" varchar
)
CONPRESSION='GZ', SALT_BUCKETS=10;
注意
- 1、CONPRESSION和SALT_BUCKETS之间需要使用逗号分隔,否则会出现语法错误。
- 2、此种方式构建的预分区, 在插入数据时候, 会自动给rowkey进行加盐处理, 保证rowkey不会发生热点问题。能够让数据均衡的落在不同region中, 而且这种操作对于直接使用Phoenix用户而言是无感的。
- 3、但是, 一旦使用这种方式, 我们将无法在通过hbase直接查询数据, 因为对rowkey是有改变的。
五、phoenix表与hbase表对应关系
默认情况下,直接在hbase中创建的表,通过phoenix是查看不到的。
如果要在phoenix中操作由hbase创建的表,则需要在phoenix中进行表的映射。
映射方式有两种:视图映射和表映射
假设Hbase中已创建表test,test有两个列簇name、company
5.1 视图映射:
Phoenix创建的视图是只读的,只能用来做查询,无法通过视图对源数据进行修改等操作。
create view "test"(
empid varchar primary key,
"name"."firstname" varchar,
"name"."lastname" varchar,
"company"."name" varchar,
"company"."address" varchar
);
5.2 表映射
- 1、把create view改为create table即可
- 2、当HBase中不存在表时,使用create table指令创的表,系统将会自动在Phoenix和HBase中创建表,并会根据指令内的参数对表结构进行初始化。
create table "test"(
empid varchar primary key,
"name"."firstname" varchar,
"name"."lastname" varchar,
"company"."name" varchar,
"company"."address" varchar
);
六、Phoneix 二级索引
在HBase中,只有一个单一的按照字典序排序的rowKey索引。使用rowKey数据查询速度较快;但是,若使用filter来对全表进行扫描(不使用rowKey来查询),则会在很大程度上降低检索性能。鉴于此,Phoenix提供了二级索引技术,来应对这种使用rowKey之外的条件进行检索的场景。
6.1 索引配置
建立非事务性、可变索引表需要在hbase-site.xml添加如下配置:
<property>
<name>hbase.regionserver.wal.codec</name>
<value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
</property>
<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>
注意:
若hive整合了hbase,需要在hive的lib中添加phoenix-core-x.x.x-HBase-x.x.jar,否则在创建hive映射hbase的表时报如下错:
Caused by: java.lang.ClassNotFoundException: >org.apache.hadoop.hbase.ipc.controller.ServerRpcControllerFactory
在phoenix4.8以下还需添加如下配置:
<property>
<name>hbase.master.loadbalancer.class</name>
<value>org.apache.phoenix.hbase.index.balancer.IndexLoadBalancer</value>
</property>
<property>
<name>hbase.coprocessor.master.classes</name>
<value>org.apache.phoenix.hbase.index.master.IndexMasterObserver</value>
</property>
<property>
<name>hbase.coprocessor.regionserver.classes</name>
<value>org.apache.hadoop.hbase.regionserver.LocalIndexMerger</value>
</property>
6.2 索引类别
Covered Index 覆盖索引
- Phoenix支持覆盖索引功能。访问的列如果是覆盖索引的列,不需要去访问主表即可查询到所需要的数据。
- 如果创建的是覆盖索引,那么查询语句中的条件字段、返回字段都必须创建过索引,否则就会触发full table scan。如:
创建方式如下:
create index my_index on test (v1) include(v2);
当执行select v2 from test where v1='...'时,就只会查找索引表数据,不会去主表扫描。
Global Indexes 全局索引
Global Index是默认的索引格式,创建全局索引本质上是在HBase中建立一张新表,因此,全局索引适用于读多写少的业务场景。因为在向HBase表写数据的时候,索引表也会更新,索引表的数据也是分布在不同数据节点,跨节点数据传输会造成较大性能消耗。采用全局索引在读数据时,Phoenix会尽量选择索引表来降低查询消耗的时间。若查询字段不是索引字段,则索引表不会被使用,也就不会带来查询速度的提升。
create index my_index on test (v1);
它有一个缺陷,如果查询语句中的条件字段或返回字段不是索引字段,就会触发全表扫描。如:
SELECT USERID,KEYWORD FROM CSVTABLES WHERE USERID='9bb8b2af92df3c3'
解决办法有两个
- 一是和覆盖索引一样,创建索引时把相关字段include进来。
CREATE INDEX MYINDEX ON CSVTABLES(USERID) INCLUDE(KEYWORD);
- 二是强制使用索引
SELECT /*+ INDEX(my_table my_index) */ "v1" FROM my_table WHERE "v3" = '13406157616';
这样的查询语句会导致二次检索数据表,第一次检索是去索引表中查找符合v3='13406157616'的数据,这时候发现 KEYWORD 字段并不在索引字段中,会去CSVTABLES 表中第二次扫描,
因此只有当用户明确知道符合检索条件的数据较少的时候才适合使用,否则会造成全表扫描,对性能影响较大。
本地索引 Local Indexing
Local Index 的索引数据 和 HBase的表数据 是存放在 同一个表(且是同一个Region中),避免了写操作的时候往不同服务器的索引表中写数据带来的额外的性能开销。查询的字段不是索引字段,索引表也会被使用,这会带来查询速度的提升。
与Global indexing一样,Phoenix会自动判定查询是否使用索引。与Global indexing不同的地方,也即适用于写操作频繁的原因,具体如下:
- 使用Local indexing时,索引数据和数据表的数据存放在相同的服务器中。这样也就避免了在写操作时,往不同服务器的索引表中写索引而带来的额外开销。
- 使用Local indexing时,本地索引和主表的数据存储在同一个表中,使用隐藏的列族来存储所以数据。并且,一个数据表的所有索引数据都存储在一个单一的独立的可共享的表中。
- 使用Local indexing时,即使查询的字段不是索引字段,索引表也会被使用。这就会带来查询速度的提升,这点跟Global indexing不同。
create local index my_index on test (v1);
6.3 可变索引和不可变索引
不可变索引 IMMutable Indexing
不可变索引主要创建在不可变表上,适用于数据只写一次不会有Update等操作,在什么场景下会用到不可变索引呢,很经典的时序数据:write once read many times。在这种场景下,所有索引数据(primary和index)要么全部写成功,要么一个失败全都失败返回错误给客户端。不可变索引用到场景比较少,下面是创建不可变索引的方式:
create table test (pk VARCHAR primary key,v1 VARCHAR, v2 VARCHAR) IMMUTABLE_ROWS=true;
即在创建表时指定IMMUTABLE_ROWS参数为true,默认这个参数为false。如果想把不可变索引改为可变索引,可用alter修改:
alter table test set IMMUTABLE_ROWS=false;
可变索引 Mutable Indexing
可变索引意思是在修改数据如Insert、Update或Delete数据时会同时更新索引。这里的索引更新涉及WAL,即主表数据更新时,会把索引数据也同步更新到WAL,只有当WAL同步到磁盘时才会去更新实际的primary/index数据,以保证当中间任何一个环节异常时可通过WAL来恢复主表和索引表数据。
七、Phoenix 散步表 Salted Tables
7.1 Salted Tables场景
在密码学中,在散列中加入字符串的方式称为“加盐”,以增加额外的安全性。在Phoenix中,“加盐”是指对pk对应的byte数组插入特定的byte数据。
Phoenix的Salted Tables技术(加盐)能解决HBASE读写热点问题。例如,单调递增rowkey数据的持续写入,使得负载集中在某一个RegionServer上从而引起的热点问题。
7.2 Salted Tables原理
加盐的过程就是在原来key的基础上增加一个byte作为前缀,计算公式如下:
new_row_key =(++index % BUCKETS_NUMBER) + original_key
自增rowkey通过加盐被打散写入到各个region中,参见如下图所示。
![](https://img.haomeiwen.com/i13587608/721886003243b612.png)
7.3 表加盐命令及参数设置
在创建表的建加盐表时,不能再指定split key。需要指定属性值SALT_BUCKETS,可参考如下操作。
CREATE TABLE table (
key VARCHAR PRIMARY KEY,
col VARCHAR
) SALT_BUCKETS = 8;
SALT_BUCKETS参数值,表示所分buckets(region)数量,范围是1~256。具体设值大小,参考如下建议:
- 当可用block cache的大小小于表数据大小时,较优的slated bucket是和region server数量相同,这样可以得到更好的读写性能。
- 当表的数量很大时,基本上会忽略blcok cache的优化收益,大部分数据仍然需要走磁盘IO。比如对于10个region server集群的大表,可以考虑设计64~128个slat buckets。
- 太大的slated buckets会减小range查询的灵活性,甚至降低查询性能。
八、性能
在官网,有作一个性能测试,主要是将Phoenix和Hive、Impala作一个对比。
先来看下和Hive的性能对比,测试基准如下:
select count(1) from table over 10M and 100M rows.
Data is 5 narrow columns.
Number of Region Servers: 4 (HBase heap: 10GB, Processor: 6 cores @ 3.3GHz Xeon)
测试结果:
![](https://img.haomeiwen.com/i13587608/5898aa48b6467998.png)
从图中可看出,带有Key过滤的Phoenix耗时最少,不带Key过滤的Phoenix和基于HDFS的Hive性能差不多,直接基于HBase的Hive性能最差。
再来看下和Impala的对比,测试基准如下:
select count(1) from table over 1M and 5M rows.
Data is 3 narrow columns.
Number of Region Server: 1 (Virtual Machine, HBase heap: 2GB, Processor: 2 cores @ 3.3GHz Xeon)
测试结果:
![](https://img.haomeiwen.com/i13587608/a189f7806ca7b640.png)
从图中可看出,Impala执行时间比Phoenix长很多,原因大概有几点:Impala基于内存进行并行计算,容易内存吃紧,对HBase和HDFS的支持也还远远不够,性能比较差。
参考:
https://blog.csdn.net/haoheiao/article/details/126583757
https://blog.csdn.net/qq_40585384/article/details/122313621
https://www.cnblogs.com/wdh01/p/16290482.html
https://blog.csdn.net/m0_37767351/article/details/114753572
https://www.cnblogs.com/tenic/p/14881674.html
https://blog.51cto.com/u_15259710/3406715
https://www.cnblogs.com/yangms/p/14597772.html
网友评论