查找有关 Apache Phoenix 及其部署的常见问题解答。
Phoenix 可以用于 ETL 用例吗?
是的。Apache Phoenix 用于 OLTP(在线事务处理)用例,而不是 OLAP(在线分析处理)用例。不过,您可以将 Phoenix 用于实时数据摄取作为主要用例。
Phoenix 部署的典型架构是什么?
典型的 Phoenix 部署具有以下内容:
- 应用
- Phoenix 客户端/JDBC 驱动程序
- HBase 客户端
Phoenix 客户端/JDBC 驱动程序本质上是一个 Java 库,您应该将其包含在您的 Java 代码中。Phoenix 使用 HBase 作为存储,类似于 HBase 使用 HDFS 作为存储的方式。但是,Phoenix 的抽象还没有完成,例如为了实现访问控制,您需要在包含 Phoenix 数据的底层 HBase 表上设置 ACL。
是否有适用于 Phoenix JDBC 服务器的大小指南?
对于 Phoenix 应用程序,您必须遵循与 HBase 相同的大小调整指南。有关 Phoenix 性能调优的更多信息,请访问 https://phoenix.apache.org/tuning_guide.html。
我可以管理对 Phoenix 服务器的访问吗?
是的,您可以使用 Kerberos 进行身份验证。您可以使用 HBase 授权配置授权。
我可以在 Phoenix 表中看到单个单元格的时间戳吗?这是常用的东西吗?
您可以将 HBase 的本机行时间戳映射到 Phoenix 列。通过这样做,您可以利用 HBase 为存储文件的时间范围提供的各种优化以及 Phoenix 内置的各种查询优化功能。
有关更多信息,请参阅https://phoenix.apache.org/rowtimestamp.html
如果 Phoenix 索引是异步构建的,并且在索引期间将数据添加到表中怎么办?
Phoenix 在全局索引维护期间执行本地索引以防止死锁。:当索引更新失败时,Phoenix 还会部分自动重建索引 ( PHOENIX-1112 )。
序列如何在Phoenix工作?
序列是一个标准的 SQL 特性,它允许生成通常用于形成 ID 的单调递增数字。
有关更多信息,请参阅https://phoenix.apache.org/sequences.html。
当 RegionServer 出现故障时,Phoenix 写入会发生什么?
写入是持久的,持久性由提交到磁盘(在预写日志中)的 WRITE 定义。因此,在 RegionServer 发生故障的情况下,可以通过重放 WAL 来恢复写入。“完整”写入是已从 WAL 刷新到 HFile 的写入。任何失败都将表示为异常。
我可以在 Phoenix 中进行批量数据加载吗?
是的,您可以在 Phoenix 中进行批量插入。有关更多信息,请参阅https://phoenix.apache.org/bulk_dataload.html。
我可以使用标准 HBase API 访问 Phoenix 创建的表吗?
是的,但不推荐或不支持。数据是由 Phoenix 编码的,因此您必须对数据进行解码才能读取。直接写入 HBase 表会导致 Phoenix 损坏。
我可以将 Phoenix 表映射到现有的 HBase 表上吗?
是的,只要使用 Phoenix 数据类型。您必须使用异步索引并手动更新它们,因为 Phoenix 不会知道任何更新。
Phoenix JDBC URL 语法是什么?
Thick驱动器
Phoenix (Thick) Driver JDBC URL 语法如下(方括号中的元素是可选的):
代码语言:javascript复制jdbc:phoenix:[comma-separated ZooKeeper Quorum [:port [:hbase root znode [:kerberos_principal [:path to kerberos keytab] ] ] ]
最简单的网址是:
jdbc:phoenix:localhost
而最复杂的 URL 是:
代码语言:javascript复制jdbc:phoenix:zookeeper1.domain,zookeeper2.domain,zookeeper3.domain:2181:/hbase-1:phoenix@EXAMPLE.COM:/etc/security/keytabs/phoenix.keytab
请注意,URL 中的每个可选元素都需要之前的所有可选元素。例如,以指定根HBase的Z序节点,动物园管理员端口必须也被指定。
此信息最初包含在索引页上。
Thin驱动器
Phoenix Thin Driver(与 Phoenix Query Server 一起使用)JDBC URL 语法如下:
代码语言:javascript复制jdbc:phoenix:thin:[key=value[;key=value...]]
有许多密钥公开供客户端使用。最常用的键是:url和serialization。该网址的关键是需要与Phoenix网查询服务器进行交互。
最简单的网址是:
代码语言:javascript复制jdbc:phoenix:thin:url=http://localhost:8765
其中非常复杂的 URL 是:
代码语言:javascript复制jdbc:phoenix:thin:url=http://queryserver.domain:8765;serialization=PROTOBUF;authentication=SPENGO;principal=phoenix@EXAMPLE.COM;keytab=/etc/security/keytabs/phoenix.keytab
有关瘦客户端 JDBC URL 中受支持选项的完整列表,请参阅Apache Avatica 文档,或参阅查询服务器文档
有没有办法在Phoenix批量加载?
CSV
可以使用名为 psql 的内置实用程序批量加载 CSV 数据。典型的 upsert 速率是每秒 20K - 50K 行(取决于行的宽度)。
用法示例: 使用 psql 创建表$ psql.py [zookeeper] ../examples/web_stat.sql
Upsert CSV 批量数据$ psql.py [zookeeper] ../examples/web_stat.csv
如何将 Phoenix 表映射到现有的 HBase 表?
您可以通过 CREATE TABLE/CREATE VIEW DDL 语句在预先存在的 HBase 表上创建 Phoenix 表或视图。在这两种情况下,我们将保留 HBase 元数据原样。对于 CREATE TABLE,我们将创建任何尚不存在的元数据(表、列族)。我们还将为每一行添加一个空键值,以便查询按预期运行(无需在扫描期间投影所有列)。
另一个警告是字节序列化的方式必须与 Phoenix 的字节序列化方式相匹配。对于 VARCHAR、CHAR 和 UNSIGNED_* 类型,我们使用 HBase Bytes 方法。CHAR 类型只需要单字节字符,UNSIGNED 类型需要大于或等于零的值。对于有符号类型(TINYINT、SMALLINT、INTEGER 和 BIGINT),Phoenix 将翻转第一位,以便负值排在正值之前。因为 HBase 按字典顺序对行键进行排序,负值的第一位是 1 而正值是 0,所以如果我们不翻转第一位,负值就会“大于”正值。因此,如果您通过 HBase 本机 API 存储整数并希望通过 Phoenix 访问它们,请确保您的所有数据类型都是 UNSIGNED 类型。
我们的复合行键是通过简单地将值连接在一起形成的,在可变长度类型之后使用一个零字节字符作为分隔符。
如果您像这样创建 HBase 表:
代码语言:javascript复制create 't1', {NAME => 'f1', VERSIONS => 5}
那么你有一个名为“t1”的 HBase 表和一个名为“f1”的列族。请记住,在 HBase 中,您不会对可能的 KeyValues 或行键的结构进行建模。这是您在 Phoenix 中指定的超出表和列族的信息。
因此,在 Phoenix 中,您将创建一个如下所示的视图:
代码语言:javascript复制CREATE VIEW "t1" ( pk VARCHAR PRIMARY KEY, "f1".val VARCHAR )
“pk”列声明您的行键是 VARCHAR(即字符串),而“f1”.val 列声明您的 HBase 表将包含具有列族和列限定符“f1”:VAL 的键值,并且它们的值将是一个 VARCHAR。
请注意,如果您使用所有大写名称创建 HBase 表,则不需要双引号(因为这是 Phoenix 通过大写字母对字符串进行规范化的方式)。例如,与:
代码语言:javascript复制create 'T1', {NAME => 'F1', VERSIONS => 5}
你可以创建这个Phoenix视图:
代码语言:javascript复制CREATE VIEW t1 ( pk VARCHAR PRIMARY KEY, f1.val VARCHAR )
或者,如果您正在创建新的 HBase 表,只需让 Phoenix 像这样为您做所有事情(根本不需要使用 HBase shell。):
代码语言:javascript复制CREATE TABLE t1 ( pk VARCHAR PRIMARY KEY, val VARCHAR )
有没有优化Phoenix的技巧?
使用Salting提高读/写性能 Salting 可以通过将数据预先拆分到多个区域来显着提高读/写性能。尽管在大多数情况下加盐会产生更好的性能。
例子:
代码语言:javascript复制CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY,DESCRIPTION VARCHAR) SALT_BUCKETS=16
注意:理想情况下,对于具有四核 CPU 的 16 个区域服务器集群,选择 32-64 之间的盐桶以获得最佳性能。
- 每个拆分表 Salting 会自动进行表拆分,但如果您想精确控制表拆分发生的位置而不添加额外字节或更改行键顺序,那么您可以预先拆分表。
例子:
代码语言:javascript复制CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY,DESCRIPTION VARCHAR) SPLIT ON ('CS','EU','NA')
- 使用多个列族
列族在单独的文件中包含相关数据。如果您查询使用选定的列,那么将这些列组合在一个列族中以提高读取性能是有意义的。
例子:
下面的 create table DDL 将创建两个列 faimiles A 和 B。
创建表测试(MYKEY VARCHAR NOT NULL PRIMARY KEY、A.COL1 VARCHAR、A.COL2 VARCHAR、B.COL3 VARCHAR)
- 使用压缩磁盘压缩可提高大型表的性能
例子:
代码语言:javascript复制CREATE TABLE TEST (HOST VARCHAR NOT NULL PRIMARY KEY, description VARCHAR) COMPRESSION='GZ'
如何在表上创建二级索引?
从 Phoenix 2.1 版开始,Phoenix 支持对可变和不可变数据进行索引。请注意,Phoenix 2.0.x 仅支持对不可变数据进行索引。不可变表的索引写入性能指标比可变表稍快,但不可变表中的数据无法更新。
例子
- 创建表
不可变表:
代码语言:javascript复制create table test (mykey varchar primary key, col1 varchar, col2 varchar) IMMUTABLE_ROWS=true;
可变表:
代码语言:javascript复制create table test (mykey varchar primary key, col1 varchar, col2 varchar);
在 col2 上创建索引
代码语言:javascript复制create index idx on test (col2)
- 在 col1 上创建索引并在 col2 上创建覆盖索引
create index idx on test (col1) include (col2)
Upsert 这个测试表中的行,Phoenix 查询优化器会选择正确的索引来使用。如果 Phoenix 正在使用索引表,您可以在解释计划中看到。您还可以在 Phoenix 查询中提示使用特定索引。
为什么我的二级索引没有被使用?
除非查询中使用的所有列都在其中(作为索引或覆盖的列),否则不会使用二级索引。构成数据表主键的所有列都将自动包含在索引中。
示例:
代码语言:javascript复制DDL create table usertable (id varchar primary key, firstname varchar, lastname varchar); create index idx_name on usertable (firstname);
查询:
代码语言:javascript复制DDL select id, firstname, lastname from usertable where firstname = 'foo';
在这种情况下不会使用索引,因为姓氏不是索引或覆盖列的一部分。这可以通过查看解释计划来验证。要修复此创建具有索引的姓氏部分或覆盖列的索引。示例:
代码语言:javascript复制create idx_name on usertable (firstname) include (lastname);
Phoenix有多快?为什么这么快?
Phoenix很快。100M 行的全表扫描通常在 20 秒内完成(中型集群上的窄表)。如果查询包含键列上的过滤器,这个时间会减少到几毫秒。对于非键列或非前导键列上的过滤器,您可以在这些列上添加索引,通过制作带有索引列的表的副本作为键的一部分,从而获得与对键列进行过滤等效的性能。
为什么即使进行全扫描,Phoenix 也很快:
- Phoenix 使用区域边界将您的查询分块,并使用可配置的线程数在客户端上并行运行它们
- 聚合将在服务器端的协处理器中完成,合并返回给客户端的数据量,而不是全部返回。
如何连接到安全的 HBase 集群?
查看 Anil Gupta 的精彩帖子http://bigdatanoob.blogspot.com/2013/09/connect-phoenix-to-secure-hbase-cluster.html
如何连接在 Hadoop-2 上运行的 HBase?
Hadoop-2 配置文件存在于 Phoenix pom.xml 中。
phoenix 是否可以像 HBase API 一样灵活地处理具有任意时间戳的表?
默认情况下,Phoenix 让 HBase 管理时间戳,并只显示所有内容的最新值。然而,Phoenix 也允许用户提供任意时间戳。为此,您需要在连接时指定“CurrentSCN”,如下所示:
代码语言:javascript复制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 执行此操作:
代码语言:javascript复制myTable.put(Bytes.toBytes('a'),ts);
通过指定 CurrentSCN,您告诉 Phoenix 您希望在该时间戳完成该连接的所有操作。请注意,这也适用于在连接上完成的查询 - 例如,上面 myTable 上的查询不会看到它刚刚插入的数据,因为它只能看到在其 CurrentSCN 属性之前创建的数据。这提供了一种执行快照、闪回或时间点查询的方法。
请记住,创建新连接并不是一项昂贵的操作。相同的底层 HConnection 用于到同一个集群的所有连接,因此它或多或少类似于实例化一些对象。
为什么我的查询不进行范围扫描?
代码语言:javascript复制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'; . 请注意,您可以在“pk2”和“pk3”列上添加二级索引,这将导致对第一个查询(通过索引表)进行范围扫描。
DEGENERATE SCAN 意味着查询不可能返回任何行。如果我们可以在编译时确定这一点,那么我们甚至不必费心运行扫描。
FULL SCAN 意味着将扫描表的所有行(如果您有 WHERE 子句,则可能会应用过滤器)
SKIP SCAN 意味着将扫描表中的一个子集或所有行,但是它会根据过滤器中的条件跳过大组行。有关更多详细信息,请参阅此博客。如果前导主键列没有过滤器,我们不会执行 SKIP SCAN,但您可以使用 / SKIP_SCAN / 提示强制执行 SKIP SCAN 。在某些情况下,即当您的前导主键列的基数较低时,它会比 FULL SCAN 更有效。
我应该池化 Phoenix JDBC 连接吗?
不,没有必要将 Phoenix JDBC 连接池化。
由于底层的 HBase 连接,Phoenix 的 Connection 对象与大多数其他 JDBC Connection 不同。Phoenix Connection 对象被设计为一种创建成本低的薄对象。如果重复使用 Phoenix Connections,则底层 HBase 连接可能不会始终处于前一个用户的健康状态。最好创建新的 Phoenix Connections 以确保避免任何潜在问题。
为 Phoenix 实现池化可以简单地通过创建一个委托 Connection 来完成,该连接在从池中检索时实例化一个新的 Phoenix 连接,然后在将其返回到池中时关闭连接(参见PHOENIX-2388)。
为什么 Phoenix 在执行 upsert 时会添加一个空的/虚拟的 KeyValue?
需要空的或虚拟的 KeyValue(列限定符为 _0)以确保给定的列可用于所有行。
您可能知道,数据作为 KeyValues 存储在 HBase 中,这意味着为每个列值存储完整的行键。这也意味着除非存储了至少一列,否则根本不存储行键。
现在考虑具有整数主键的 JDBC 行和几个全为空的列。为了能够存储主键,需要存储一个 KeyValue 以表明该行完全存在。此列由您注意到的空列表示。这允许执行“SELECT * FROM TABLE”并接收所有行的记录,即使是那些非 pk 列为空的记录。
即使某些(或所有)记录只有一列为空,也会出现同样的问题。Phoenix 上的扫描将包括空列,以确保仅包含主键(并且所有非键列都为 null)的行将包含在扫描结果中。
原文链接:http://phoenix.apache.org/faq.html
https://docs.cloudera.com/cdp-private-cloud-base/7.1.6/phoenix-faq/topics/phoenix-faq.html