Apache Phoenix(一)答疑

2020-01-16  本文已影响0人  我知他风雨兼程途径日暮不赏
  1. 我想开始使用Phoenix,如何编写Phoenix “Hello World”?
  2. Phoenix的JDBC URL语法格式?
  3. 如何批量加载数据至Phoenix?
  4. 如何映射Phoenix表至已有的Hbase表?
  5. 有哪些点关于Phoenix的优化的?
  6. 如何创建二级索引在表中?
  7. 为什么我的二级索引没被使用到?
  8. Phoenix速度多快?为什么这么快?
  9. 我如何更安全的连接Hbase集群?
  10. 我如何连接Hbase跑在Hadoop-2.x上?
  11. Phoenix可以像Hbase带有时间戳灵活的操作表吗?
  12. 我的查询为什么没有进入RANGE SCAN?
  13. 我应该创建Phoenix JDBC连接池吗?
  14. 为什么Phoenix在执行upsert时添加空键值或伪键值?

01. 我想开始使用Phoenix,如何编写Phoenix “Hello World”

1. 命令行使用

$ sqlline.py [zookeeper]
> create table test (mykey integer not null primary key, mycolumn varchar);
> upsert into test values (1,'Hello');
> upsert into test values (2,'World!');
> select * from test;
+-------+------------+
| MYKEY |  MYCOLUMN  |
+-------+------------+
| 1     | Hello      |
| 2     | World!     |
+-------+------------+

2. JAVA语法

创建test.java 文件包含如下内容:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.PreparedStatement;
import java.sql.Statement;

public class test {

    public static void main(String[] args) throws SQLException {
        Statement stmt = null;
        ResultSet rset = null;
        
        Connection con = DriverManager.getConnection("jdbc:phoenix:[zookeeper]");
        stmt = con.createStatement();
        
        stmt.executeUpdate("create table test (mykey integer not null primary key, mycolumn varchar)");
        stmt.executeUpdate("upsert into test values (1,'Hello')");
        stmt.executeUpdate("upsert into test values (2,'World!')");
        con.commit();
        
        PreparedStatement statement = con.prepareStatement("select * from test");
        rset = statement.executeQuery();
        while (rset.next()) {
            System.out.println(rset.getString("mycolumn"));
        }
        statement.close();
        con.close();
    }
}

在命令行中编译和执行

$ javac test.java
$ java -cp "../phoenix-[version]-client.jar:." test
Hello World!

02. Phoenix的JDBC URL语法格式?

1. Thick Driver

Phoenix Thick驱动的JDBC的URL格式如下([]内的元素可选择的)

