Phoenix

一、Phoenix 介绍:
Phoenix提供了类标准Sql的方式进行操作Hbase的数据。
Phoenix 操作hbase有两种方式,创建表,创建视图。
区别如下:
创建表的话,就可以对HBase进行插入,查询,删除操作。
视图的话,一般就只可以进行查询操作。
虽然看起来表的功能比视图更强大一些。但就像是mysql等关系型数据库一样,删除视图操作不会影响原始表的结构。同时Phoenix的视图也支持创建二级索引相关的。
因为使用phoenix 创建表后,会自动和hbase建立关联映射。当你使用phoenix删除和hbase之间的关系时,就会将hbase中的表也删掉了,并将与之关联的索引表一并删除。
二、phoenix 常用命令
温馨提示:笔者开启了Kerberos安全认证机制,因此第一步是先对机器进行Kerberos认证;如果开启了用户访问hbase表权限相关的,通过phoenix查询hbase中的表需要开通该用户拥有访问hbase 系统表相关的权限。
注:phoenix会将没有用双引号的表名列名等转化成大写,所以如果表名跟列名为小写需用双引号括起来。
2.1 进入phoenix 命令行
[root@hdp39 ~]# cd /usr/hdp/2.5.3.0-37/phoenix/bin
[root@hdp39 bin]# ./sqlline.py hdp39,hdp40,hdp41:2181
(集群采用的安全模式为kerberos,因而执行这条命令前进行kinit的用户必须拥有操作Hbase相关的权限)
2.2、help
查看内置命令
2.3、!tables
List all the tables in the database



查看表结构 ! desc tableName
0: jdbc:phoenix:hdp40,hdp41,hdp39:2181> !desc “t_hbase1”
+————+————–+————-+————–+————+————+————–+—————-+—————–+—————–+———–+———-+————-+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE | TYPE_NAME | COLUMN_SIZE | BUFFER_LENGTH | DECIMAL_DIGITS | NUM_PREC_RADIX | NULLABLE | REMARKS | COLUMN_DEF |
+————+————–+————-
2.4、创建表
分为两种方式,一种为hbase中没有对应表,另一种为hbase中已经存在该表
a 创建phoenix表
[root@hdp07 bin]# ./sqlline.py hdp40,hdp41,hdp39:2181 /tmp/us_population.sql
us_population.sql 内容如下所示:
[root@hdp07 tmp]# cat us_population.sql
CREATE TABLE IF NOT EXISTS us_population (
state CHAR(2) NOT NULL,
city VARCHAR NOT NULL,
population BIGINT
CONSTRAINT my_pk PRIMARY KEY (state, city));
创建成功后可在hbase中查询,结果如下所示(给表添加了协处理coprocessor):



