- 調(diào)試指南
- 主鍵優(yōu)化
- 常規(guī)優(yōu)化
- 數(shù)據(jù)庫設(shè)計優(yōu)化
- 索引優(yōu)化
- 查詢優(yōu)化
- 寫入優(yōu)化
- 更多優(yōu)化
1. 調(diào)試指南
調(diào)整phoenix比較復(fù)雜,但是需要知道一些與它相關(guān)的工作原理,你可以有效的優(yōu)化你的讀寫性能。最重要的性能影響因素是你的數(shù)據(jù)庫設(shè)計,特別是它影響到hbase底層的row keys時??梢圆榭础俺R?guī)優(yōu)化”下面針對不同的預(yù)期數(shù)據(jù)訪問樣式做相對應(yīng)的優(yōu)化建議。隨后的章節(jié)描述二級索引、碰撞和explain plan執(zhí)行計劃。
注意:Phoenix和Hbase可以很好的工作,當(dāng)你的應(yīng)用執(zhí)行點查詢或者小范圍掃描時。這可以通過良好的主鍵設(shè)計實現(xiàn)(查看下文)。如果你的應(yīng)用有一些必須要做全表掃描,Phoenix和Hbase不適合做這些工作。相反地,可以使用其他工具直接寫入HDFS,使用柱狀的代表工具例如Parquet。
2. 主鍵優(yōu)化
底層的row key設(shè)計是最簡單最重要的因素影響Phoenix的性能,在設(shè)計時,正確的使用它非常重要,如果你要更改它,你就必須重新寫入數(shù)據(jù)和重建索引。
Phoenix的主鍵被連接起來,在Hbase的底層進(jìn)行創(chuàng)建row key。主鍵約束的列應(yīng)當(dāng)和普通查詢的樣式一致的選擇和排序,即選擇最經(jīng)常訪問的列作為主鍵。把該點放在你的最核心的位置是最有效的優(yōu)化方式。例如,如果你以包含人名ID的為開始主鍵,那么它會很容易的查詢所有行關(guān)于特殊的人員信息。你可以在Hbase的row timestamp添加到主鍵中,這樣可以改善掃描效率,通過跳過不在范圍內(nèi)的時間的掃描。
每個主鍵都需要成本,因為整個row key都會加入到內(nèi)存和磁盤的每一塊數(shù)據(jù)塊中。row key越大,存儲開銷越大。
例如,設(shè)法將信息緊湊地存儲在計劃用于主鍵存儲增量而不是完整時間戳的列中。
總結(jié),最好的實踐是設(shè)計好主鍵,將它加入到row key設(shè)計中,讓你掃描更少的數(shù)據(jù)。
提示:當(dāng)選擇主鍵,在最需要優(yōu)化的查詢中過濾最頻繁的列開始,如果你選擇ORDER BY在你的查詢中,請確認(rèn)你的主鍵列是否匹配到你的ORDER BY的表達(dá)式。
主鍵單調(diào)遞增
如果你的主鍵是單調(diào)遞增,使用加鹽幫助通過集群分布式寫入,和改善并行能力,例如:
CREATE TABLE … ( … ) SALT_BUCKETS = N
最理想的性能是加鹽桶的數(shù)據(jù)量約等于region server的數(shù)量。不要自動加鹽。只有遇到熱點數(shù)據(jù)時才自動加鹽。加鹽的缺點是它會增加讀取成本,因為在查詢數(shù)據(jù)時必須運行多個查詢才能進(jìn)行范圍掃描。
3. 常規(guī)優(yōu)化
按照該章節(jié)提供的一些常規(guī)優(yōu)化在不同的場景下使用。
當(dāng)數(shù)據(jù)進(jìn)行隨機(jī)訪問
與任何隨機(jī)讀取工作負(fù)載一樣,SSD可以提高性能,因為它們具有更快的隨機(jī)尋道時間
當(dāng)數(shù)據(jù)讀負(fù)擔(dān)大或者寫負(fù)擔(dān)大時?
讀負(fù)擔(dān)大時:
- 創(chuàng)建全局索引。它將會影響寫入的性能,取決于一個索引有多少個列,因為索引得分別寫入他們自己獨立的表中。
- 使用多個索引提供快速的訪問普通查詢。
- 在為HBase指定機(jī)器時,不要在cpu顆數(shù)上節(jié)約;HBase需要它們。
寫負(fù)擔(dān)大時: - 預(yù)分裂表。它可以幫助切割表在預(yù)先定義的region或者鍵在單調(diào)遞增時使用加鹽去避免創(chuàng)建寫入熱點數(shù)據(jù)在小數(shù)量的節(jié)點。使用真實的數(shù)據(jù)類型而非原始的字節(jié)數(shù)據(jù)。
-創(chuàng)建本地索引。從本地索引讀取數(shù)據(jù)會有性能損耗,所以提前性能測試是非常重要的??梢圆榭?a target="_blank">Pherf工具。
當(dāng)列經(jīng)常被訪問時?
選擇常用查詢列作為主鍵。更多的信息,可以查看“Primary keys”在下面:
-創(chuàng)建附加的索引去支持常見的查詢模式,包括大量訪問的非主鍵字段。
數(shù)據(jù)只能追加(數(shù)據(jù)不可變)
-如果數(shù)據(jù)不可變或者只能追加,在創(chuàng)建表和索引的同時,使用IMMUTABLE_ROWS選項進(jìn)行聲明表和索引處于不可變,這樣可以減少數(shù)據(jù)寫入的時間成本。如果你需要對一個已經(jīng)存在的表做不可變,你需要使用到ALTER TABLE trans.event SET IMMUTABLE_ROWS=true在創(chuàng)建之后。如果速度比數(shù)據(jù)完整性更重要,你可以使用DISABLE_WAL選項。注意:它可能會丟失數(shù)據(jù)在DISABLE_WAL,如果一個region服務(wù)器宕機(jī)時。
-設(shè)置UPDATE_CACHE_FREQUENCY選項如果你的元數(shù)據(jù)不經(jīng)常發(fā)生改變,則設(shè)置成15分鐘。這個屬性決定多頻繁的RPC去確認(rèn)你看到的模式是最新的。
-如果數(shù)據(jù)不是松散的(超過50%以上的單元格是有值的),使用SINGLE_CELL_ARRAY_WITH_OFFSETS在Phoenix4.10中引入了數(shù)據(jù)編碼方案,通過減少數(shù)據(jù)的大小,來獲得更快速的性能。關(guān)于更多的資料,可以參考Phoenix的博客《列映射和不可變數(shù)據(jù)的編碼》。
如果表很大?
-使用ASYNC關(guān)鍵字在你創(chuàng)建異步索引時候調(diào)用,通過MapReduce作業(yè),你需要啟動該任務(wù);可以查看詳細(xì)內(nèi)容在此。
-如果數(shù)據(jù)太長以至于無法完全掃描表,使用主鍵創(chuàng)建一個復(fù)合行鍵使它更容易去返回數(shù)據(jù)的子集,或者促進(jìn)skip scanning--Phoenix當(dāng)查詢在謂詞中包含鍵集時,直接跳轉(zhuǎn)到匹配的鍵。
事務(wù)是否必須?
事務(wù)是一種原子性的數(shù)據(jù)操作,即保證完全成功或完全失敗。舉個例子,如果你需要對表的數(shù)據(jù)進(jìn)行交叉更新的話,你需要考慮你的數(shù)據(jù)事務(wù)。
-如果你需要使用,使用TRANSACTIONALoption。(同樣也可查看http://phoenix.apache.org/transactions.html)。
塊編碼
必須使用壓縮或編碼。SNAPPY和FAST_DIFF都是不錯的選擇。
FAST_DIFF編碼默認(rèn)情況下,在所有Phoenix表上都是自動啟用的,并且?guī)缀蹩偸峭ㄟ^允許更多的數(shù)據(jù)放入塊緩存來提高整體的讀取延遲和吞吐量。注意:FAST_DIFF編碼會增加請求處理過程中產(chǎn)生的垃圾。
設(shè)置編碼在表創(chuàng)建時。例子:
CREATE TABLE ...(...) DATA_BLOCK_ENCODING='FAST_DIFF'
4. 數(shù)據(jù)庫設(shè)計優(yōu)化
因為設(shè)計影響數(shù)據(jù)的寫入在Hbase底層,Phoenix的性能依靠你的表、索引和主鍵的設(shè)計。
Phoenix和Hbase數(shù)據(jù)模型關(guān)系
Hbase存儲數(shù)據(jù)在表中,而表又包含列簇和列成員。Hbase表中行數(shù)據(jù)由版本控制單元組成關(guān)聯(lián)著對應(yīng)一個或者多個列。Hbase的行數(shù)據(jù)包含著多個鍵值對數(shù)據(jù),其中它們rowkey的屬性是相同的。數(shù)據(jù)在Hbase表中是排序的通過rowkey進(jìn)行。所有的訪問都通過rowkey完成。Phoenix在Hbase之上創(chuàng)建了一個關(guān)系數(shù)據(jù)模型,強(qiáng)制主鍵約束,主鍵約束的列被組合起來成為底層Hbase表的row key。為此,了解PK的大小和數(shù)量的約束是非常重要的,因為row key副本中包含每一個單元格在底層的Hbase的表中。
列簇
如果有一些列訪問的比其他更頻繁,創(chuàng)建多個列簇去分離經(jīng)常被訪問的列。這將會改善性能因為Hbase的讀僅是在一個指定的列簇中完成。
列
這里有一些常用的技巧應(yīng)用在列上,無論它們是否是索引:
- 由于IO的成本,將varchar列壓縮在1MB以下。在處理查詢時,Hbase物化所有的單元格,在發(fā)送它們給客戶端之前,客戶端在將它們交給應(yīng)用程序代碼之前將它們進(jìn)行全部接收。
- 在結(jié)構(gòu)對象中,不要使用JSON,因為不是很緊湊??梢允褂胮rotobuf、avro、msgpack或者BSON。
- 考慮在存儲之前使用快速LZ變體壓縮數(shù)據(jù),以減少延遲和I/O成本。
- 使用列映射特性(Phoenix 4.10)在使用非PK的Hbase數(shù)值列時,去替換直接使用列名稱。將會改善Hbase在已排序的單元數(shù)據(jù)中進(jìn)行查找的性能,添加特性通過減少表在磁盤中的使用大小全面提升性能和加速DDL操作,比如列重命名、元數(shù)據(jù)級別刪除。可以查看更多的信息在列映射和不可變數(shù)據(jù)編碼在Apache Phoenix博客中。
5. 索引優(yōu)化
Phoenix索引是一個物理表,它存儲主表中部分或全部數(shù)據(jù)的一個旋轉(zhuǎn)副本,去提供指定的查詢。當(dāng)你發(fā)布一個查詢,Phoenix會自動選擇最佳的索引進(jìn)行查詢。主鍵索引是自動創(chuàng)建基于你選擇的主鍵。你可以創(chuàng)建二級索引,根據(jù)索引將支持的預(yù)期查詢指定包含哪些列。
也可以查看二級索引
二級索引
二級索引可以改善讀性能,通過將普通的全表掃描,轉(zhuǎn)換成范圍掃描(以犧牲存儲空間和寫入速度作為代價)。二級索引可以追加或者刪除在表被創(chuàng)建之后,不需要對現(xiàn)在的查詢進(jìn)行更改-達(dá)到查詢只會運行的更快。少量的二級索引通常是足夠的。根據(jù)你的需求,可以考慮創(chuàng)建覆蓋索引或者函數(shù)索引,或者兩者都創(chuàng)建。
如果你的表是非常大的,使用ASYNC關(guān)鍵字和**CREATE INDEX去異步創(chuàng)建索引(同步創(chuàng)建索引,可能導(dǎo)致整張表被鎖住,無法對外提供服務(wù))。在這種情況下,這個索引的創(chuàng)建需要通過MapReduce,這意味著客戶機(jī)的在線或者離線都不會影響索引的創(chuàng)建和這個任務(wù)在必要時會自動完成重試工作。如果你需要手動的啟動任務(wù),你可以監(jiān)控該任務(wù)就像你監(jiān)控其他MapReduce任務(wù)一樣。
例子:
create index if not exists event_object_id_idx_b on trans.event (object_id) ASYNC UPDATE_CACHE_FREQUENCY=60000;
可以查看Index Population更詳細(xì)。
如果你由于某些原因不能創(chuàng)建異步索引,那么增加查詢超時時間(phoenix.query.timeoutMs)在對于大表進(jìn)行構(gòu)建索引時。如果CREATE INDEX執(zhí)行超時或者客戶端掉線在執(zhí)行未完成時,這個索引的創(chuàng)建將會被停止,必須重新執(zhí)行它一遍。你可以監(jiān)控索引表的創(chuàng)建--你可以看到分割時創(chuàng)建新的分區(qū)產(chǎn)生。你可以查詢SYSTEM.STATS表,它將會隨著拆分和壓縮的發(fā)生而填充。你可以直接運行count(*)對索引表進(jìn)行統(tǒng)計,盡管著會增加負(fù)載在你的系統(tǒng)上,因為必須進(jìn)行全表掃描。
技巧:
- 創(chuàng)建本地索引在寫負(fù)擔(dān)重的用例上。
- 創(chuàng)建全局索引在讀負(fù)擔(dān)重的機(jī)器上。為了節(jié)約讀時開銷,可以考慮覆蓋索引。
- 如果主鍵是單調(diào)遞增,創(chuàng)建加鹽分桶。這個加鹽分桶不會被改變。所以設(shè)計它時必須控制未來的增長。加鹽分桶可以幫助避免寫熱點數(shù)據(jù)問題,但是,由于讀取時需要額外的掃描,因此可能會降低總體吞吐量。
- 設(shè)置定時任務(wù)去創(chuàng)建索引,使用ASYNC和CREATE INDEX去避免堵塞。
- 只創(chuàng)建你需要的索引。
- 限制索引數(shù)據(jù),在經(jīng)常更新的表上。
- 使用覆蓋索引去轉(zhuǎn)變表掃描成有效的點查詢或者范圍查詢在索引表中替代主鍵表:CREAET INDEX index ON table(...) INCLUDE(...)。
6. 查詢優(yōu)化
非常重要的是了解哪些查詢執(zhí)行是在服務(wù)端還是客戶端,因為這可以影響性能在網(wǎng)絡(luò)IO上或者其他瓶頸上。如果你的查詢在一個十億行的表,則需要在服務(wù)器端進(jìn)行盡可能多的計算,而不是在將十億數(shù)據(jù)傳輸至客戶端上處理。另一方面,一些查詢必須在客戶機(jī)上執(zhí)行。例如,對多個區(qū)域服務(wù)器上數(shù)據(jù)進(jìn)行排序,需要在客戶機(jī)上聚合和重新排序。
讀
- 避免joins查詢,除非另外一端比較小,尤其是頻繁查詢。對于較大的連接join ,查看“提示”章節(jié)。
- 在WHERE子句中,篩選主鍵約束中的主導(dǎo)列。
- 在WHERE子句中使用IN或IN或IN過濾第一個前導(dǎo)列,可以實現(xiàn)跳過掃描優(yōu)化(skip scan optimizations)。
- 在WHERE子句中的相等或比較(<或>)可以實現(xiàn)范圍掃描優(yōu)化( range scan optimizations)。
- 讓Phoenix使用統(tǒng)計優(yōu)化查詢并行性。如果在生產(chǎn)中使用Phoenix 4.2或更高版本,這將自動帶來好處。
也可以查看 https://phoenix.apache.org/joins.html
范圍查詢
如果您經(jīng)常從旋轉(zhuǎn)磁盤掃描大型數(shù)據(jù)集,那么最好使用GZIP(但是要注意寫速度)。使用大量內(nèi)核進(jìn)行掃描,以利用可用的內(nèi)存帶寬。Apache Phoenix使得利用多個內(nèi)核來提高掃描性能變得很容易。
對于范圍查詢,HBase塊緩存沒有提供太多優(yōu)勢。
大范圍查詢
針對大范圍查詢,考慮設(shè)置Scan.setCacheBlocks(flase)即使整個掃描可以放入塊緩存。
如果你主要執(zhí)行大范圍的查詢,你甚至可以考慮運行Hbase在更小的堆和塊緩存上,從而只應(yīng)用OS的緩存,這將緩解一些垃圾回收的問題。
點查詢
對于點查找,緩存數(shù)據(jù)集是非常重要的,您應(yīng)該使用HBase塊緩存。
提示
提示允許你運行查詢處理行為,并執(zhí)行哪個索引,執(zhí)行哪種類型的掃描,以及使用哪種類型的連接因素等。
- 當(dāng)進(jìn)行查詢,如果您希望在查詢包含不在索引中的列時強(qiáng)制執(zhí)行全局索引,請?zhí)崾尽?/li>
- 如果有必要,你可以執(zhí)行大量數(shù)據(jù)的Joins操作和USE_SORT_MERGE_JOIN執(zhí)行,但是大的join將會是昂貴的操作在大數(shù)據(jù)量上。
- 如果右表的執(zhí)行內(nèi)存超過了限制,可以使用NO_STAR_JOIN提示。
詳細(xì)的查看:提示。
執(zhí)行計劃
一個執(zhí)行計劃可以告訴你大量的關(guān)于一個查詢是如何運行的。去生成一個執(zhí)行計劃運行查詢和解釋這個執(zhí)行計劃,請查看這里參考。
并行計算
你可以改善并行計算在執(zhí)行UPDATE STATISTICS命令。該命令通過確定稱為導(dǎo)柱的鍵來細(xì)分每個區(qū)域,這些鍵彼此之間距離相等,然后使用這些導(dǎo)柱將查詢分解為多個并行掃描。默認(rèn)打開統(tǒng)計信息。使用Phoenix 4.9,用戶可以為每個表設(shè)置導(dǎo)柱寬度。最佳導(dǎo)柱寬度取決于許多因素,如集群大小、集群使用、每個節(jié)點的內(nèi)核數(shù)量、表大小和磁盤I/O。
在Phoenix 4.12中,我們添加了一個新的配置Phoenix .use.stats.parallelization ,控制是否應(yīng)該使用統(tǒng)計數(shù)據(jù)來驅(qū)動并行化。注意,仍然可以運行stats收集。收集的信息用于估計在生成解釋時查詢將掃描的字節(jié)數(shù)和行數(shù)。
7. 寫入優(yōu)化
更新數(shù)據(jù)使用upsert
當(dāng)使用UPSERT VALUES去寫入大數(shù)據(jù)量記錄,請以較小的批量關(guān)閉自動提交和批處理記錄(嘗試100行開始進(jìn)行調(diào)優(yōu)性能)。
注意:使用默認(rèn)的fat驅(qū)動,使用executeBatch()不會提供任何收益。相反的,通過多次執(zhí)行upsert values多次使用commit()進(jìn)行提交給集群。通過thin驅(qū)動,使用executeBatch()非常重要,因為將最小化客戶機(jī)和查詢服務(wù)器的rpc數(shù)量。
try (Connection conn = DriverManager.getConnection(url)) {
conn.setAutoCommit(false);
int batchSize = 0;
int commitSize = 1000; // number of rows you want to commit per batch.
try (Statement stmt = conn.prepareStatement(upsert)) {
stmt.set ... while (there are records to upsert) {
stmt.executeUpdate();
batchSize++;
if (batchSize % commitSize == 0) {
conn.commit();
}
}
conn.commit(); // commit the last batch of records
}
注意:因為Phoenix客戶機(jī)需要保存未提交的行在內(nèi)存中,當(dāng)心commitSize不要設(shè)置過高。
更新數(shù)據(jù)使用upsert select
當(dāng)使用UPSERT SEELCT 去寫入一些數(shù)據(jù)在在處于簡單的狀態(tài),開啟自動提交和行數(shù)據(jù)會自動按批次進(jìn)行根據(jù)phoenix.mutate.batchSize。這將會最小化的返回客戶機(jī)的數(shù)據(jù)量,并且是更新多行數(shù)據(jù)最有效的方法。
刪除數(shù)據(jù)
當(dāng)刪除大量數(shù)據(jù)集時,開啟autoCommit在發(fā)布刪除查詢時,因為這樣客戶機(jī)不需要在刪除所有鍵時去記住它們的row key。這樣可以防止客戶端緩沖受刪除影響的行,這樣Phoenix舊可以直接在區(qū)域服務(wù)器上刪除它們,不必要將它們返回給客戶端。
減少RPC交互
減少RPC交互,設(shè)置UPDATE_CACHE_FREQUENCY (4.7或者以上的版本)在你的表和索引,當(dāng)創(chuàng)建它們(或者時發(fā)布ALTER TABLE/INDEX調(diào)用時,可以查看https://phoenix.apache.org/#Altering
)。
使用本地索引
如果使用Phoenix 4.8,可以考慮使用本地索引來最小化寫的耗時。在本例中,二級索引的寫操作將與基本表位于相同的區(qū)域服務(wù)器上。不過,這種方法確實涉及到讀取方面的性能問題,所以一定要量化寫入速度的提高和讀取速度的降低。
8. 更多優(yōu)化
有一些建議在底層的Hbase和JVM層,可以查看操作和性能配置選項在Apahce Hbase的參考指南中。
特殊案例
下面的小節(jié)是前面提到的Apache Hbase參考指南中調(diào)優(yōu)建議的補(bǔ)充。
對于應(yīng)用而言,快速失敗比等待更好
除了上面提及到了Hbase調(diào)優(yōu)之外,設(shè)置 phoenix.query.timeoutMs在hbase-site.xml中作用于客戶端以毫秒為最大容忍度度等待時間。
對于應(yīng)用可以容忍稍微過時的數(shù)據(jù)
除了上述提及的Hbase調(diào)優(yōu)之外,設(shè)置 phoenix.connection.consistency = timeline 在 hbase-site.xml 設(shè)置所有客戶端的連接。