1. 程式人生 > >Phoenix綜述(史上最全Phoenix中文文件)

Phoenix綜述(史上最全Phoenix中文文件)

1. Phoenix定義

Phoenix最早是saleforce的一個開源專案,後來成為Apache基金的頂級專案。

Phoenix是構建在HBase上的一個SQL層,能讓我們用標準的JDBC APIs而不是HBase客戶端APIs來建立表,插入資料和對HBase資料進行查詢。

put the SQL back in NoSQL

Phoenix完全使用Java編寫,作為HBase內嵌的JDBC驅動。Phoenix查詢引擎會將SQL查詢轉換為一個或多個HBase掃描,並編排執行以生成標準的JDBC結果集。直接使用HBase API、協同處理器與自定義過濾器,對於簡單查詢來說,其效能量級是毫秒,對於百萬級別的行數來說,其效能量級是秒。

HBase的查詢工具有很多,如:Hive、Tez、Impala、Spark SQL、Phoenix等。

Phoenix通過以下方式使我們可以少寫程式碼,並且效能比我們自己寫程式碼更好:

  • 將SQL編譯成原生的HBase scans。
  • 確定scan關鍵字的最佳開始和結束
  • 讓scan並行執行
  • ...

使用Phoenix的公司

Paste_Image.png

2. 歷史演進

  • 3.0/4.0 release

ARRAY Type. 支援標準的JDBC陣列型別

Sequences. 支援 CREATE/DROP SEQUENCE, NEXT VALUE FOR, CURRENT VALUE FOR也實現了

Multi-tenancy. 同一張HBase物理表上,不同的租戶可以建立相互獨立的檢視

Views. 同一張HBase物理表上可以建立不同的檢視

  • 3.1/4.1 release

Apache Pig Loader . 通過pig來處理資料時支援pig載入器來利用Phoenix的效能

Derived Tables. 允許在一個FROM子句中使用SELECT子句來定義一張衍生表

Local Indexing. 後面介紹

Tracing. 後面介紹

  • 3.2/4.2 release

Subqueries 支援在WHERE和FROM子句中的獨立子查詢和相關子查詢

Semi/anti joins. 通過標準的[NOT] IN 和 [NOT] EXISTS關鍵字來支援半/反連線

Optimize foreign key joins. 通過利用跳躍掃描過濾器來優化外來鍵連線

Statistics Collection. 通過收集表的統計資訊來提高並行查詢能力

  • 3.3/4.3 release

Many-to-many joins. 支援兩邊都太大以至於無法放進記憶體的連線

Map-reduce Integration. 支援Map-reduce整合

Functional Indexes. 後面介紹

  • 4.4 release

User Defined Functions. 後面介紹

  • 4.5 release

Asynchronous Index Population. 通過一個Map-reduce job,索引可以被非同步建立

  • 4.6 release

Time series Optimization. 優化針對時間序列資料的查詢

  • 4.7 release

Transaction Support. 後面介紹

  • 4.8 release

DISTINCT Query Optimization. 使用搜索邏輯來大幅提高 SELECT DISTINCT 和 COUNT DISTINCT的查詢效能

Local Index Improvements. Reworked 後面介紹

Hive Integration. 能夠在Phoenix內使用Hive來支援大表和大表之間的連線

Namespace Mapping. 將Phoenix schema對映到HBase的名稱空間來增強不同schema之間的隔離性

3. 特性

3.1 Transactions (beta) 事務

該特性還處於beta版,並非正式版。通過整合Tephra,Phoenix可以支援ACID特性。Tephra也是Apache的一個專案,是事務管理器,它在像HBase這樣的分散式資料儲存上提供全域性一致事務。HBase本身在行層次和區層次上支援強一致性,Tephra額外提供交叉區、交叉表的一致性來支援可擴充套件性。

要想讓Phoenix支援事務特性,需要以下步驟:

  • 配置客戶端hbase-site.xml
