原文地址:https://cloud.tencent.com/developer/article/1498057javascript
來源: 雲棲社區java
做者: 瑾謙node
By 大數據技術與架構sql
文章簡介:Phoenix是一個開源的HBASE SQL層。它不只可使用標準的JDBC API替代HBASE client API建立表,插入和查詢HBASE,也支持二級索引、事物以及多種SQL層優化。數據庫
此係列文章將會從Phoenix的語法和功能特性、相關工具、實踐經驗以及應用案例多方面從淺入深的闡述。但願對Phoenix入門、在作架構設計和技術選型的同窗能有一些幫助。express
關鍵詞:Phoenix Hbase SQLapache
Phoenix做爲應用層和HBASE之間的中間件,如下特性使它在大數據量的簡單查詢場景有着獨有的優點。json
通常可使用如下三種方式訪問Phoenix數組
CREATE TABLE IF NOT EXISTS us_population ( state CHAR(2) NOT NULL, city VARCHAR NOT NULL, population BIGINT CONSTRAINT my_pk PRIMARY KEY (state, city));
NY,New York,8143197 CA,Los Angeles,3844829 IL,Chicago,2842518 TX,Houston,2016582 PA,Philadelphia,1463281 AZ,Phoenix,1461575 TX,San Antonio,1256509 CA,San Diego,1255540 TX,Dallas,1213825 CA,San Jose,912332
SELECT state as "State",count(city) as "City Count",sum(population) as "Population Sum" FROM us_population GROUP BY state ORDER BY sum(population) DESC;
./psql.py <your_zookeeper_quorum> us_population.sql us_population.csv us_population_queries.sql
<dependencies> <dependency> <groupId>com.aliyun.phoenix</groupId> <artifactId>ali-phoenix-core</artifactId> <version>${version}</version> </dependency> </dependencies>
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
目前Phoenix支持24種簡單數據類型和1個一維Array的複雜類型。如下是對支持數據類型的說明:緩存
類型名
序號 |
對應的java類型 |
取值範圍 |
說明 |
|
---|---|---|---|---|
1 |
INTEGER |
INTEGER |
[-2147483648, 2147483647] |
binary表示是4個byte的整數, 符號位被翻轉(爲了讓負數排在正數前面) |
2 |
UNSIGNED_INT |
Integer |
[ 0,2147483647] |
binary表示是4個byte的整型。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(int)方法。 |
3 |
BIGINT |
Long |
[-9223372036854775808 ,9223372036854775807] |
binary表示是8位byte的Long類型, 符號位被翻轉(爲了讓負數排在正數前面) |
4 |
UNSIGNED_LONG |
Long |
[0 ,9223372036854775807] |
binary表示是8位byte的Long類型。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(long)方法。 |
5 |
TINYINT |
Byte |
[-128,127] |
binary表示是單個byte,爲了排序符號位被翻轉。 |
6 |
UNSIGNED_TINYINT |
Byte |
[0,127] |
binary表示是單個byte。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配 HBase Bytes.toBytes(byte)方法。 |
7 |
SMALLINT |
Short |
[-32768,32767] |
binary表示是兩個byte,爲了排序符號位被翻轉。 |
8 |
UNSIGNED_SMALLINT |
Short |
[0,32767] |
binary表示是兩個byte。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(short)方法。 |
9 |
FLOAT |
Float |
[-3.402823466 E + 38,3.402823466 E + 38] |
binary表示是四個byte, 爲了排序符號位被翻轉。 |
10 |
UNSIGNED_FLOAT |
Float |
[0,3.402823466 E + 38] |
binary表示是四個byte。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(float)方法。 |
11 |
DOUBLE |
DOUBLE |
[-1.7976931348623158 E + 308,1.7976931348623158 E + 308] |
binary表示是8個byte,爲了排序符號位被翻轉。 |
12 |
UNSIGNED_DOUBLE |
DOUBLE |
[0,1.7976931348623158 E + 308] |
binary表示是8個byte。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(double)方法。 |
13 |
DECIMAL(precision,scale) |
BigDecimal |
最大精度38位 |
binary是可比較的邊長格式。若是用於rowkey。當它不是最後一列時,比較終結符號是null byte |
14 |
BOOLEAN |
BOOLEAN |
0或1 |
binary表示0是flase, 1是true |
15 |
TIME |
java.sql.Time |
格式:yyyy-MM-dd hh:mm:ss |
二進制表示是8位byte的long類型數據, 數據內容是客戶端時區自1970-01-01 00:00:00 UTC到如今的毫秒大小(GMT)。此類型與 SQL 92中的Time類型不兼容 |
16 |
DATE |
java.sql.Date |
格式:yyyy-MM-dd hh:mm:ss |
二進制表示是8位byte的long類型數據, 數據內容是客戶端時區自1970-01-01 00:00:00 UTC到如今的毫秒大小(GMT)。此類型與 SQL 92中的DATE類型不兼容。 |
17 |
TIMESTAMP |
java.sql.Timestamp |
格式:yyyy-MM-dd hh:mm:ss[.nnnnnnnnn] |
二進制表示是8位byte的long類型和4位整型納秒。8位byte的long類型數據是客戶端時區自1970-01-01 00:00:00 UTC到如今的毫秒大小(GMT)。 |
18 |
UNSIGNED_TIME |
java.sql.Time |
格式:yyyy-MM-dd hh:mm:ss |
二進制表示是8位byte的long類型數據, 數據內容是客戶端時區自1970-01-01 00:00:00 UTC到如今的毫秒大小(GMT)。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(long)方法。 |
19 |
UNSIGNED_DATE |
java.sql.Date |
格式:yyyy-MM-dd hh:mm:ss |
二進制表示是8位byte的long類型數據, 數據內容是客戶端時區自1970-01-01 00:00:00 UTC到如今的毫秒大小(GMT)。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(long)方法。 |
20 |
UNSIGNED_TIMESTAMP |
java.sql.Timestamp |
格式:yyyy-MM-dd hh:mm:ss[.nnnnnnnnn] |
二進制表示是8位byte的long類型和4位整型納秒。8位byte的long類型數據是客戶端時區自1970-01-01 00:00:00 UTC到如今的毫秒大小(GMT)。這個類型主要用做序列化映射到已經存在Hbase表的數據,適配HBase Bytes.toBytes(long)方法。 |
21 |
VARCHAR(precisionInt) |
java.lang.String |
變長,可選最大長度 |
對應UTF-8字符經過HBase Bytes.toBytes(String)轉換的二進制。若是用於rowkey。當它不是最後一列時,比較終結符號是null byte |
22 |
CHAR ( precisionInt ) |
java.lang.String |
定長 |
對應UTF-8字符經過HBase Bytes.toBytes(String)轉換的二進制。 |
23 |
BINARY ( precisionInt ) |
byte[] |
定長 |
定長byte數組 |
24 |
VARBINARY |
byte[] |
變長 |
變長byte數組 |
25 |
ARRAY [dimension] |
java.sql.Array |
- |
Java原始類型數組,只支持一維數組。例如:VARCHAR ARRAY, CHAR(10) ARRAY [5],INTEGER [],INTEGER [100] |
從一個或者多個表中查詢數據。 LIMIT(或者FETCH FIRST) 在ORDER BY子句後將轉換爲top-N查詢。 OFFSET子句指定返回查詢結果前跳過的行數。
示例
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語義有異於標準SQL中的Insert,當寫入值不存在時,表示寫入數據,不然更新數據。其中列的聲明是能夠省略的,當省略時,values指定值的順序和目標表中schema聲明列的順序須要一致。
ON DUPLICATE KEY是4.9版本中的功能,表示upsert原子寫入的語義,在寫入性能上弱於非原子語義。相同的row在同一batch中按照執行順序寫入。
示例
UPSERT INTO TEST VALUES('foo','bar',3); UPSERT INTO TEST(NAME,ID) VALUES('foo',123); UPSERT INTO TEST(ID, COUNTER) VALUES(123, 0) ON DUPLICATE KEY UPDATE COUNTER = COUNTER + 1; UPSERT INTO TEST(ID, MY_COL) VALUES(123, 0) ON DUPLICATE KEY IGNORE;
從另一張表中讀取數據寫入到目標表中,若是數據存在則更新,不然插入數據。插入目標表的值順序和查詢表指定查詢字段一致。當auto commit被打開而且select子句沒有聚合時,寫入目標表這個過程是在server端完成的,不然查詢的數據會先緩存在客戶端再寫入目標表中(phoenix.mutate.upsertBatchSize表示從客戶端一次commit的行數,默認10000行)。
示例
UPSERT INTO test.targetTable(col1, col2) SELECT col3, col4 FROM test.sourceTable WHERE col5 < 100 UPSERT INTO foo SELECT * FROM bar;
刪除選定的列。若是auto commit打開,刪除操做將在server端執行。
示例
DELETE FROM TABLENAME;
DELETE FROM TABLENAME WHERE PK=123; DELETE FROM TABLENAME WHERE NAME LIKE '%';
在密碼學中,加鹽是指在散列以前將散列內容(例如:密碼)的任意固定位置插入特定的字符串。這個在散列中加入字符串的方式稱爲「加鹽」。其做用是讓加鹽後的散列結果和沒有加鹽的結果不相同,在不一樣的應用情景中,這個處理能夠增長額外的安全性。而Phoenix中加鹽是指對pk對應的byte數組插入特定的byte數據。
加鹽能解決HBASE讀寫熱點問題,例如:單調遞增rowkey數據的持續寫入,使得負載集中在某一個RegionServer上引發的熱點問題。
在建立表的時候指定屬性值:SALT_BUCKETS
,其值表示所分buckets(region)數量, 範圍是1~256。
CREATE TABLE mytable (my_key VARCHAR PRIMARY KEY, col VARCHAR) SALT_BUCKETS = 8;
加鹽的過程就是在原來key的基礎上增長一個byte做爲前綴,計算公式以下:
new_row_key = ((byte) (hash(key) % BUCKETS_NUMBER) + original_key
以上公式中 BUCKETS_NUMBER 表明建立表時指定的 salt buckets 大小,hash 函數的實際計算方式以下:
public static int hash (byte a[], int offset, int length) { if (a == null) return 0; int result = 1; for (int i = offset; i < offset + length; i++) { result = 31 * result + a[i]; } return result; }
目前HBASE只有基於字典序的主鍵索引,對於非主鍵過濾條件的查詢都會變成掃全表操做,爲了解決這個問題Phoenix引入了二級索引功能。然而此二級索引又有別於傳統關係型數據庫的二級索引,本文將詳細描述了Phoenix中二級索引功能、用法和原理。
示例表以下(爲了可以容易經過HBASE SHELL對照表內容,咱們對屬性值COLUMN_ENCODED_BYTES
設置爲0,不對column family進行編碼):
CREATE TABLE TEST ( ID VARCHAR NOT NULL PRIMARY KEY, COL1 VARCHAR, COL2 VARCHAR ) COLUMN_ENCODED_BYTES=0; upsert into TEST values('1', '2', '3');
全局索引更多的應用在讀較多的場景。它對應一張獨立的HBASE表。對於全局索引,在查詢中檢索的列若是不在索引表中,默認的索引表將不會被使用,除非使用hint。
建立全局索引:
CREATE INDEX IDX_COL1 ON TEST(COL1)
經過HBASE SHELL觀察生成的索引表IDX_COL1
。咱們發現全局索引表的RowKey存儲了索引列的值和原表RowKey的值,這樣編碼更有利於提升查詢的性能。
hbase(main):001:0> scan 'IDX_COL1' ROW COLUMN+CELL 2\x001 column=0:_0, timestamp=1520935113031, value=x 1 row(s) in 0.1650 seconds
實際上全局索引的RowKey將會按照以下格式進行編碼。
SALT_BUCKETS
或者split key。此byte正是存儲着salt。由於本地索引和原數據是存儲在同一個表中的,因此更適合寫多的場景。對於本地索引,查詢中不管是否指定hint或者是查詢的列是否都在索引表中,都會使用索引表。
建立本地索引:
create local index LOCAL_IDX_COL1 ON TEST(COL1);
經過HBASE SHELL觀察表'TEST', 咱們能夠看到表中多了一行column爲L#0:_0的索引數據。
hbase(main):001:0> scan 'TEST' ROW COLUMN+CELL \x00\x002\x001 column=L#0:_0, timestamp=1520935997600, value=_0 1 column=0:COL1, timestamp=1520935997600, value=2 1 column=0:COL2, timestamp=1520935997600, value=3 1 column=0:_0, timestamp=1520935997600, value=x 2 row(s) in 0.1680 seconds
本地索引的RowKey將會按照以下格式進行編碼:
覆蓋索引的特色是把原數據存儲在索引數據表中,這樣在查詢到索引數據時就不須要再次返回到原表查詢,能夠直接拿到查詢結果。
建立覆蓋索引:
create index IDX_COL1_COVER_COL2 on TEST(COL1) include(COL2);
經過HBASE SHELL 查詢表IDX_COL1_COVER_COL2
, 咱們發現include的列的值被寫入到了value中。
hbase(main):003:0> scan 'IDX_COL1_COVER_COL2' ROW COLUMN+CELL 2\x001 column=0:0:COL2, timestamp=1520943893821, value=3 2\x001 column=0:_0, timestamp=1520943893821, value=x 1 row(s) in 0.0180 seconds
對於相似select col2 from TEST where COL1='2'
的查詢,查詢一次索引表就能得到結果。其查詢計劃以下:
+--------------------------------------------------------------------------------------+-----------------+----------------+---+ | PLAN | EST_BYTES_READ | EST_ROWS_READ | E | +--------------------------------------------------------------------------------------+-----------------+----------------+---+ | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER IDX_COL1_COVER_COL2 ['2'] | null | null | n | +--------------------------------------------------------------------------------------+-----------------+----------------+---+
函數索引的特色是能根據表達式建立索引,適用於對查詢表,過濾條件是表達式的表建立索引。例如:
//建立函數索引
CREATE INDEX CONCATE_IDX ON TEST (UPPER(COL1||COL2)) //查詢函數索引 SELECT * FROM TEST WHERE UPPER(COL1||COL2)='23'
Phoenix的二級索引咱們基本上已經介紹過了,咱們回過頭來繼續看Phoenix二級索引的官方定義:Secondary indexes are an orthogonal way to access data from its primary access path
。經過如下例子咱們再理解下這個定義。
TEST
的COL1
建立全局索引CREATE INDEX IDX_COL1 ON TEST(COL1);
select * from TEST where COL1='2';
以上查詢的查詢計劃以下:
+----------------------------------------------------------------+-----------------+----------------+--------------+ | PLAN | EST_BYTES_READ | EST_ROWS_READ | EST_INFO_TS | +----------------------------------------------------------------+-----------------+----------------+--------------+ | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN FULL SCAN OVER TEST | null | null | null | | SERVER FILTER BY COL1 = '2' | null | null | null | +----------------------------------------------------------------+-----------------+----------------+--------------+
select id from TEST where COL1='2';
查詢計劃以下
+---------------------------------------------------------------------------+-----------------+----------------+--------------+ | PLAN | EST_BYTES_READ | EST_ROWS_READ | EST_INFO_TS | +---------------------------------------------------------------------------+-----------------+----------------+--------------+ | CLIENT 1-CHUNK PARALLEL 1-WAY ROUND ROBIN RANGE SCAN OVER IDX_COL1 ['2'] | null | null | null | | SERVER FILTER BY FIRST KEY ONLY | null | null | null | +---------------------------------------------------------------------------+-----------------+----------------+--------------+
兩個查詢都沒有經過hint強制指定索引表,查詢計劃顯示,查詢全部字段時發生了須要極力避免的掃全表操做(通常數據量在幾十萬級別的掃全表很容易形成集羣不穩定),而查詢id時利用索引表走了點查。從現象來看,當查詢中出現的字段都在索引表中時(能夠是索引字段或者數據表主鍵,也能夠是覆蓋索引字段),會自動走索引表,不然查詢會退化爲全表掃描。
在咱們實際應用中一個數據表會有多個索引表,爲了能讓咱們的查詢使用合理的索引表,目前都須要經過Hint去指定。
Phoenix的二級索引建立有同步和異步兩種方式。
CREATE INDEX IDX_COL1 ON TEST(COL1)
時會進行索引數據的同步。此方法適用於數據量較小的狀況。ASYNC
。//建立異步索引
CREATE INDEX ASYNC_IDX ON DB.TEST (COL1) ASYNC //build 索引數據 ${HBASE_HOME}/bin/hbase org.apache.phoenix.mapreduce.index.IndexTool --schema DB --data-table TEST --index-table ASYNC_IDX --output-path ASYNC_IDX_HFILES
在客戶端配置文件hbase-site.xml中,把超時參數設置大一些,足夠build索引數據的時間。
<property> <name>hbase.rpc.timeout</name> <value>60000000</value> </property> <property> <name>hbase.client.scanner.timeout.period</name> <value>60000000</value> </property> <property> <name>phoenix.query.timeoutMs</name> <value>60000000</value> </property>
建議不超過10個
索引表越多寫放大越嚴重。寫放大狀況能夠參考下圖。
1)利用MR對Phoenix表(可帶有二級索引表)進行Bulkload入庫, 其原理是直接生成主表(二級索引表)的HFILE寫入HDFS。相對於走API的數據導入方式,不只速度更快,並且對HBASE集羣的負載也會小不少。目前雲HBASE上的Phoenix支持如下數據源的Bulkload工具:
2)利用MR Building二級索引。當主表數據量較大時,能夠經過建立異步索引,使用MR快速同步索引數據。
因爲雲HBASE上沒有MR,須要藉助外部的計算引擎(自建的HADOOP集羣或者EMR),而使用外部的計算引擎的首先面臨的問題是,如何跨集羣訪問HDFS。 1.因爲雲HBASE的HDFS端口默認是不開的,須要聯繫工做人員開通。 2.端口開通之後,要想順利的訪問HDFS是HA配置的雲HBASE集羣,須要向工做人員獲取雲HBASE的主備(emr-header-1,emr-header-2)namenode host/IP。參考以下配置模板,設置hadoop客戶端配置文件: hdfs-site.xml
<configuration> <property> <name>dfs.nameservices</name> <value>emr-cluster</value> </property> <property> <name>dfs.client.failover.proxy.provider.emr-cluster</name> <value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value> </property> <property> <name>dfs.ha.automatic-failover.enabled.emr-cluster</name> <value>true</value> </property> <property> <name>dfs.ha.namenodes.emr-cluster</name> <value>nn1,nn2</value> </property> <property> <name>dfs.namenode.rpc-address.emr-cluster.nn1</name> <value>{emr-header-1-host}:8020</value> </property> <property> <name>dfs.namenode.rpc-address.emr-cluster.nn2</name> <value>{emr-header-2-host}:8020</value> </property> </configuration>
3.驗證訪問雲HBASE HDFS 在emr或自建集羣上訪問雲HBase集羣
hadoop dfs -ls hdfs://emr-cluster/
以EMR訪問雲HBASE爲例。EMR集羣須要把雲HBASE HDFS的emr-cluster 相關配置和當前EMR的HDFS配置合在一塊兒造成新的配置文件,單獨存放在一個目錄(${conf-dir})下。 經過yarn/hadoop命令的--config參數指定新的配置目錄,使這些配置文件放在CLASSPATH最前面覆蓋掉當前EMR集羣hadoop_conf_dir下的配置,以便bulkload程序能識別到雲HBASE HA的HDFS URL。當在emr或自建集羣上可以訪問本身的HDFS(hadoop --config <confdir> dfs -ls /
), 也可以訪問雲HBase的HDFS(hadoop --config <confdir> dfs -ls hdfs://emr-cluster/
)說明配置成功了。
執行以下BULKLOAD命令
yarn --config ${CONF_DIR} \ jar ${PHOENIX_HOME}/phoenix-${version}-client.jar org.apache.phoenix.mapreduce.CsvBulkLoadTool \ --table "TABLENAME" \ --input "hdfs://emr-header-1.cluster-55090:9000/tmp/test_data" \ --zookeeper "zk1,zk2,zk3" \ --output "hdfs://emr-cluster/tmp/tmp_data"
注意: --output 配置的是雲HBASE的臨時文件,這樣直接把生成的HFILE存儲在雲HBASE的HDFS上,後續的只有簡單的move操做。不然,若是生成在EMR集羣還須要走網絡發送到雲HBASE HDFS上。
在傳統關係型數據庫中設計主鍵時,自增ID常常被使用。不只可以保證主鍵的惟一,同時也能簡化業務層實現。Phoenix怎麼使用自增ID,是咱們這篇文章的重點。
CREATE SEQUENCE [IF NOT EXISTS] SCHEMA.SEQUENCE_NAME [START WITH number] [INCREMENT BY number] [MINVALUE number] [MAXVALUE number] [CYCLE] [CACHE number]
start
用於指定第一個值。若是不指定默認爲1.increment
指定每次調用next value for
後自增大小。若是不指定默認爲1。minvalue
和maxvalue
通常與cycle
連用, 讓自增數據造成一個環,從最小值到最大值,再從最大值到最小值。cache
默認爲100, 表示server端生成100個自增序列緩存在客戶端,能夠減小rpc次數。此值也能夠經過phoenix.sequence.cacheSize
來配置。示例
CREATE SEQUENCE my_sequence;-- 建立一個自增序列,初始值爲1,自增間隔爲1,將有100個自增值緩存在客戶端。 CREATE SEQUENCE my_sequence START WITH -1000 CREATE SEQUENCE my_sequence INCREMENT BY 10 CREATE SEQUENCE my_cycling_sequence MINVALUE 1 MAXVALUE 100 CYCLE; CREATE SEQUENCE my_schema.my_sequence START 0 CACHE 10
DROP SEQUENCE [IF EXISTS] SCHEMA.SEQUENCE_NAME
示例
DROP SEQUENCE my_sequence
DROP SEQUENCE IF EXISTS my_schema.my_sequence
對現有的書籍進行編號並存儲,要求編號是唯一的。存儲書籍信息的建表語句以下:
create table books( id integer not null primary key, name varchar, author varchar )SALT_BUCKETS = 8;
因爲自增ID做爲rowkey, 容易形成集羣熱點問題,因此在建立表時最好經過加鹽的方式解決這個問題
動態列是指在查詢中新增字段,操做建立表時未指定的列。傳統關係型數據要實現動態列目前經常使用的方法有:設計表結構時預留新增字段位置、設計更通用的字段、列映射爲行和利用json/xml存儲字段擴展字段信息等,這些方法多少都存在一些缺陷,動態列的實現只能依賴邏輯層的設計實現。因爲Phoenix是HBase上的SQL層,藉助HBase特性實現的動態列,避免了傳統關係型數據庫動態列實現存在的問題。
示例表(用於語法說明)
CREATE TABLE EventLog ( eventId BIGINT NOT NULL, eventTime TIME NOT NULL, eventType CHAR(3) CONSTRAINT pk PRIMARY KEY (eventId, eventTime)) COLUMN_ENCODED_BYTES=0
在插入數據時指定新增列字段名和類型,並在values對應的位置設置相應的值。語法以下:
upsert into <tableName> (exists_col1, exists_col2, ... (new_col1 time, new_col2 integer, ...)) VALUES (v1, v2, ... (v1, v2, ...))
動態列寫入示例:
UPSERT INTO EventLog (eventId, eventTime, eventType, lastGCTime TIME, usedMemory BIGINT, maxMemory BIGINT) VALUES(1, CURRENT_TIME(), 'abc', CURRENT_TIME(), 512, 1024);
咱們來查詢看一下
查詢發現並沒新增列的數據,也就是經過動態列插入值時並無對錶的schema直接改變。HBase表中發生了怎麼樣的變化呢?
實際上HBase表中已經新增列以及數據。那經過動態列添加的數據怎麼查詢呢?
動態列查詢語法
select [*|table.*|[table.]colum_name_1[AS alias1][,[table.]colum_name_2[AS alias2] …], <dy_colum_name_1>] FROM tableName (<dy_colum_name_1, type> [,<dy_column_name_2, type> ...]) [where clause] [group by clause] [having clause] [order by clause]
動態列查詢示例
SELECT eventId, eventTime, lastGCTime, usedMemory, maxMemory FROM EventLog(lastGCTime TIME, usedMemory BIGINT, maxMemory BIGINT) where eventId=1
查詢結果以下:
Phoneix的動態列功能是非SQL標準語法,它給咱們帶來更多的靈活性,再也不爲靜態schema的字段擴展問題而困擾。然而咱們在實際應用中,應該根據本身的業務需求決定是否真的使用動態列,由於動態列的濫用會大幅度的增長咱們的維護成本。
所謂分頁查詢就是從符合條件的起始記錄,日後遍歷「頁大小」的行。數據庫的分頁是在server端完成的,避免客戶端一次性查詢到大量的數據,讓查詢數據數據分段展現在客戶端。對於Phoenix的分頁查詢,怎麼使用?性能怎麼樣?須要注意什麼?將會在文章中經過示例和數聽說明。
[ LIMIT { count } ] [ OFFSET start [ ROW | ROWS ] ] [ FETCH { FIRST | NEXT } [ count ] { ROW | ROWS } ONLY ]
Limit或者Fetch在order by子句後轉化爲爲top-N的查詢,其中offset子句表示從開始的位置跳過多少行開始掃描。
對於如下的offsset使用示例, 咱們可發現當offset的值爲0時,查詢結果從第一行記錄開始掃描limit指定的行數,當offset值爲1時查詢結果從第二行記錄開始開始掃描limit指定的行數...
0: jdbc:phoenix:localhost> select SS_CUSTOMER_SK from STORE_SALES where SS_ITEM_SK < 3600order by SS_ITEM_SK limit 6; +-----------------+ | SS_CUSTOMER_SK | +-----------------+ | 109734 | | null | | 168740 | | 344372 | | 249078 | | 241017 | +-----------------+ 6 rows selected (0.025 seconds) 0: jdbc:phoenix:localhost> select SS_CUSTOMER_SK from STORE_SALES where SS_ITEM_SK < 3600 order by SS_ITEM_SK limit 3 offset 0; +-----------------+ | SS_CUSTOMER_SK | +-----------------+ | 109734 | | null | | 168740 | +-----------------+ 3 rows selected (0.034 seconds) 0: jdbc:phoenix:localhost> select SS_CUSTOMER_SK from STORE_SALES where SS_ITEM_SK < 3600 order by SS_ITEM_SK limit 3 offset 1; +-----------------+ | SS_CUSTOMER_SK | +-----------------+ | null | | 168740 | | 344372 | +-----------------+ 3 rows selected (0.026 seconds) 0: jdbc:phoenix:localhost> select SS_CUSTOMER_SK from STORE_SALES where SS_ITEM_SK < 3600 order by SS_ITEM_SK limit 3 offset 2; +-----------------+ | SS_CUSTOMER_SK | +-----------------+ | 168740 | | 344372 | | 249078 | +-----------------+ 3 rows selected (0.017 seconds) 0: jdbc