使用phoenix踩的坑與設計思考

阿丸發表於2020-05-21

本文主要介紹在壓測HBase的二級索引phoenix時踩的一個坑,使用時需要特別注意,而且背後的原因也很有意思,可以看出HBase和Phoenix對後設資料設計上的差異。 

1.問題介紹

在做phoenix壓測時發現一個奇怪的現象。

壓測請求分佈非常均勻,但是有一臺機器的流量、負載都明顯高於其他機器。

如下圖所示。

請求均勻

使用phoenix踩的坑與設計思考

 

資源利用率不均勻,單個節點明顯偏高。

使用phoenix踩的坑與設計思考

 

2.排查思路

看到這個問題的第一反應,是去看下錶分佈是否均勻。

  • hbase表分佈是否均勻
  • 索引表分佈是否均勻

令人遺憾的是,確認後hbase表和索引表都是均勻分佈的,各個機器上的region數量、儲存用量都是一致,非常均勻。

然後在網上查相關資料,在官網上看到這樣一段描述。(http://phoenix.apache.org/language/index.html)

UPDATE_CACHE_FREQUENCY option (available as of Phoenix 4.7) determines how often the server will be checked for meta data updates (for example, the addition or removal of a table column or the updates of table statistics). Possible values are ALWAYS (the default), NEVER, and a millisecond numeric value. An ALWAYS value will cause the client to check with the server each time a statement is executed that references a table (or once per commit for an UPSERT VALUES statement). A millisecond value indicates how long the client will hold on to its cached version of the metadata before checking back with the server for updates.

 

大致的意思是,phoenix的表設計時有一個表級別引數UPDATE_CACHE_FREQUENCY,這個引數預設是ALWAYS,表示每次sql查詢都會先去請求meta資料。也可以設定為一定頻率,表示多久去請求一次meta資料。

那我們大概能猜想到了,因為設計表的時候沒有指定這個引數,所以為預設的always,而phoenix的meta資料正好落在了那個機器上。

我們查驗了下系統表的位置,果然如此!

於是立刻做了變更

alter table xxx set UPDATE_CACHE_FREQUENCY = xxxxx。

  效果顯著!

不僅流量、負載均勻了,而且整體負載下降了很多!!!

使用phoenix踩的坑與設計思考

 

那為什麼監控上請求量是均勻的呢?因為phoenix需要訪問catalog表,然後這個表剛才在core-2上,訪問這個表走的coprocessor,所以沒統計出請求數。

3.進一步思考

到上面為止,問題的原因找到了,也解決了。

但是熟悉HBase的同學肯定會馬上有個疑問,跟我一樣。為什麼會有這樣一個引數設定,HBase本身也有meta表,而HBase的meta表在客戶端快取就可以。

下面,就讓我們來思考下。

1)phoenix為什麼要這麼設計呢,而不是像hbase的meta表一樣?

這個問題得從phoenix的架構設計說起。

phoenix一直以來,都是重客戶端模式,即使是現在的輕客戶端版本,本質也是如此。只不過把客戶端放在了query server這個角色上,讓query server跑在服務端了。

使用phoenix踩的坑與設計思考

 

但對hbase來說,phoenix的核心邏輯都在client側。後設資料管理也是如此。phoenix和HBase不同的是,phoenix的後設資料更復雜,如果後設資料有變化,沒有拿到最新meta的客戶端不一定會拋錯。

舉個例子,比如一個查詢命中索引後會更高效。但是,某個客戶端它 不知道有這個索引表,於是就去查了主表,這個在phoenix上看也沒有毛病。

所以,關鍵原因是phoenix無法感知後設資料是否發生了變化!而HBase可以。

因此,最初為了解決後設資料同步的問題,就採用了比較激進的每次重新整理的方式。後來發現會影響效能,就加了一個週期性重新整理的功能,來避免per request去刷meta資料。

2)那可以alter table時觸發meta更新嗎?

這個是不可以的。因為客戶端可能比較多。而且,也不會有一個地方去記錄全域性有哪些client。即使有這樣一個地方,那某個client掛了怎麼辦?沒有通知成功怎麼辦?此時,alter操作是成功還是不成功呢?

3)結論

所以這個引數是phoenix的設計,而不是缺陷。phoenix預設情況下,每個請求都會去校驗一次表的後設資料資訊,以避免因meta未重新整理導致失敗。為此,phoenix提供了一個表引數,來控制meta的重新整理頻率,比如1分鐘刷一次,類似這種。

可以在建表的時候設定:

craete table if not exists ns.table_demo (
id varchar not null primary key,
f1.a varchar,
f1.b varchar,
f1.c varchar
)
TTL=86400,
UPDATE_CACHE_FREQUENCY=900000;

也可以變更表結構設定:

alter table xxx set UPDATE_CACHE_FREQUENCY = xxxxx; 

設一個你期望的重新整理時間,就可以解決問題了。

當然,這樣帶來的副作用就是,如果未來你修改了這個表,比如add了一個新的列,或者新加了一個索引,最少要等待一個重新整理週期才能生效。但是無關大局,一般表結構變更就屬於低頻操作,而且能夠接受一定延遲。

 

看到這裡了,原創不易,點個關注、點個贊吧,你最好看了~

知識碎片重新梳理,構建Java知識圖譜:https://github.com/saigu/JavaKnowledgeGraph(歷史文章查閱非常方便)

相關文章