<property>
  <name>phoenix.transactions.enabled</name>
  <value>true</value>
</property>
  • 配置服務端hbase-site.xml
<property>
  <name>data.tx.snapshot.dir</name>
  <value>/tmp/tephra/snapshots</value>
</property>

<property>
  <name>data.tx.timeout</name>
  <value>60</value>
  <description> set the transaction timeout (time after which open transactions become invalid) to a reasonable value.</description>
</property>
  • 配置$HBASE_HOME並啟動Tephra
./bin/tephra

通過以上配置,Phoenix已經支援了事務特性,但建立表的時候預設還是不支援的。如果想建立一個表支援事務特性,需要顯示宣告,如下:

CREATE TABLE my_table (k BIGINT PRIMARY KEY, v VARCHAR) TRANSACTIONAL=true;

就是在建表語句末尾增加 TRANSACTIONAL=true

原本存在的表也可以更改成支援事務的,需要注意的是,事務表無法改回非事務的,因此更改的時候要小心。一旦改成事務的,就改不回去了。

ALTER TABLE my_other_table SET TRANSACTIONAL=true;

3.2 User-defined functions(UDFs) 使用者定義函式

3.2.1 概述

Phoenix從4.4.0版本開始支援使用者自定義函式。

使用者可以建立臨時或永久的使用者自定義函式。這些使用者自定義函式可以像內建的create、upsert、delete一樣被呼叫。臨時函式是針對特定的會話或連線,對其他會話或連線不可見。永久函式的元資訊會被儲存在一張叫做SYSTEM.FUNCTION的系統表中,對任何會話或連線均可見。

3.2.2 配置

  • hive-site.xml
<property>
  <name>phoenix.functions.allowUserDefinedFunctions</name>
  <value>true</value>
</property>
<property>
  <name>fs.hdfs.impl</name>
  <value>org.apache.hadoop.hdfs.DistributedFileSystem</value>
</property>
<property>
  <name>hbase.rootdir</name>
  <value>${hbase.tmp.dir}/hbase</value>
  <description>The directory shared by region servers and into
    which HBase persists.  The URL should be 'fully-qualified'
    to include the filesystem scheme.  For example, to specify the
    HDFS directory '/hbase' where the HDFS instance's namenode is
    running at namenode.example.org on port 9000, set this value to:
    hdfs://namenode.example.org:9000/hbase.  By default, we write
    to whatever ${hbase.tmp.dir} is set too -- usually /tmp --
    so change this configuration or else all data will be lost on
    machine restart.</description>
</property>
<property>
  <name>hbase.dynamic.jars.dir</name>
  <value>${hbase.rootdir}/lib</value>
  <description>
    The directory from which the custom udf jars can be loaded
    dynamically by the phoenix client/region server without the need to restart. However,
    an already loaded udf class would not be un-loaded. See
    HBASE-1936 for more details.
  </description>
</property>

後兩個配置需要跟hbse服務端的配置一致。

以上配置完後,在JDBC連線時還需要執行以下語句:

Properties props = new Properties();
props.setProperty("phoenix.functions.allowUserDefinedFunctions", "true");
Connection conn = DriverManager.getConnection("jdbc:phoenix:localhost", props);

以下是可選的配置,用於動態類載入的時候把jar包從hdfs拷貝到本地檔案系統

<property>
  <name>hbase.local.dir</name>
  <value>${hbase.tmp.dir}/local/</value>
  <description>Directory on the local filesystem to be used
    as a local storage.</description>
</property>

3.3 Secondary Indexing 二級索引

在HBase中,只有一個單一的按照字典序排序的rowKey索引,當使用rowKey來進行資料查詢的時候速度較快,但是如果不使用rowKey來查詢的話就會使用filter來對全表進行掃描,很大程度上降低了檢索效能。而Phoenix提供了二級索引技術來應對這種使用rowKey之外的條件進行檢索的場景。

  • Covered Indexes