jdbc:phoenix:[comma-separated ZooKeeper Quorum [:port [:hbase root znode [:kerberos_principal [:path to kerberos keytab] ] ] ]

最简单的例子:

jdbc:phoenix:localhost

最复杂的例子:

jdbc:phoenix:zookeeper1.domain,zookeeper2.domain,zookeeper3.domain:2181:/hbase-1:phoenix@EXAMPLE.COM:/etc/security/keytabs/phoenix.keytab

请注意,URL中的每个可选元素都需要前面所有可选元素。例如,去指定Hbase的zk节点,那么zk的端口必须是指定的。
这些信息最初包含在该页

2. Thin Driver

Phoenix Thin驱动(与Phoenix查询服务一起使用)JDBC格式如下:

jdbc:phoenix:thin:[key=value[;key=value...]]

这里暴露了数个key供客户端使用。最经常使用的key为urlserialization。url key直接影响到需要查询的Phoenix查询服务器的地址。
最简单的URL例子:

jdbc:phoenix:thin:url=http://localhost:8765

非常复杂的URL例子:

jdbc:phoenix:thin:url=http://queryserver.domain:8765;serialization=PROTOBUF;authentication=SPENGO;principal=phoenix@EXAMPLE.COM;keytab=/etc/security/keytabs/phoenix.keytab

这里记录着所有有关于Thin 客户端JDBC URL涉及到的支持项,可以参考Apache Avatica documentationQuery Server Documentation

3. 如何批量加载数据至Phoenix?

1. Map Reduce

查看相关例子

2. CSV导入

CSV数据可以批量导入通过psql的通用功能。一般插入的速度在20K-50K行数据每秒。(取决于一行数据有多大)。
创建表例子:

 $ psql.py [zookeeper] ../examples/web_stat.sql

插入数据例子:

$ psql.py [zookeeper] ../examples/web_stat.csv

4. 如何映射Phoenix表至已有的Hbase表?

你可以创建Phoenix 表(table)和视图(view)通过 CREATE TABLE/CREATE VIEW 在已存在的HBase表上执行DLL语句。两种情况下都不会改变Hbase的元数据。对于CREATE TABLE,我们会创建任何不存在的元数据(table,column families)。我们会增加空键在每一行,所以查询的行为符合预期(不需要所有的列都进行扫描)。
需要注意的是字节的序列化必须匹配Phoenix的字节序列化。比如varchar、char和unsigned_类型,我们使用Hbase字节方法。char类型预期只有单字节字符和无符号类型预期值大于或者等于0。对于有符号的类型(tinyint,smallint,integer 和bigint),phoenix会翻转第一个字节,这样负值会在正值之前排序。因为Hbase排序row key按照ASCII正序排列,负值的第一位是1 而正值的第一位是0,那么负值大于正值,党我们不进行第一位翻转时。所以你存储整数时通过Hbase本地API接口和想通过Phoenix访问整数,确认你的数据类型是否是无符号类型。
我们复合row key可以将简单的值进行拼接在一起,在可变长类型上使用0字节作为分隔符。
如果你创建Hbase表如下:

create 't1', {NAME => 'f1', VERSIONS => 5}

如果你已经有了一张带有名称为't1'和列簇为'f1'的Hbase表。那么请记住,在Hbase中你不用对可能的键值和row key结构进行建模。这是你在phoenix指定的表和列簇之外的信息。所以在phoenix,你不需要像这样创建view:

CREATE VIEW "t1" ( pk VARCHAR PRIMARY KEY, "f1".val VARCHAR )

这个‘pk’列定义,你的row key是varchar类型(类似字符串),而'f1'.val列定义你的Hbase表包含键值形式和列成员及列限定符为'f1'.val还有他们的类型为varchar。
注意,你不需要使用双引号,当你创建你的hbase表包含所有的大写名称(Phoenix会标准化字符串,将字符串转大写)。比如:

create 'T1', {NAME => 'F1', VERSIONS => 5}

你可以这样创建phoenix视图:

CREATE VIEW t1 ( pk VARCHAR PRIMARY KEY, f1.val VARCHAR )

或者如果你创建了新的hbase表,不需要借助hbase shell,直接在phoenix操作。

CREATE TABLE t1 ( pk VARCHAR PRIMARY KEY, val VARCHAR )

5. 关于Phoenix的优化有哪些点?

CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, DESCRIPTION VARCHAR) SALT_BUCKETS=16

注意:理想情况下4核CPU的16个region server,可以选择salt buckets在32-64最佳性能。

CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, DESCRIPTION VARCHAR) SPLIT ON ('CS','EU','NA')
CREATE TABLE TEST (MYKEY VARCHAR NOT NULL PRIMARY KEY, A.COL1 VARCHAR, A.COL2 VARCHAR, B.COL3 VARCHAR)
CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, DESCRIPTION VARCHAR) COMPRESSION='GZ'

6. 如何创建二级索引在表中?

始于Phoenix 2.1版本,Phoenix开始支持索引在可变和不可变的数据中。注意,Phoenix在2.0.x中仅支持不可变数据的索引。索引写性能在不可变索引表中,稍微快一些于可变数据表,不可变数据表即是数据不支持更新。
例子:

不可变表: create table test (mykey varchar primary key, col1 varchar, col2 varchar) IMMUTABLE_ROWS=true;
可变表:create table test (mykey varchar primary key, col1 varchar, col2 varchar);
create index idx on test (col2);
create index idx on test (col1) include (col2);

更新行信息在test表中,Phoenix查询优化器会选择正确的索引去使用。你可以查看explain plan如果Phoenix使用这个索引表。你还可以在Phoenix查询中给出使用特定索引的提示

7. 为什么我的二级索引没被使用到?

除非查询中使用到的所有列都在二级索引中,否作不会使用二级索引(作为直接索引,或者覆盖索引)。构成数据表中的主键全部列都会自动包含在索引中。

DLL例子:
create table usertable (id varchar primary key, firstname varchar, lastname varchar); create index idx_name on usertable (firstname);
查询DLL:
select id, firstname, lastname from usertable where firstname = 'foo';

在lastname不是索引或者覆盖所有一部分时,索引是没有被使用到。这点可以在"explain plan"中被证实。在创建索引时固定lastname字段存在索引中一部分,或者时覆盖索引的覆盖字段。
例子:

create idx_name on usertable (firstname) include (lastname);

8.Phoenix速度有多快?为什么这么快?

Phoenix速度非常快,全表扫描100M的数据,通常情况下在20秒内完成(中等规模的集群上的小表)。如果查询包含到关键列的过滤器,则降低到毫秒级。你可以添加索引在导致性能等同于过滤键的列上,使用索引列作为键的一部分复制表。
为什么Phoenix即使做了全扫描,速度依然快:

9.我如何更安全的连接Hbase集群

检阅Anil Gupta的优秀文章

10. 我如何连接Hbase跑在Hadoop-2.x上?

Hadoop2的配置存在Phoenix的pom.xml中即可。

11. Phoenix可以像Hbase带有时间戳灵活的操作表吗?

在默认情况下,Phoenix让Hbase管理时间戳,并只让你查看最后的版本数据。但是,Phoenix也给用户提供支持任意的时间戳。要做到这一点,使用连接属性"CurrentSCN",像这样:

Properties props = new Properties();
props.setProperty("CurrentSCN", Long.toString(ts));
Connection conn = DriverManager.connect(myUrl, props);

conn.createStatement().execute("UPSERT INTO myTable VALUES ('a')");
conn.commit();

上面的操作等同于Hbase API中的:

myTable.put(Bytes.toBytes('a'),ts);

通过指定CurrentSCN,你可以告诉Phoenix。所有东西在这次连接中按该时间戳被完成。注意的是,这也适用于对连接执行的查询。举个例子,一个查询在myTable表的数据是不会看到刚插入的数据,因为它只看到创建连接时指定CurrentSCN属性之前的数据。这样提供了一种类似于快照的功能,或者是时间点查询。
请记住,创建个新的连接不是一个很昂贵的操作。相同的底层Hbase的连接用于全部连接的相同的集群,所以或多或少实例出一些对象。

12. 我的查询为什么没有进入RANGE SCAN?

DDL: CREATE TABLE TEST (pk1 char(1) not null, pk2 char(1) not null, pk3 char(1) not null, non-pk varchar CONSTRAINT PK PRIMARY KEY(pk1, pk2, pk3));

RANGE SCAN :意味着只扫描表中一部分数据。如果使用主键约束中的一个或者多个组成主键的列会出现这种情况。没有过滤PK列的查询,例子:select * from test where pk2='x' and pk3='y';将会出现全扫描,然而在select * from test where pk1='x' and pk2='y';中却是范围扫描(range scan)。注意,你可以添加二级索引在"pk2"和"pk3"列上,会触发范围扫描(range scan)在第一次查询中。
DEGENERATE SCAN:意味着一个查询不能返回行数据。如果我们决定在编译时完成。我们甚至不需要运行扫描。
FULL SCAN:意味着所有行都被扫描到。(如果你有一个where子句,可能会应用到一个过滤器)
SKIP SCAN:将扫描一个表中的子集或者全部行。无论如何它将会根据过滤器跳过大分组的行。可以查看博客了解更多。如果主键上没有过滤器的列,那我们就不做SKIP SCAN,但是你可以强制SKIP SCAN通过使用/+ SKIP_SCAN/去命中。在一些条件下,也就是说当你的主键基数比较少时,它将会更有效超过FULL SCAN。

13. 我应该创建Phoenix JDBC连接池吗?

不,它不是必须的Phoenix jDBC连接池。
Phoenix的连接对象时不同大部分的JDBC连接,原因在于底层是Hbase的连接。Phoenix连接对象在设计时就是为了更精细便宜的代价去创建。如果Phoenix的连接被再利用,底层的HBase连接可能并不总是由前一个用户保持在健康状态。更好的方式是去创建一个新的Phoenix连接,保证避免潜在的问题。
实行Phoenix连接池可以简单的创建实例化委派,对外部而言,创建出新的Phoenix连接,实际是需要时从池中获取,对外部而言的关闭,实际是返回到池中去,可以参考Phoenix-2388

14. 为什么Phoenix在执行upsert时添加空键值或伪键值?

空键值或者伪键值(_01列限定符)必须的,去保证列都有有效的在全部行中。
数据存储在Hbase是以键值对形式存在,意味着存储每个列值完整的行键。这还意味着,除非至少存储了一个列,否则根本不存储行键。
现在考虑到JDBC的行存在int类型的主键,和若干列可能是空(null)的。为了可以存储主键,键值必须去存储去展示这行数据的全部(现在的时间戳,考虑的时间戳版本)。你会注意到这些列是空列。这允许执行“SELECT * FROM TABLE”并接收所有行的记录,即使那些非pk列为空的记录也是如此。
即使对于某些(或所有)记录只有一个列为空,也会出现相同的问题。在Phoenix上的扫描将包括空列,以确保只包含主键的行(所有非键列都为空)将包含在扫描结果中。

上一篇 下一篇

猜你喜欢

热点阅读