hbase(main):002:0> desc ‘US_POPULATION’
Table US_POPULATION is ENABLED
US_POPULATION, {TABLE_ATTRIBUTES => {coprocessor$1 => ‘|org.apache.phoenix.coprocessor.ScanRegionObserver|805306366|’, coprocessor$2 => ‘|org.apache.phoenix.coprocessor.UngroupedAggregateRe
gionObserver|805306366|’, coprocessor$3 => ‘|org.apache.phoenix.coprocessor.GroupedAggregateRegionObserver|805306366|’, coprocessor$4 => ‘|org.apache.phoenix.coprocessor.ServerCachingEndpoi
ntImpl|805306366|’, coprocessor$5 => ‘|org.apache.phoenix.hbase.index.Indexer|805306366|org.apache.hadoop.hbase.index.codec.class=org.apache.phoenix.index.PhoenixIndexCodec,index.builder=or
g.apache.phoenix.index.PhoenixIndexBuilder’}
COLUMN FAMILIES DESCRIPTION
{NAME => ‘0’, BLOOMFILTER => ‘ROW’, VERSIONS => ‘1’, IN_MEMORY => ‘false’, KEEP_DELETED_CELLS => ‘FALSE’, DATA_BLOCK_ENCODING => ‘FAST_DIFF’, TTL => ‘FOREVER’, COMPRESSION => ‘NONE’, MIN_VE
RSIONS => ‘0’, BLOCKCACHE => ‘true’, BLOCKSIZE => ‘65536’, REPLICATION_SCOPE => ‘0’}
导入数据
[root@hdp07 bin]# ./psql.py -t US_POPULATION hdp40,hdp41,hdp39:2181 /tmp/us_population.csv
1
csv文件内容如下:
[root@hdp07 tmp]# cat us_population.csv
NY,New York,8143197
CA,Los Angeles,3844829
IL,Chicago,2842518
导入数据之后查看如下:



  • 查看数据



phoenix shell 中查看数据如下所示:
0: jdbc:phoenix:hdp40,hdp41,hdp39:2181> select * from us_population;
+——–+—————+————-+
| STATE | CITY | POPULATION |
+——–+—————+————-+
| AZ | Phoenix | 1461575 |
hbase shell 下查看数据如下所示:
hbase(main):005:0> scan ‘US_POPULATION’
ROW COLUMN+CELL
AZPhoenix column=0:POPULATION, timestamp=1522308898804, value=\x80\x00\x00\x00\x00\x16MG
AZPhoenix column=0:_0, timestamp=1522308898804, value=x
也可以像创建表一样通过执行查询sql或者直接在phoenix shell命令行中输入对应的命令,如下所示
[root@hdp07 bin]# ./sqlline.py hdp40,hdp41,hdp39:2181 /tmp/us_population_queries.sql
…..
Building list of tables and columns for tab-completion (set fastconnect to true to skip)…
106/106 (100%) Done
Done
1/1 SELECT state as “State”,count(city) as “City Count”,sum(population) as “Population Sum”
FROM us_population
GROUP BY state
ORDER BY sum(population) DESC;
+——–+————-+—————–+
| State | City Count | Population Sum |
+——–+————-+—————–+
| NY | 1 | 8143197 |
us_population_queries.sql 内容如下:



[root@hdp07 tmp]# cat us_population_queries.sql
SELECT state as “State”,count(city) as “City Count”,
sum(population) as “Population Sum”
FROM us_population
GROUP BY state
ORDER BY sum(population) DESC;
0: jdbc:phoenix:hdp40,hdp41,hdp39:2181> UPSERT INTO us_population VALUES(‘YY’,’PHOENIX_TEST’,99999);
1 row affected (0.046 seconds)
注:如果插入的是同一个rowkey对应的数据,则相当于关系数据库的修改。



删除数据
0: jdbc:phoenix:hdp40,hdp41,hdp39:2181> delete from us_population where CITY=’PHOENIX_TEST’;
1 row affected (0.05 seconds)
b 创建已存在的Hbase表或视图
(phoenix 操作Hbase的数据,首先在Phoenix中创建与Hbase关联的表或视图)
建立hbase关联的映射表(该hbase表已存在)

Phoenix是一个支持在HBase上进行SQL查询的引擎。由于HBase的语言和传统SQL有较大不同,不利于数据库开发者的迁移,因此Phoenix作为中间件,可以极大的提高数据开发者的开发效率。
Phoenix官方文档:http://phoenix.apache.org/,这是目前最权威的Phoenix文档,可惜的是没有中文版;
阿里云上的中文简介:https://help.aliyun.com/document_detail/53600.html?spm=a2c4g.11186623.6.581.EuG7gQ;
https://yq.aliyun.com/articles/574090
Phoenix是一个开源的HBASE SQL层。它不仅可以使用标准的JDBC API替代HBASE client API创建表,插入和查询HBASE,也支持二级索引、事物以及多种SQL层优化。
Phoenix作为应用层和HBASE之间的中间件,以下特性使它在大数据量的简单查询场景有着独有的优势



二级索引支持(global index + local index)
编译SQL成为原生HBASE的可并行执行的scan
在数据层完成计算,server端的coprocessor执行聚合
下推where过滤条件到server端的scan filter上
利用统计信息优化、选择查询计划(5.x版本将支持CBO)
skip scan功能提高扫描速度
一般可以使用以下三种方式访问Phoenix



JDBC API
使用Python编写的命令行工具(sqlline, sqlline-thin和psql等)
SQuirrel
云HBASE上Phoenix支持的DML
select
upsert values
upsert select
delete




  1. 什么是加盐?
    在密码学中,加盐是指在散列之前将散列内容(例如:密码)的任意固定位置插入特定的字符串。这个在散列中加入字符串的方式称为“加盐”。其作用是让加盐后的散列结果和没有加盐的结果不相同,在不同的应用情景中,这个处理可以增加额外的安全性。而Phoenix中加盐是指对pk对应的byte数组插入特定的byte数据。




  2. 加盐能解决什么问题?
    加盐能解决HBASE读写热点问题,例如:单调递增rowkey数据的持续写入,使得负载集中在某一个RegionServer上引起的热点问题。




  3. 怎么对表加盐?
    在创建表的时候指定属性值:SALT_BUCKETS,其值表示所分buckets(region)数量, 范围是1~256。





CREATE TABLE mytable (my_key VARCHAR PRIMARY KEY, col VARCHAR) SALT_BUCKETS = 8;



  1. 加盐的原理是什么?
    加盐的过程就是在原来key的基础上增加一个byte作为前缀,计算公式如下:



new_row_key = (++index % BUCKETS_NUMBER) + original_key
目前HBASE只有基于字典序的主键索引,对于非主键过滤条件的查询都会变成扫全表操作,为了解决这个问题Phoenix引入了二级索引功能。然而此二级索引又有别于传统关系型数据库的二级索引,本文将详细描述了Phoenix中二级索引功能、用法和原理。



二、二级索引
示例表如下(为了能够容易通过HBASE SHELL对照表内容,我们对属性值COLUMN_ENCODED_BYTES设置为0,不对column family进行编码):



CREATE TABLE TEST (
ID VARCHAR NOT NULL PRIMARY KEY,
COL1 VARCHAR,
COL2 VARCHAR
) COLUMN_ENCODED_BYTES=0;
upsert into TEST values(‘1’, ‘2’, ‘3’);



  1. 全局索引
    全局索引更多的应用在读较多的场景。它对应一张独立的HBASE表。对于全局索引,在查询中检索的列如果不在索引表中,默认的索引表将不会被使用,除非使用hint。



创建全局索引:



CREATE INDEX IDX_COL1 ON TEST(COL1)
通过HBASE SHELL观察生成的索引表IDX_COL1。我们发现全局索引表的RowKey存储了索引列的值和原表RowKey的值,这样编码更有利于提高查询的性能。



hbase(main):001:0> scan ‘IDX_COL1’
ROW COLUMN+CELL
2\x001 column=0:_0, timestamp=1520935113031, value=x
1 row(s) in 0.1650 seconds
实际上全局索引的RowKey将会按照如下格式进行编码。
Screen_Shot_2018_03_13_at_18_04_32



SALT BYTE: 全局索引表和普通phoenix表一样,可以在创建索引时指定SALT_BUCKETS或者split key。此byte正是存储着salt。
TENANT_ID: 当前数据对应的多租户ID。
INDEX VALUE: 索引数据。
PK VALUE: 原表的RowKey。



  1. 本地索引
    因为本地索引和原数据是存储在同一个表中的,所以更适合写多的场景。对于本地索引,查询中无论是否指定hint或者是查询的列是否都在索引表中,都会使用索引表。



创建本地索引:



create local index LOCAL_IDX_COL1 ON TEST(COL1);
通过HBASE SHELL观察表’TEST’, 我们可以看到表中多了一行column为L#0:_0的索引数据。



hbase(main):001:0> scan ‘TEST’
ROW COLUMN+CELL
\x00\x002\x001 column=L#0:_0, timestamp=1520935997600, value=_0
1 column=0:COL1, timestamp=1520935997600, value=2
1 column=0:COL2, timestamp=1520935997600, value=3
1 column=0:_0, timestamp=1520935997600, value=x
2 row(s) in 0.1680 seconds
本地索引的RowKey将会按照如下格式进行编码:
Screen_Shot_2018_03_13_at_20_16_24



REGION START KEY : 当前row所在region的start key。加上这个start key的好处是,可以让索引数据和原数据尽量在同一个region, 减小IO,提升性能。
INDEX ID : 每个ID对应不同的索引表。
TENANT ID :当前数据对应的多租户ID。
INDEX VALUE: 索引数据。
PK VALUE: 原表的RowKey。



  1. 覆盖索引
    覆盖索引的特点是把原数据存储在索引数据表中,这样在查询到索引数据时就不需要再次返回到原表查询,可以直接拿到查询结果。
    一、MR在Phoenix上的用途
    利用MR对Phoenix表(可带有二级索引表)进行Bulkload入库, 其原理是直接生成主表(二级索引表)的HFILE写入HDFS。相对于走API的数据导入方式,不仅速度更快,而且对HBASE集群的负载也会小很多。目前云HBASE上的Phoenix支持以下数据源的Bulkload工具:



CsvBulkLoadTool
JsonBulkLoadTool
RegexBulkLoadTool
ODPSBulkLoadTool(待上线)
利用MR Building二级索引。当主表数据量较大时,可以通过创建异步索引,使用MR快速同步索引数据。
二、如何访问云HBASE的HDFS?
由于云HBASE上没有MR,需要借助外部的计算引擎(自建的HADOOP集群或者EMR),而使用外部的计算引擎的首先面临的问题是,如何跨集群访问HDFS。
1.由于云HBASE的HDFS端口默认是不开的,需要联系工作人员开通。
2.端口开通以后,要想顺利的访问HDFS是HA配置的云HBASE集群,需要向工作人员获取云HBASE的主备(emr-header-1,emr-header-2)namenode host/IP。参考如下配置模板,设置hadoop客户端配置文件:
hdfs-site.xml





dfs.nameservices
emr-cluster


dfs.client.failover.proxy.provider.emr-cluster
org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider


dfs.ha.automatic-failover.enabled.emr-cluster
true


dfs.ha.namenodes.emr-cluster
nn1,nn2


dfs.namenode.rpc-address.emr-cluster.nn1
{emr-header-1-host}:8020


dfs.namenode.rpc-address.emr-cluster.nn2
{emr-header-2-host}:8020


3.验证访问云HBASE HDFS
在emr或自建集群上访问云HBase集群



hadoop dfs -ls hdfs://emr-cluster/
三、BULKLOAD PHOENIX表
以EMR访问云HBASE为例。EMR集群需要把云HBASE HDFS的emr-cluster 相关配置和当前EMR的HDFS配置合在一起形成新的配置文件,单独存放在一个目录(${conf-dir})下。
通过yarn/hadoop命令的–config参数指定新的配置目录,使这些配置文件放在CLASSPATH最前面覆盖掉当前EMR集群hadoop_conf_dir下的配置,以便bulkload程序能识别到云HBASE HA的HDFS URL。当在emr或自建集群上能够访问自己的HDFS(hadoop –config dfs -ls /), 也能够访问云HBase的HDFS(hadoop --config dfs -ls hdfs://emr-cluster/)说明配置成功了。



执行如下BULKLOAD命令



yarn –config ${CONF_DIR}

jar ${PHOENIX_HOME}/phoenix-${version}-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool

–table “TABLENAME”

–input “hdfs://emr-header-1.cluster-55090:9000/tmp/test_data”

–zookeeper “zk1,zk2,zk3”

–output “hdfs://emr-cluster/tmp/tmp_data”
注意: –output 配置的是云HBASE的临时文件,这样直接把生成的HFILE存储在云HBASE的HDFS上,后续的只有简单的move操作。否则,如果生成在EMR集群还需要走网络发送到云HBASE HDFS上。


Category storage