只需要通過索引就能返回所要查詢的資料,所以索引的列必須包含所需查詢的列(SELECT的列和WHRER的列)

  • Functional Indexes

從Phoeinx4.3以上就支援函式索引,其索引不侷限於列,可以合適任意的表示式來建立索引,當在查詢時用到了這些表示式時就直接返回表示式結果

  • Global Indexes

Global indexing適用於多讀少寫的業務場景。
使用Global indexing的話在寫資料的時候會消耗大量開銷,因為所有對資料表的更新操作(DELETE, UPSERT VALUES and UPSERT SELECT),會引起索引表的更新,而索引表是分佈在不同的資料節點上的,跨節點的資料傳輸帶來了較大的效能消耗。在讀資料的時候Phoenix會選擇索引表來降低查詢消耗的時間。在預設情況下如果想查詢的欄位不是索引欄位的話索引表不會被使用,也就是說不會帶來查詢速度的提升。

  • Local Indexes

Local indexing適用於寫操作頻繁的場景。
與Global indexing一樣,Phoenix會自動判定在進行查詢的時候是否使用索引。使用Local indexing時,索引資料和資料表的資料是存放在相同的伺服器中的避免了在寫操作的時候往不同伺服器的索引表中寫索引帶來的額外開銷。使用Local indexing的時候即使查詢的欄位不是索引欄位索引表也會被使用,這會帶來查詢速度的提升,這點跟Global indexing不同。一個數據表的所有索引資料都儲存在一個單一的獨立的可共享的表中。

3.4 Statistics Collection 統計資訊收集

UPDATE STATISTICS可以更新某張表的統計資訊,以提高查詢效能

3.5 Row timestamp 時間戳

從4.6版本開始,Phoenix提供了一種將HBase原生的row timestamp對映到Phoenix列的方法。這樣有利於充分利用HBase提供的針對儲存檔案的時間範圍的各種優化,以及Phoenix內建的各種查詢優化。

3.6 Paged Queries 分頁查詢

Phoenix支援分頁查詢:

  • Row Value Constructors (RVC)
  • OFFSET with limit

3.7 Salted Tables 散步表

如果row key是自動增長的,那麼HBase的順序寫會導致region server產生資料熱點的問題,Phoenix的Salted Tables技術可以解決region server的熱點問題

3.8 Skip Scan 跳躍掃描

可以在範圍掃描的時候提高效能

3.9 Views 檢視

標準的SQL檢視語法現在在Phoenix上也支援了。這使得能在同一張底層HBase物理表上建立多個虛擬表。

3.10 Multi tenancy 多租戶

通過指定不同的租戶連線實現資料訪問的隔離

3.11 Dynamic Columns 動態列

Phoenix 1.2, specifying columns dynamically is now supported by allowing column definitions to included in parenthesis after the table in the FROM clause on a SELECT statement. Although this is not standard SQL, it is useful to surface this type of functionality to leverage the late binding ability of HBase.

3.12 Bulk CSV Data Loading 大量CSV資料載入

載入CSV資料到Phoenix表有兩種方式:1. 通過psql命令以單執行緒的方式載入,資料量少的情況下適用。 2. 基於MapReduce的bulk load工具,適用於資料量大的情況

3.13 Query Server 查詢伺服器

Phoenix4.4引入的一個單獨的伺服器來提供thin客戶端的連線

3.14 Tracing 追蹤

從4.1版本開始Phoenix增加這個特性來追蹤每條查詢的蹤跡,這使使用者能夠看到每一條查詢或插入操作背後從客戶端到HBase端執行的每一步。

3.15 Metrics 指標

Phoenix提供各種各樣的指標使我們能夠知道Phoenix客戶端在執行不同SQL語句的時候其內部發生了什麼。這些指標在客戶端JVM中通過兩種方式來收集:

  • Request level metrics - collected at an individual SQL statement
    level
  • Global metrics - collected at the client JVM level

