Phoenix系列|創建Phoenix映射表


目前,在公司小部分的業務場景中有用到 Phoenix,但也僅限基於 Phoenix 的二級索引機制來進行查詢上的優化。雖然使用的頻次不大,但偶爾用到時,有些語句的使用方式和注意事項總記不太熟,每次都需要一頓谷歌和百度,然後從五花八門的文章中攝取着自己需要的信息。

本文是 Phoenix 系列的第一篇文章,我打算從 Phoenix 映射表的創建開始說起。Phoenix 映射表的創建雖然看起來很簡單,但其中的細節和潛在的坑着實也不少,且容我慢慢道來。

本文詳細記錄了 Phoenix 映射表的基本創建方式,以及我自己額外的一些思考和測試,如果文章中有描述不當的地方,還望大家多多指教,也歡迎大家在留言區踊躍發言。

1. 我的軟件環境

在開始正式介紹之前,還是先來說明下我 Phoenix 的版本。文中涉及到的關於 Phoenix 的使用小示例,基於的版本分別是PHOENIX-4.14.0-cdh5.13.2PHOENIX-5.0.0-cdh6.2.0,其中兩個 Phoenix 對應的 HBase 的版本分別是HBase 1.2.0-cdh5.13.1HBase 2.1.0-cdh6.3.1。如果沒有特別的說明,所選示例在兩個版本中運行方式以及結果是一樣的。

2. 關於 Phoenix

Phoenix 是一款優秀的基於 HBase 的數據庫中間件,由 Apache 官方出品,圈內共識,Apache 出品,必屬精品,當然,爛尾的除外哈。Phoenix 把用戶輸入的 SQL 語句翻譯成 HBase 底層的查詢 API,簡化了 HBase 客戶端的查詢方式,讓用戶可以使用標準的 SQL 語句來查詢 HBase 表裏的數據。配合 Phoenix 的二級索引機制,Phoenix 可以實現更加方便和複雜的 HBase 數據查詢場景。有關 Phoenix 更深入的介紹,可以前往其官網查看。http://phoenix.apache.org/

3. 在 Phoenix 中創建不存在的 HBase 表的映射表

Phoenix 創建表的語句與 MySql 的語法有點相似,但需要關注的細節也有很多。

create table "user"(
   " id" VARCHAR PRIMARY KEY,
    "info"."name" VARCHAR,
    "info"."age" VARCHAR)
 column_encoded_bytes=0;

把上述建表語句 copy 到 Phoenix 的 shell 下,運行後查看我們創建的表的信息。

同時,在 HBase 的 default 命名空間中,也自動創建出了一張名叫 user 的表。

跟以普通方式創建的 HBase 表相比,除了常規的表描述信息之外,Phoenix 創建的表還多了一些協處理器等的描述信息。望文生義,這些協處理器大致與 Scan、索引創建等等操作有關。

讓我們再來解讀下這個簡單的建表語句,“user”是表名,id 作爲主鍵,也即是 HBase 表中的 RowKey,“info”最終在 HBase 中體現爲表的列簇,“name","age"就是表的字段名,緊隨其後的是列的類型,統一都是 VARCHAR,也即字符串,字段名默認可以爲空,如果強制不爲空,則爲其加上 NOT NULL。"id"因爲要作爲 RowKey,所以一定不能是空的。最後一行 column_encoded_bytes=0 表示需要禁用列映射規則,否則會降低查詢性能,所以一般情況下這裏都置爲 0。

到這裏大家也許會發現,表名、字段名、列簇名都被引號包括,如果我不指定引號呢?讓我們先刪除表,然後重新運行如下的建表語句:

刪除表的操作是 drop table "user",如果待刪除的表不存在,就會拋出異常,如果想要避免表不存在的異常,則需要加上表是否存在的判斷,即運行 drop table if exists "user"。刪除表的同時,底層映射的 HBase 表也一同被刪除,這絕對是你刪庫跑路的利器,所以生產環境下還是謹慎使用吧。

create table user(
   id VARCHAR PRIMARY KEY,
    info.name VARCHAR,
    info.age VARCHAR)
 column_encoded_bytes=0;

然後查看下我們重新創建的表:

首先我們的表名被轉成了大寫,再 select 下數據。

查看下 HBase 表:

如上圖所示,引號的作用應該很明顯了吧,Phoenix 中表名、列簇名、字段名如果不加引號,會被默認轉成大寫。

4. 在 Phoenix 中創建存在的 HBase 表的映射表

接着,我們刪除剛剛創建的 user 表。再來嘗試如下的建表方式。首先在 HBase 的命令行下創建一張名叫 user 的表,其列簇是 info,create user,info此時 user 表就是一個普普通通的 HBase 表,然後運行如下建表語句:

create table "user"(
   id VARCHAR PRIMARY KEY,
    info.name VARCHAR,
    info.age VARCHAR)
 column_encoded_bytes=0;

上述建表語句中我故意把 user 加引號,是爲了確保在 Phoenix 中創建的表就是小寫的名字,而不會在 HBase 中重新生成一張名叫 USER 的表。運行完查看下我們的 HBase 表。多了一個名叫 INFO 的列簇。所以,我們只有嚴格保證這些大小寫一一對應,才能保證 Phoenix 表與 HBase 確立正確一致的映射關係,重新運行如下建表語句。

create table "user"(
   " id" VARCHAR PRIMARY KEY,
    "info"."name" VARCHAR,
    "info"."age" VARCHAR)
 column_encoded_bytes=0;

所以,我們可以先提前創建 HBase 表,再去創建該表的 Phoenix 映射表,保證大小寫一一對應,同樣可以達到如小節 3 中的效果。

5. 創建帶有 schema 的 Phoenix 表

直接運行如下建表語句:

create table "person"."user"(
   " id" VARCHAR PRIMARY KEY,
    "info"."name" VARCHAR,
    "info"."age" VARCHAR)
 column_encoded_bytes=0;

如果這個名叫 person 的 schema 不存在就會遇到建表的異常。

運行 create schema "person"來創建 schema,同樣需要注意大小寫。能正確創建 schema 的前提是需要在 HBase 的服務端和 Phoenix 的客戶端增加如下配置:

<property>
        <name>phoenix.schema.isNamespaceMappingEnabled</name>
        <value>true</value>
</property>
<property>
        <name>phoenix.schema.mapSystemTablesToNamespace</name>
        <value>true</value>
</property>

schema 創建完成之後,我們的表就能被正常創建了,Phoenix 的表創建完畢後,HBase 中會自動創建名爲 person 的 namespace 和名爲 person:user 的表。

6. 創建 Phoenix 表指定預分區

默認情況下創建的 Phoenix 表映射的 HBase 底層表只有一個分區,但是如果你的表數據很大,導數據時一個分區能把你急的肝疼。創建 Phoenix 表時預分區的方法有兩種,一種是提前創建一個具有預分區的 HBase 表,然後再創建 Phoenix 表來映射已經存在的表;另一種則是創建 Phoenix 表的同時指定好預分區的信息。

示例語句如下:

 create table "person"."user"(
   "id" VARCHAR PRIMARY KEY,
    "info"."name" VARCHAR,
    "info"."age" VARCHAR) column_encoded_bytes=0
    SPLIT ON ('1','2','3','4','5','6','7','8','9');

然後在 HBase 的表監控界面上,你就可以看到如下的分區信息:

還有一種比較糟糕情況是,你的 HBase 表提前創建好了,大量數據也導入了表中,此時你纔想起來需要創建 Phoenix 表。在大量數據存在的前提下,在 Phoenix shell 中直接創建映射表,十有八九會遇到客戶端超時的異常,貌似不太影響使用,只是不知道會不會數據映射不完整。目前對於這種情況還沒找到合適的解決辦法,只能在創建表的開始階段就好好規劃下是否需要創建 Phoenix 表以及創建表時的預分區等,儘量避免出現這樣的尷尬狀況。

7. 各種數據寫入後的映射情況

在這一小節裏將說明 HBase 的各種寫入方式寫入的數據能否在 Phoenix 中正常綁定,以及 Phoenix 端數據寫入後能否在 HBase 表中順利查到。

