当使用Phoenix作为客户端去操作HBase时,Phoenix中执行DDL语句中的namespace名称、表名称、字段名称等等默认都会被转换成大写,如果想要其维持小写形式,需要使用双引号("")括住。
Phoenix实际上是HBase客户端的一个皮肤,其支持标准SQL语法。
一、Shell操作
1. 帮助命令
# 显示出可以使用的Phoenix命令
!help
2. 展示所有创建的表
!tables
3. 创建schema(即namespace或者数据库)
CREATE SCHEMA [IF NOT EXISTS] schema_name;
4. 切换当前工作schema
USE schema_name;
5. 创建表
-- 建表的时候,指定单一主键
CREATE TABLE IF NOT EXISTS my_schema.my_table (
field_name1 type PRIMARY KEY,
field_name2 type,
...
)
-- 建表的时候指定联合主键:
CREATE TABLE IF NOT EXISTS my_schema.my_table (
field_name1 type,
field_name2 type,
...
CONSTRAINT pk_name PRIMARY KEY (field_name, ...)
)
-- 无论是单一主键、还是联合主键,在HBase中都体现为Row Key
-- 为了节约HBase底层存储表中字段的存储空间,Phoenix默认会在创建表的时候,对表中每个
-- 字段进行编码,将编码后的内容实际存储到HFile中,因此会出现这样一种现象:
-- 在HBase原生客户端中使用scan命令查看数据,会发现列名由十六进制编码组成,
-- 如果不想让Phoenix将表中字段编码后放进HFile中,可以如下书写建表语句:
create_table_statement
COLUMN_ENCODED_BYTES = NONE;
-- 详细内容参考:https://phoenix.apache.org/columnencoding.html
-- 值问题:对于VARCHAR、CHAR、无符号数值类型,Phoenix会直接套用HBase的处理逻辑;
-- 但是对于有符号数值类型,Phoenix有自己的处理方案,这就会导致Phoenix无法正确
-- 读取HBase中的有符号数值类型,Hbase也无法正确读取Phoenix中的有符号数值类型
6. 插入\修改数据
UPSERT INTO [schema_name.]table_name [(field_name1, field_name2, ...)] VALUES (value1, value2, ...), ...;
7. 删除数据
DELETE FROM [schema_name.]table_name [WHERE condition];
8. 视图映射(HBase中已经存在表,但是Phoenix中不存在,视图是只读的)
-- PRIMARY KEY的命名随意,它充当HBase中的Row Key
CREATE VIEW "[namespace_name_in_hbase.]table_name_in_hbase"
(
id VARCHAR PRIMARY KEY,
"column_family_name_in_hbase"."column_name_in_hbase" type,
...
) ;
9. 表映射(HBase中已经存在表,但是Phoenix中不存在)
-- PRIMARY KEY的命名随意,它充当HBase中的Row Key
CREATE TABLE "[namespace_name_in_hbase.]table_name_in_hbase"
(
id VARCHAR PRIMARY KEY,
"column_family_name_in_hbase"."column_name_in_hbase" type,
...
)
COLUMN_ENCODED_BYTES = NONE;
10. 删除表
DROP TABLE [IF EXISTS] [schema_name.]table_name;
11. 查询(SELECT)
和标准SQL对比起来,不能说完全一样,简直是如出一辙。
二、API操作
由于Phoenix遵守了标准SQL规范,其操作流程完全符合普通JDBC API的使用,唯一不同的仅仅在于:获取连接对象的方法有些改动、某些SQL语句需要符合Phoenix的语法。
What is the Phoenix JDBC URL syntax?
1. 获取胖客户端连接对象
(1) 添加依赖
<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>
</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>
<scope>provided</scope>
</dependency>
(2) 获取
// 后面的主机名是zookeeper所在机器主机名,端口号是2181
String jdbcURL = "jdbc:phoenix:hadoop101,hadoop102,hadoop103:2181";
Properties properties = new Properties();
properties.put("phoenix.schema.isNamespaceMappingEnabled", "true");
Connection connection = DriverManager.getConnection(jdbcURL, properties);
2. 获取瘦客户端连接对象
(1) 添加依赖
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-queryserver-client</artifactId>
<version>5.0.0-HBase-2.0</version>
</dependency>
(2) 获取
// 主机名是query server所在的机器主机名,默认端口号为8765
String jdbcURL = ThinClientUtil.getConnectionUrl("hadoop101", 8765);
Connection connection = DriverManager.getConnection(jdbcURL);
3. 基操
String sql = "select * from TEST1.user1";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
String outputForamt = "id: {0}, name: {1}, age: {2}";
while (resultSet.next()){
String id = resultSet.getString("id");
String name = resultSet.getString("name");
Integer age = resultSet.getInt("age");
System.out.println(MessageFormat.format(outputForamt, id, name, age));
}
resultSet.close();
preparedStatement.close();
connection.close();
三、Phoenix二级索引
1. 配置二级索引
添加如下配置到HBase的Regionserver节点的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>
# 任意一台机器上配置,然后分发
vim $HBASE_HOME/conf/hbase-site.xml
xrsync.sh $HBASE_HOME/conf/hbase-site.xml
2. 全局索引
(1) 语法
# 为其他列建立索引
# INCLUDE后包含的列并不建索引,作为一个普通列加入索引表中,
# 从而可以实现直接从索引表中查出数据,不需要跑到整张表中查询数据
CREATE INDEX index_name on table_name(column1, column2, ...) [INCLUDE (column3, column4, ...)]
# 什么情况下从索引表中查询数据?
# (1) 过滤条件使用row key,单点查询,效率最高
# (2) 如果索引表中不包含普通列,要求查询语句中出现的所有列必须是row key以及建立了索引的列,
# 同时要求条件中列的顺序为:[row key] 声明索引时指定的第一列 声明索引时指定的第二列 ...
# 不可以不连续。特别的,如果条件中出现了所有索引列,可以不按顺序书写,Phoenix会进行优化,
# 将其重新排序
# (3) 使用了INCLUDE,此时查询语句放宽了限制,可以有在索引表中的普通列,其他相对于(2)情况不变
(2) 实现方式
当在Phoenix中建好了一张表后,只有建表时标记为primary key的列会作为HBase中的row key,这样就导致:根据row key进行查询,效率非常高,但是根据其他列查询,只会是全表扫描。
当为某一列(或者组合多列)建好索引索引后,会将这些列和row key拿出来,同时在Phoenix和HBase中新建一张索引表,Phoenix中索引表的结构为各个列组成,列的顺序就是建索引的时候书写的列的顺序;HBase底层中表示为索引的列+row key组合为一个row key。
3. 本地索引
(1) 语法
# 为其他列建立本地索引
CREATE LOCAL INDEX index_name on table_name(column1, column2, ...)
# 什么情况下根据索引查询数据?
# (1) 过滤条件使用row key,单点查询,效率最高
# (2) 要求查询语句中出现的所有列必须是row key以及建立了索引的列,
# 同时要求条件中列的顺序为:[row key] 声明索引时指定的第一列 声明索引时指定的第二列 ...
# 不可以不连续。特别的,如果条件中出现了所有索引列,可以不按顺序书写,Phoenix会进行优化,
# 将其重新排序
(2) 实现
Phoenix中基本一样,但在HBase底层存储上,不会新建一张表用来单独存储索引,而是会将索引内容直接放入原表中与全数据一起维护。
4. 查看查询语句的执行计划
EXPLAIN select_statement