Apache Phoenix
更多phoenix操作: https://www.jianshu.com/p/a5c892f36188
https://blog.csdn.net/qq1226317595/article/details/80375009?utm_source=blogxgwz0
一.概述
1. Phoenix定位
Phoenix最早是saleforce的一个开源项目,后来成为Apache基金的顶级项目。Phoenix是一个HBASE SQL层(即为HBase的一个SQL引擎),用作应用层和HBASE之间的中间件。Phoeinx可以用标准的JDBC API替代HBASE client API来创建表、插入与查询HBASE中的数据。
2. 应用场景
Phoenix已与Hadoop其他服务完全集成,如Spark,Hive,Flume和Map Reduce。适合高并发、低延迟、简单查询、二级索引的场景。Phoenix通过结合HBase与SQL两者的优点,在Hadoop中为低延迟应用程序启用OLTP和运营分析。
- 具有完整ACID事务功能的标准SQL和JDBC API的强大功能。
- 通过利用HBase作为其后备存储,来自NoSQL世界的后期绑定模式读取功能的灵活性。
3. SQL支持
Apache Phoenix支持所有标准SQL查询构造,包括SELECT,FROM,WHERE,GROUP BY,HAVING,ORDER BY等。它还支持一整套DML命令以及通过DDL命令创建表和版本化增量更改。
Apache Phoenix目前不支持关系运算符(相交,减)以及其他内置功能。
4. Phoenix整体架构
5. Phoenix技术点
在应用程序和HBase之间增加额外的HBASE SQL层,不会影响性能。因为相较人工写代码而言,Phoenix以下技术点使其在大数据量的简单查询场景,可实现与其人工手写相同或更优的性能,并且省去了大部分人工代码的工作量。
- phoenix会把SQL编译成一系列的HBase可并行执行的scan操作,然后把scan结果生成标准的JDBC结果集。
- 编排scan语句使其并行执行:统计相关数据来提高并行化水平,并帮助选择最佳优化方案。
- 二级索引支持(global index + local index):实现了二级索引来提升非主键字段查询的性能。
- 在数据层完成计算,server端的coprocessor执行聚合。
- 检测scan语句最佳的开始和结束的key。
- skip scan功能提高扫描速度:跳过扫描过滤器来优化IN,LIKE,OR查询。
- 下推where过滤条件到server端的scan filter上。
- phoenix底层由于使用了Hbase的API,协处理器,过滤器,处理千万级行的数据也只用毫秒或秒级就搞定。
- 优化主键的盐值来均匀分布写压力。
6. Phoenix较HBase优势特性
Phoenix事务Transactions (beta)
HBase只支持行级事务,Phoenix通过与Apache Tephra集成,增加了对跨行、跨表的事务ACID语义的支持。HBase本身在行层次和区层次上支持强一致性,Tephra额外提供交叉区、交叉表的一致性来支持可扩展性。此外,Tephra通MVCC提供并发事务的快照隔离。
Phoenix时间戳timestamp
一般情况下,应用程序会让HBase管理时间戳。在某些特殊情况下,应用程序也需要自己来控制时间戳。当应用程序需自己控制时间戳时,可以在与HBase连接时指定CurrentSCN属性,以控制任何DDL、DML或查询的时间戳。
Phoenix提供了一种将HBase原生的row timestamp映射到Phoenix列的方法。这样有利于充分利用HBase提供的针对存储文件时间范围的各种优化,以及Phoenix内置的各种查询优化。
二.Phoenix部署
基础环境:
-
zookeeper-3.4.5-cdh5.14.0
-
hadoop-2.6.0-cdh5.14.0
-
hbase-1.2.0-cdh5.14.0
安装包下载:
http://phoenix.apache.org/download.html
http://archive.apache.org/dist/phoenix/apache-phoenix-4.14.0-cdh5.14.2/bin/apache-phoenix-4.14.0-cdh5.14.2-bin.tar.gz
解压安装包,更改文件夹名
tar -zxvf /devinkim/softwares/apache-phoenix-4.14.0-cdh5.14.2-bin.tar.gz -C /devinkim/servers/
mv apache-phoenix-4.14.0-cdh5.14.2-bin phoenix-4.14.0-cdh5.14.2-bin
复制phoenix安装目录下的client和server包到hbase的安装目录下的lib包内
cp /devinkim/servers/phoenix-4.14.0-cdh5.14.2-bin/phoenix-4.14.0-cdh5.14.2-client.jar /devinkim/servers/hbase-1.2.0-cdh5.14.0/lib/
cp /devinkim/servers/phoenix-4.14.0-cdh5.14.2-bin/phoenix-4.14.0-cdh5.14.2-server.jar /devinkim/servers/hbase-1.2.0-cdh5.14.0/lib/
注意:HBase的每个节点都要有这两个包
配置phoenix的环境变量
vim /etc/profile
export PHOENIX_HOME=/devinkim/servers/phoenix-4.14.0-HBase-1.2-bin
export PHOENIX_CLASSPATH=$PHOENIX_HOME
export PATH=$PATH:$PHOENIX_HOME/bin
source /etc/profile
三.phoenix shell
1. 启动phoenix客户端
# 启动客户端
/devinkim/servers/phoenix-4.14.0-cdh5.14.2-bin/bin/sqlline.py node01:2181
# 退出客户端
!quit
注意:如果是连接zookeeper集群,写法:node01,node02,node03:2181
2. 表操作
- 建表必须指定主键
- 表名、字段名默认会转为大写,若使用小写,表名、字段名需加双引号
-- 查看表
!tables
-- 创建STU表 表名、字段名为大写
create table stu (
id integer not null primary key,
name varchar
);
-- 创建person表 表名、字段名为小写,使用联合主键,my_pk作为主键别名
create table "person" (
"id" varchar,
"name" varchar,
"age" varchar
constraint my_pk primary key ("id","name")
);
-- 插入/更新一条数据到person表
upsert into "person" values('1','zhangsan','20');
upsert into stu values(2,'lisi');
-- 查询person表数据
select * from "person";
select * from stu;
-- 删除插入person表中的数据
delete from "person" where "id" = 1;
-- 查看表结构
!describe stu
!describe "person"
-- 删除表
drop table stu;
drop table "person";
HBase查看表
describe 'STU'
describe 'person'
scan 'person'
3. 表映射
在HBase中建一张user表,包含info、data两个列族
#若表存在先删除表
disable 'user_view'
drop 'user_view'
#建表
create 'user_view', 'info', 'data'
#添加一条数据
put 'user_view', 'rk0001', 'info:name', 'zhangsan'
put 'user_view', 'rk0001', 'data:age', 20
# 查看表
scan 'user_view'
disable 'user_table'
drop 'user_table'
create 'user_table', 'info', 'data'
put 'user_table', 'rk0001', 'info:name', 'wangwu'
put 'user_table', 'rk0001', 'data:age', 25
scan 'user_table'
创建phoenix视图
-- age 列必须使用varchar类型,使用integer和bigint类型会报错
create view "user_view"(
"id" varchar primary key,
"info"."name" varchar,
"data"."age" varchar
);
select * from "user_view";
-- 视图是只读类型的,不能增删改,下面upset语句会报错。需在HBase中操作
upsert into "user_view" values('rk0002','李四','18');
-- 删除视图,HBase中的表依然存在
drop view "user_view";
创建phoenix表
-- age 列必须使用varchar类型,使用integer和bigint类型会报错
create table "user_table"(
"id" varchar primary key,
"info"."name" varchar,
"data"."age" varchar
) column_encoded_bytes=0;
select * from "user_table";
-- 表可以操作数据
upsert into "user_table" values('rk0002','李四','18');
-- 删除phoenix中的表,HBase中的表也会被删除
drop table "user_table";
四.Phoenix集成HBase创建二级索引
在HBase中,只有一个单一的按照字典序排序的rowKey索引。使用rowKey数据查询速度较快;但是,若使用filter来对全表进行扫描(不使用rowKey来查询),则会在很大程度上降低检索性能。鉴于此,Phoenix提供了二级索引技术,来应对这种使用rowKey之外的条件进行检索的场景。
- 索引最常用的三个类型:全局索引、覆盖索引、本地索引。
- 索引遵循最左前缀原则:即(a,b,c)三列索引,查询条件中包含a=?、 a = ? and b =? 、a=? and b=? and c=? 可以使用索引,但访问 b =? 、c=? 、b=? and c=?则无法使用索引(请参照下面的演示案例)。
- 索引表会带来空间和数据写入的性能消耗。建议索引表最多10个以下,一般推荐2-3个二级索引。
总之,一般在满足查询的情况下,索引尽可能的越少越好。由于遵循最左前缀原因,不要重复创建索引。比如创建了索引(a,b,c)则就不需要创建(a),(a,b)索引了。
修改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>
在每一个Master的hbase-site.xml中加入如下的属性
<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>
在phoenix客户端创建worker表
-- drop table "worker";
create table "worker"(
"id" varchar primary key,
"info"."name" varchar,
"info"."sex" varchar,
"data"."age" varchar
);
upsert into "worker" values('1001','jack','male','16');
upsert into "worker" values('1002','rose','female','17');
upsert into "worker" values('1003','devin','male','18');
select * from "worker";
1. 全局索引
Global Index是默认的索引格式,创建全局索引本质上是在HBase中建立一张新表,因此,全局索引适用于读多写少的业务场景。因为在向HBase表写数据的时候,索引表也会更新,索引表的数据也是分布在不同数据节点,跨节点数据传输会造成较大性能消耗。采用全局索引在读数据时,Phoenix会尽量选择索引表来降低查询消耗的时间。若查询字段不是索引字段,则索引表不会被使用,也就不会带来查询速度的提升。
创建单个字段的全局索引,此操作会把表中已存在的数据读取一遍写入索引表
-- drop index "worker_index_name" on "worker";
create index "worker_index_name" on "worker" ("info"."name");
在HBase中,使用list命令可看到新增了一张表worker_index_name就是这个索引表,查看表数据如下(注意row key变化)
hbase(main):007:0> scan 'worker_index_name'
ROW COLUMN+CELL
devin\x001003 column=0:\x00\x00\x00\x00, timestamp=1594732987893, value=x
jack\x001001 column=0:\x00\x00\x00\x00, timestamp=1594732987893, value=x
rose\x001002 column=0:\x00\x00\x00\x00, timestamp=1594732987893, value=x
3 row(s) in 0.0310 seconds
使用 name 作为查询条件,查询不同字段时SCAN的表和SCAN的方式不同,如下
-- 结果: RANGE SCAN OVER worker_index_name ['jack']
explain select "id","name" from "worker" where "name" = 'jack';
explain select "name" from "worker" where "name" = 'jack';
-- 结果: FULL SCAN OVER worker
explain select "age" from "worker" where "name" = 'jack';
explain select "name","age" from "worker" where "name" = 'jack';
explain select * from "worker" where "name" = 'jack';
2. 覆盖索引
Phoenix支持覆盖索引功能。访问的列如果是覆盖索引的列,不需要去访问主表即可查询到所需要的数据。
-- drop index "worker_index_name_age" on "worker";
create index "worker_index_name_age" on "worker" ("info"."name") include ("data"."age");
-- 添加多个字段时
-- drop index "worker_index_name_age_sex" on "worker";
-- create index "worker_index_name_age_sex" on "worker" ("info"."name") include ("data"."age","info"."sex");
分别使用name和age列作为查询条件,SCAN方式如下
-- 结果: RANGE SCAN OVER worker_index_name ['jack']
explain select "id","name" from "worker" where "name" = 'jack';
-- 结果: RANGE SCAN OVER worker_index_name_age ['jack']
explain select "id","name","age" from "worker" where "name" = 'jack';
explain select "id","name","age" from "worker" where "name" = 'jack' and "age" = '20';
--结果: FULL SCAN OVER worker_index_name_age
explain select "id","name" from "worker" where "age" = '17';
explain select "id","name","age" from "worker" where "age" = '17';
--结果: FULL SCAN OVER worker
explain select * from "worker" where "age" = '17';
3. 本地索引
Local Index 的索引数据 和 HBase的表数据 是存放在 同一个表(且是同一个Region中),避免了写操作的时候往不同服务器的索引表中写数据带来的额外的性能开销。查询的字段不是索引字段,索引表也会被使用,这会带来查询速度的提升。
与Global indexing一样,Phoenix会自动判定查询是否使用索引。与Global indexing不同的地方,也即适用于写操作频繁的原因,具体如下:
- 使用Local indexing时,索引数据和数据表的数据存放在相同的服务器中。这样也就避免了在写操作时,往不同服务器的索引表中写索引而带来的额外开销。
- 使用Local indexing时,本地索引和主表的数据存储在同一个表中,使用隐藏的列族来存储所以数据。并且,一个数据表的所有索引数据都存储在一个单一的独立的可共享的表中。
- 使用Local indexing时,即使查询的字段不是索引字段,索引表也会被使用。这就会带来查询速度的提升,这点跟Global indexing不同。
在HBase中创建一个分区表
# 分了5个区,可查看 http://node01:60010/table.jsp?name=teacher
create 'teacher','info','data',SPLITS => ['1000','2000','3000','4000']
put 'teacher', '1001', 'info:name', 'zhangsan'
put 'teacher', '1001', 'data:age', 25
scan 'teacher'
在Phoenix创建关联表
-- drop table "teacher";
create table "teacher" (
"id" varchar primary key,
"info"."name" varchar,
"data"."age" varchar
)column_encoded_bytes=0;
select * from "teacher";
在Phoenix创建本地索引
create local index "teacher_index_name" on "teacher"("info"."name");
在hbase中查看表,未发现新创建teacher_index_name表,再次 scan ‘teacher’ 多出了一行数据(创建本地索引前后扫描teacher表的对比如下)
hbase(main):013:0> scan 'teacher'
ROW COLUMN+CELL
1001 column=data:age, timestamp=1594779691823, value=25
1001 column=info:name, timestamp=1594779691236, value=zhangsan
1 row(s) in 0.8720 seconds
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 创建 teacher_index_name 索引前 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 创建 teacher_index_name 索引后 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
hbase(main):014:0> scan 'teacher'
ROW COLUMN+CELL
1000\x00\x00zhangsan\x001001 column=L#0:_0, timestamp=1594779691823, value=_0
1001 column=data:age, timestamp=1594779691823, value=25
1001 column=info:_0, timestamp=1594779691823, value=
1001 column=info:name, timestamp=1594779691236, value=zhangsan
2 row(s) in 0.0360 seconds
注意:1000\x00\x00zhangsan\x001001 这个rowkey,最前面的1000是指数据所属的分区,同时也可保证索引数据与原数据所属同一个分区。可以使用 upsert into “teacher” values (‘2008’,‘lisi’,‘24’); 来测试验证。
五.Phoenix 视图 view
Phoenix支持标准的SQL视图语法(支持可更新的视图),这样就可以在同一张底层HBase物理表上创建多个虚拟表。利用HBase的无模式功能,可以向其添加列。所有视图都共享相同的底层物理HBase表,甚至可以独立编制索引。
六.Phoenix 散步表 Salted Tables
1. Salted Tables场景
在密码学中,在散列中加入字符串的方式称为“加盐”,以增加额外的安全性。在Phoenix中,“加盐”是指对pk对应的byte数组插入特定的byte数据。
Phoenix的Salted Tables技术(加盐)能解决HBASE读写热点问题。例如,单调递增rowkey数据的持续写入,使得负载集中在某一个RegionServer上从而引起的热点问题。
2. Salted Tables原理
加盐的过程就是在原来key的基础上增加一个byte作为前缀,计算公式如下:
new_row_key =(++index % BUCKETS_NUMBER) + original_key
自增rowkey通过加盐被打散写入到各个region中,参见如下图所示。
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 JDBC
1. 基于cdh5.14.2的Phoenix JDBC
pom依赖
<repositories>
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/nexus/content/groups/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.3.1</version>
</dependency>
<!--https://mvnrepository.com/artifact/org.apache.phoenix/phoenix-core-->
<!-- CDH的HBase依赖包会与此冲突 -->
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-core</artifactId>
<version>4.14.0-cdh5.14.2</version>
</dependency>
</dependencies>
基础代码实现
import java.sql.*;
public class LinkTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1. 定义参数,不需要用户名和密码
String driver = "org.apache.phoenix.jdbc.PhoenixDriver";
// 多个Zookeeper节点时: jdbc:phoenix:node02,node03:2181
String url = "jdbc:phoenix:node01:2181";
// 2. 加载驱动
Class.forName(driver);
// 3. 创建连接
Connection connection = DriverManager.getConnection(url);
// 4. 预编译SQL
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM \"user_table\"");
// 5. 执行
ResultSet resultSet = preparedStatement.executeQuery();
// 6. 打印数据
while (resultSet.next()) {
System.out.println(resultSet.getString(1)
+ "\t" + resultSet.getString(2)
+ "\t" + resultSet.getString(3)
);
}
// 7. 关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
2. 基于hdp3.1.0的Phoenix JDBC
注意事项:
ZooKeeper Znode Parent
的属性值必须为/hbase
,因为Phoenix JDBC驱动包会连接集群的Zookeeper,默认去/hbase/hbaseid目录下去查询数据,而ambari集群ZooKeeper Znode Parent
的默认属性值是/hbase-unsecure
,会导致链接查不到相关数据。
pom
<repositories>
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
</repository>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
<repository>
<id>jboss</id>
<url>http://repository.jboss.com/nexus/content/groups/public</url>
</repository>
<repository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>hortonworks.extrepo</id>
<name>Hortonworks HDP</name>
<url>http://repo.hortonworks.com/content/repositories/releases</url>
</repository>
<repository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>hortonworks.other</id>
<name>Hortonworks Other Dependencies</name>
<url>http://repo.hortonworks.com/content/groups/public</url>
</repository>
</repositories>
<!-- <hadoop.version>3.1.1.3.1.0.0-78</hadoop.version>-->
<!-- <hbase.version>2.0.2.3.1.0.0-78</hbase.version>-->
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<!--<version>3.0.0</version>-->
<version>3.1.1.3.1.0.0-78</version>
</dependency>
<dependency>
<groupId>org.apache.phoenix</groupId>
<artifactId>phoenix-core</artifactId>
<version>5.0.0.3.1.0.0-78</version>
<!--<version>5.0.0-HBase-2.0</version>-->
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<!--<testSourceDirectory>src/test/scala</testSourceDirectory>-->
<plugins>
<!-- 指定编译java的插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
</plugin>
<!-- 指定编译scala的插件 -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<useFile>false</useFile>
<disableXmlReport>true</disableXmlReport>
<includes>
<include>**/*Test.*</include>
<include>**/*Suite.*</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
maven本地仓库
链接:https://pan.baidu.com/s/14jmVfmH7uLpR_YIg7IFxHg
提取码:c5b9
log4j.properties
log4j.rootLogger=info,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p - %m%n
java api实现
import java.sql.*;
/**
* User: Devin Kim
* Date: 2020/8/10 15:14
* todo Description:
*/
public class FirstLink {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1. 定义参数,不需要用户名和密码
String driver = "org.apache.phoenix.jdbc.PhoenixDriver";
// jdbc 的 url 类似为 jdbc:phoenix [ :<zookeeper quorum> [ :<port number> ] [ :<root node> ] ],
// 需要引用三个参数:hbase.zookeeper.quorum、hbase.zookeeper.property.clientPort、and zookeeper.znode.parent,
// 这些参数可以缺省不填而在 hbase-site.xml 中定义。
String url = "jdbc:phoenix:ambari01,ambari02,ambari03:2181";
// 2. 加载驱动
Class.forName(driver);
// 3. 创建连接
Connection connection = DriverManager.getConnection(url);
System.out.println(connection);
// 4. 预编译SQL
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM T1");
// 5. 执行
ResultSet resultSet = preparedStatement.executeQuery();
// 6. 打印数据
while (resultSet.next()) {
System.out.println(resultSet.getString(1)
// + "\t" + resultSet.getString(2)
// + "\t" + resultSet.getString(3)
);
}
// 7. 关闭资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}