對於 HBase 的數據寫入方式,我在這裏主要分成 bulkload 和普通的 hbase client api 寫入,首先來測試下 client api 的數據寫入方式,簡單起見我直接在 shell 下 put 一些數據。

再來查看 Phoenix 中的數據,數據正常綁定。

接着測試在 phoenix 表插入一些數據,然後觀察 HBase 的數據變化。

 upsert into "person"."user" ("id","name","age") values('2''leo2','17');

就不貼圖了,Phoenix 和 HBase 兩邊的數據是一一對應的。

那繼續來測試 bulkload 的方式導入數據,然後觀察 Phoenix 和 HBase 兩邊的數據情況吧!

測試之前,我們先用truncate_preserve 'person:user'命令清空 user 表裏的數據。

準備一些測試數據,然後上傳至 hdfs 的某一個測試目錄。

使用如下命令先把 csv 文件轉換成 hfile:

hbase org.apache.hadoop.hbase.mapreduce.ImportTsv -Dimporttsv.separator="," -Dimporttsv.bulk.output=/user/leo_jie/user_test_hfile -Dimporttsv.columns=HBASE_ROW_KEY,info:name,info:age person:user /user/leo_jie/user_test.csv

繼續運行如下命令,以 bulkload 的方式把數據導入 person:user 表裏:

hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles -Dcreate.table=no /user/leo_jie/user_test_hfile person:user

以上命令成功運行結束後我們來檢查下 HBase 和 Phoenix 兩邊的數據:HBase:

Phoenix:

上述的測試是爲了說明,數據不同的寫入方式對 HBase 和 Phoenix 兩邊的數據綁定是否有不同的影響,而目前看來,似乎沒有什麼特殊的地方。後續還會測試數據不同的寫入方式,對 Phoenix 索引數據的影響,在那種場景下,還會是一樣的結果嗎?讓我們拭目以待!

8. 創建多個列簇 Phoenix 映射表

這一小節補充下多列簇 Phoenix 映射表的創建,直接上建表語句:

create table "person"."user2"(
    "id" VARCHAR PRIMARY KEY,
    "info"."name" VARCHAR,
    "info"."age"  VARCHAR,
    "info2"."name" VARCHAR,
    "info2"."age" VARCHAR,
    "info2"."address" VARCHAR)
 column_encoded_bytes=0;

以 put 的方式插入一些數據,然後在 Phoenix 中做以下查詢:

對於所屬不同列簇但名稱相同的字段的查詢,一定要顯示指定字段所屬的列簇名,不然查詢時會報異常。除此之外,跟單列簇創建方式相比,倒也沒啥特別之處了。

9. Phoenix 視圖

類似 MySql 等關係型數據庫中的視圖,Phoenix 也有自己的視圖概念,其視圖的創建方式與表的創建語句類似。如果單純只是爲了查詢數據,強烈建議爲 HBase 表創建 Phenix 的視圖映射,此時視圖的刪除對 HBase 的源表不會造成任何影響。

之前在倒騰 Phoenix 視圖的時候,頻繁遇到各種各樣的報錯,直至現在,對於其中的有些異常,依舊不明就裏。下面先直接演示能正確創建視圖的語句吧!

首先創建一張 HBase 表,create 'USER','INFO'不要爲這張表創建其 Phoenix 映射表,然後運行如下創建視圖的語句。

create view USER(
ID VARCHAR PRIMARY KEY,
INFO.NAME VARCHAR,
INFO.AGE VARCHAR);
view

在 HBase 中插入一些數據,觀察視圖中的數據是否會實時更新。答案是,在 HBase 中插入數據,Phoenix 視圖會實時更新數據。

那麼以 bulkload 的方式更新數據呢?根據上文中提到的測試方法,我們再來實施一下。答案是,以 bulkload 的方式把數據導入 HBase 中,Phoenix 依舊會實時更新數據。

反過來,我們是否可以在 Phoenix 中 upset 數據呢?

 upsert into USER (ID,NAME,AGE) values('111''leo111','17');
upsert

視圖是隻讀的,不能做修改之類的操作。此時刪掉視圖,對 HBase 源表是沒有影響的。

drop view USER;

scan 'USER'

再來說明一下當初折騰視圖時遇到的各種各樣的異常。