4. 架構和組成

  • Phoenix架構

Phoenix Architecture.png

  • Phoenix在Hadoop生態系統中的位置

位置.png

5. 資料儲存

Phoenix將HBase的資料模型對映到關係型世界

Data Model.png

6. 對QL的支援

支援的命令如下:

  • SELECT
Example:

SELECT * FROM TEST LIMIT 1000;
SELECT * FROM TEST LIMIT 1000 OFFSET 100;
SELECT full_name FROM SALES_PERSON WHERE ranking >= 5.0 UNION ALL SELECT reviewer_name FROM CUSTOMER_REVIEW WHERE score >= 8.0
  • UPSERT VALUES
Example:

UPSERT INTO TEST VALUES('foo','bar',3);
UPSERT INTO TEST(NAME,ID) VALUES('foo',123);
  • UPSERT SELECT
Example:

UPSERT INTO test.targetTable(col1, col2) SELECT col3, col4 FROM test.sourceTable WHERE col5 < 100
UPSERT INTO foo SELECT * FROM bar;
  • DELETE
Example:

DELETE FROM TEST;
DELETE FROM TEST WHERE ID=123;
DELETE FROM TEST WHERE NAME LIKE 'foo%';
  • CREATE TABLE
CREATE TABLE my_schema.my_table ( id BIGINT not null primary key, date)
CREATE TABLE my_table ( id INTEGER not null primary key desc, date DATE not null,m.db_utilization DECIMAL, i.db_utilization) m.DATA_BLOCK_ENCODING='DIFF'
CREATE TABLE stats.prod_metrics ( host char(50) not null, created_date date not null,txn_count bigint CONSTRAINT pk PRIMARY KEY (host, created_date) )
CREATE TABLE IF NOT EXISTS "my_case_sensitive_table"
    ( "id" char(10) not null primary key, "value" integer)
    DATA_BLOCK_ENCODING='NONE',VERSIONS=5,MAX_FILESIZE=2000000 split on (?, ?, ?)
CREATE TABLE IF NOT EXISTS my_schema.my_table (org_id CHAR(15), entity_id CHAR(15), payload binary(1000),CONSTRAINT pk PRIMARY KEY (org_id, entity_id) )TTL=86400
  • DROP TABLE
Example:

DROP TABLE my_schema.my_table;
DROP TABLE IF EXISTS my_table;
DROP TABLE my_schema.my_table CASCADE;
  • CREATE FUNCTION
Example:

CREATE FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction' using jar 'hdfs:/localhost:8080/hbase/lib/myjar.jar'
CREATE FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction'
CREATE FUNCTION my_increment(integer, integer constant defaultvalue='10') returns integer as 'com.mypackage.MyIncrementFunction' using jar '/hbase/lib/myincrement.jar'
CREATE TEMPORARY FUNCTION my_reverse(varchar) returns varchar as 'com.mypackage.MyReverseFunction' using jar 'hdfs:/localhost:8080/hbase/lib/myjar.jar'
  • DROP FUNCTION
Example:

DROP FUNCTION IF EXISTS my_reverse
DROP FUNCTION my_reverse
  • CREATE VIEW
Example:

CREATE VIEW "my_hbase_table"( k VARCHAR primary key, "v" UNSIGNED_LONG) default_column_family='a';
CREATE VIEW my_view ( new_col SMALLINT ) AS SELECT * FROM my_table WHERE k = 100;
CREATE VIEW my_view_on_view AS SELECT * FROM my_view WHERE new_col > 70;
  • DROP VIEW
Example:

DROP VIEW my_view
DROP VIEW IF EXISTS my_schema.my_view
DROP VIEW IF EXISTS my_schema.my_view CASCADE
  • CREATE SEQUENCE
Example:

CREATE SEQUENCE my_sequence;
CRE