「HBase 源表不存在而直接創建視圖」

error1

看來 Phoenix 視圖創建跟 Phoenix 表創建有很大不同啊。

「HBase 源表存在且 Phoenix 映射表也存在」

刪除掉原來的測試表,然後直接用創建 Phoenix 表的方式,同時創建出 HBase 的源表。

create table USER(
ID VARCHAR PRIMARY KEY,
INFO.NAME VARCHAR,
INFO.AGE VARCHAR)
column_encoded_bytes=0;

此時我們再來運行視圖創建語句。

create view USER(
ID VARCHAR PRIMARY KEY,
INFO.NAME VARCHAR,
INFO.AGE VARCHAR);

如果直接運行這個語句,不出意外一定會報錯,報錯信息如下:Error: ERROR 1036 (42J04): Cannot modify the primary key of a VIEW if last PK column of parent is variable length. columnName=USER.ID (state=42J04,code=1036)

table 的名字是 USER,view 的名字也是 USER,但這真的是報錯的根本原因嗎?那讓我們換一個名字來試試看。

create view USER_VIEW(
ID VARCHAR PRIMARY KEY,
INFO.NAME VARCHAR,
INFO.AGE VARCHAR);

繼續報錯:

Error: ERROR 505 (42000): Table is read only. (state=42000,code=505),又回到了我們剛開始的異常,HBase 源表不存在而直接創建視圖。這時候如果你一頓谷歌加百度,十有八九會遇到類似這樣的視圖創建語句。

create view USER_VIEW(
ID VARCHAR PRIMARY KEY,
INFO.NAME VARCHAR,
INFO.AGE VARCHARas select * from USER;

指定查詢數據集,也就是 select 的語句,再映射到視圖 USER_VIEW 上,依舊報錯:

Error: ERROR 1036 (42J04): Cannot modify the primary key of a VIEW if last PK column of parent is variable length. columnName=USER_VIEW.ID (state=42J04,code=1036)

聰明的你一定會這樣修改上述的語句。

 create view USER_VIEW as select * from USER;

這樣的創建語句其實很類似 hive 還有其他的一些關係型數據庫。如果,你還想創建指定列的視圖呢?

 create view USER_VIEW_NAME as select NAME from USER;

你以爲這樣寫就可以了嗎?臉黑的我又遇見了這樣的報錯。

Error: ERROR 604 (42P00): Syntax error. Mismatched input. Expecting "ASTERISK", got "NAME" at line 1, column 38. (state=42P00,code=604)

百度了一波,從大佬的博客裏瞭解到,目前在 Phoenix 創建視圖時,子句中不能選擇字段,只能用*號,否則就會報如上錯誤。

是否可以創建語法更復雜的視圖呢?例如,我已經有了一張 USER 表,再創建一張 PAY 表,記錄每個用戶的花銷。然後以 JOIN 的方式創建視圖 USER_PAY_VIEW。

left join

當前版本的視圖支持的語法比較有限,SELECT 中不能有複雜的語法,否則解析時會無法判斷結束標記。

視圖使用時,一定要注意,在沒有創建索引的情況下,不要瞎寫 SQL 語句,什麼 group by,order by。否則一旦表裏的數據量過大,你十有八九能遇到全表掃描,嚴重情況下影響線上業務。這樣的規則,對錶的查詢同樣適用。

10. 最後

最後來總結一下 Phoenix 的常規使用吧。Phoenix 表創建時要注意大小寫,如果需要使用小寫,那麼請給表名,列簇名,字段名加上英文類型的雙引號;如果你的 HBase 表數據量過大,創建表時,請儘可能提前把對應的 Phoenix 映射表也創建出來吧,最後再導入數據;針對建表時的預分區操作,文中也提到了兩種方式。如果對錶僅有查詢需要,那麼請考慮創建視圖,但視圖的使用又有很大的限制,使用不當就會導致全表掃描,不僅影響查詢效率,還會影響線上的業務。

11. 參考鏈接

  • https://blog.csdn.net/github_39577257/article/details/95968727

據說,關注我的人都找到了對象👇

本文分享自微信公衆號 - HBase工作筆記(HBase-Notes)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章