當您Hologres的表數據寫入或更新的性能無法達到業務預期時,可根據本文提供的寫入瓶頸判斷方法分析其具體原因(上游數據讀取較慢,或達到了Hologres的資源瓶頸等),從而選擇合適的調優手段,幫助業務實現更高的數據寫入和更新性能。
背景信息
Hologres是一站式實時數據倉庫引擎,支持海量數據高性能實時寫入與實時更新,滿足大數據場景上對數據高性能低延遲的需求,Hologres的寫入技術原理詳情請參見寫入技術揭秘。在寫入或更新場景上,Hologres提供多種數據寫入和更新的方式,詳情請參見數據同步概述。
基本原理
在了解寫入或更新的調優手段前,先了解基本的原理,以幫助業務在使用過程中,對不同寫入模式的寫入性能有著更加合理的預估。
不同表存儲格式的寫入或更新性能。
全列寫入或更新時,性能排序如下。
行存 > 列存 > 行列共存
。部分列寫入或更新時,性能排序如下。
行存 > 行列共存 > 列存
。
不同寫入模式的性能。
寫入模式類型如下。
寫入模式
說明
Insert
以追加(Append-Only)的方式寫入,結果表無主鍵(PK)。
InsertOrIgnore
寫入時忽略更新,結果表有主鍵,實時寫入時如果主鍵重復,丟棄后到的數據。
InsertOrReplace
寫入覆蓋,結果表有主鍵,實時寫入時如果主鍵重復,按照主鍵更新。如果寫入的一行數據不包含所有列,缺失的列的數據補Null。
InsertOrUpdate
寫入更新,結果表有主鍵,實時寫入時如果主鍵重復,按照主鍵更新。分為整行更新和部分列更新,部分列更新指如果寫入的一行數據不包含所有列,缺失的列不更新。
列存表不同寫入模式的性能排序如下。
結果表無主鍵性能最高。
結果表有主鍵時:
InsertOrIgnore > InsertOrReplace >= InsertOrUpdate(整行)> InsertOrUpdate(部分列)
。
行存表不同寫入模式的性能排序如下。
InsertOrReplace = InsertOrUpdate(整行)>= InsertOrUpdate(部分列) >= InsertOrIgnore
。
開啟Binlog的表寫入或更新性能排序如下。
行存 > 行列共存 > 列存
。
寫入瓶頸判斷
在表數據寫入或更新時,如果寫入性能慢,可通過查看管理控制臺的CPU使用率監控指標,初步判斷性能瓶頸:
CPU使用率很低。
說明Hologres資源沒有完全用上,性能瓶頸不在Hologres側,可自行排查是否存在上游數據讀取較慢等問題。
CPU使用率較高(長期100%)。
說明已經達到了Hologres的資源瓶頸,可以通過如下方法處理。
通過基本調優手段排查是否因基礎設置不到位導致資源負載較高,影響寫入性能,詳情請參見基本調優手段。
在基本調優手段已經檢查完畢后,可以通過寫入渠道(常見的如Flink、數據集成)以及Hologres的高級調優手段,更深層次的判斷是否存在寫入瓶頸,并做相應的處理,詳情請參見Flink寫入調優、數據集成調優和高級調優手段。
查詢影響寫入,二者共同執行會導致資源使用率較高,通過慢Query日志排查同一時間查詢的CPU消耗。若是查詢影響寫入,可以考慮為實例配置讀寫分離高可用部署,詳情請參見主從實例讀寫分離部署(共享存儲)。
所有調優手段已操作完畢,但寫入性能仍然不滿足預期,可適當擴容Hologres實例。
基本調優手段
一般情況下Hologres就能達到非常高的寫入性能,如果在數據寫入過程中覺得性能不符合預期,可以通過以下方法進行常規調優。
避免使用公網,減少網絡開銷。
Hologres提供VPC、經典、公網等網絡類型,適用場景請參見網絡配置。推薦在進行數據寫入時,尤其是使用JDBC、PSQL等業務應用連接Hologres時,盡量使用VPC網絡連接而不是公網連接。因為公網有流量限制,相比VPC網絡會更加不穩定。
盡量使用Fixed Plan寫入。
一個SQL在Hologres中的執行流程如下圖所示,詳細原理請參見執行引擎。
SQL為普通OLAP寫入,那么就會走左側的鏈路,會經過優化器(QO)、執行引擎(QE)等一系列組件,數據寫入或更新時會整個表拿鎖即表鎖,如果是并發執行
INSERT
、UPDATE
或DELETE
命令,那么SQL間會相互等鎖,導致延遲較高。SQL為點查點寫,那么就會走右側的執行鏈路,此鏈路統稱為Fixed Plan。Fixed Plan的Query特征足夠簡單,沒有QO等組件的開銷,因此在寫入或更新時是行鎖,這樣就能極大的提高Query的并發能力及性能。
因此在優化寫入或更新性能時,優先考慮讓Query盡量走Fixed Plan。
Query走Fixed Plan
走Fixed Plan的SQL需要符合一定的特征,常見未走Fixed Plan的情形如下。
使用
insert on conflict
語法進行多行插入更新時,示例如下。INSERT INTO test_upsert(pk1, pk2, col1, col2) VALUES (1, 2, 5, 6), (2, 3, 7, 8) ON CONFLICT (pk1, pk2) DO UPDATE SET col1 = excluded.col1, col2 = excluded.col2;
使用
insert on conflict
語法進行局部更新時,結果表的列和插入數據的列沒有一一對應。結果表中有SERIAL類型的列。
結果表設置了
Default
屬性。基于主鍵的
update
或delete
,如:update table set col1 = ?, col2 = ? where pk1 = ? and pk2 = ?;
。使用了Fixed Plan不支持的數據類型。
如果SQL沒有走Fixed Plan,那么在管理控制臺監控指標中
實時導入RPS
指標則會顯示插入類型為INSERT
,示例如下。沒有走Fixed Plan的SQL,其執行引擎類型為HQE或PQE,大多數情況的寫入為HQE。因此當發現寫入或更新較慢時,可以通過如下示例語句查看慢Query日志,排查Query的執行引擎類型(engine_type)。--示例查看過去3小時未走Fixed Plan的insert/update/delete SELECT * FROM hologres.hg_query_log WHERE query_start >= now() - interval '3 h' AND command_tag IN ('INSERT', 'UPDATE', 'DELETE') AND ARRAY['HQE'] && engine_type ORDER BY query_start DESC LIMIT 500;
盡量將執行引擎類型為HQE的Query改寫為符合Fixed Plan特征的SDK SQL,從而提高性能。重點關注如下GUC參數,建議DB級別開啟GUC參數,更多關于Fixed Plan的使用請參見Fixed Plan加速SQL執行。
場景
GUC設置
說明
支持使用
insert on conflict
語法多行記錄的Fixed Plan寫入。alter database <databasename> set hg_experimental_enable_fixed_dispatcher_for_multi_values =on;
建議DB級別開啟。
支持含有SERIAL類型列的Fixed Plan寫入。
alter database <databasename> set hg_experimental_enable_fixed_dispatcher_autofill_series =on;
不建議為表設置SERIAL類型,對寫入性能有一定的犧牲。Hologres從 V1.3.25版本開始此GUC參數默認為
on
。支持Default屬性的列的Fixed Plan寫入。
Hologres從 V1.3版本開始,使用
insert on conflict
語法寫入數據時含有設置了Default屬性的字段,則會默認走Fixed Plan。不建議為表設置Default屬性,對寫入性能有一定的犧牲。Hologres V1.1版本不支持含有設置了Default屬性的字段走Fixed Plan,從V1.3版本開始支持。
基于主鍵的UPDATE。
alter database <databasename> set hg_experimental_enable_fixed_dispatcher_for_update =on;
Hologres從 V1.3.25版本開始此GUC參數值默認為
on
。基于主鍵的DELETE。
alter database <databasename> set hg_experimental_enable_fixed_dispatcher_for_delete =on;
Hologres從 V1.3.25版本開始此GUC參數值默認為
on
。如果SQL走了Fixed Plan,如下圖所示監控指標
實時導入RPS
的類型為SDK
。并且在慢Query日志中SQL的engine_type
也為SDK
。走了Fixed Plan后寫入仍然比較慢。
SQL已經走了Fixed Plan仍然耗時較長,可能原因如下。
通常情況是該表既有Fixed Plan的SDK寫入或更新,又有HQE的寫入或更新,HQE是表鎖,會導致SDK的寫入因為等鎖而耗時較長。可以通過以下SQL查詢當前表是否有HQE的操作,并根據業務情況優化為SDK的SQL。可以通過HoloWeb Query洞察快速識別出某個Fixed Plan的Query是否有HQE的鎖,詳情請參見Query洞察。
--查詢表在過去3小時未走Fixed Plan的insert/update/delete SELECT * FROM hologres.hg_query_log WHERE query_start >= now() - interval '3 h' AND command_tag IN ('INSERT', 'UPDATE', 'DELETE') AND ARRAY['HQE'] && engine_type AND table_write = '<table_name>' ORDER BY query_start DESC LIMIT 500;
如果表都是SDK寫入,但仍然慢,觀察
CPU使用率
監控指標,若是持續較高,可能已經達到實例資源瓶頸,可適當進行擴容。
開啟Binlog會降低寫入吞吐。
Hologres Binlog記錄了數據變更記錄(INSERT、UPDATE、DELETE),會完整的記錄每行數據的變更情況。為某張表打開Binlog,以UPDATE語句為例,示例SQL如下:
update tbl set body =new_body where id='1';
由于Binlog記錄的是整行所有字段的數據,因此在生成Binlog的過程中,需要通過過濾字段(示例中的
id
字段)去點查目標表的整行數據。如果是列存表的話,這種點查SQL相比行存表會消耗更多的資源,因此開啟Binlog的表在寫入性能上行存表 > 列存表
。同一張表避免并發實時和離線寫入。
離線寫入如MaxCompute寫入Hologres時是表鎖,實時寫入大多數是Fixed Plan寫入為行鎖(例如Flink實時寫入或者DataWorks數據集成實時寫入),如果對同一個表并發執行離線寫入和實時寫入,那么離線寫入就會拿表鎖,實時寫入會因為等鎖而導致寫入性能慢。所以建議同一張表避免并發進行實時和離線寫入。
Holo Client或JDBC寫入調優
Holo Client、JDBC等客戶端在寫入數據時,提高寫入性能的調優手段如下。
攢批寫入數據。
在通過客戶端寫入時,攢批寫入相比單條寫入能夠提供更高的吞吐,從而提升寫入性能。
使用Holo Client會自動攢批,建議使用Holo Client默認配置參數,詳情請參見通過Holo Client讀寫數據。
使用JDBC時可以在JDBC連接串配置
WriteBatchedInserts=true
,如下所示則可實現攢批的功能,更多JDBC詳情請參見JDBC。jdbc:postgresql://{ENDPOINT}:{PORT}/{DBNAME}?ApplicationName={APPLICATION_NAME}&reWriteBatchedInserts=true
未攢批的SQL改造成可攢批的SQL示例如下。
--未攢批的兩個sql insert into data_t values (1, 2, 3); insert into data_t values (2, 3, 4); --攢批后的sql insert into data_t values (1, 2, 3), (4, 5, 6); --攢批的另一種寫法 insert into data_t select unnest(ARRAY[1, 4]::int[]), unnest(ARRAY[2, 5]::int[]), unnest(ARRAY[3, 6]::int[]);
使用Prepared Statement模式寫入數據。
Hologres兼容PostgreSQL生態,并基于Postgres的extended協議,支持了Prepared Statement模式,會緩存服務器的SQL編譯結果,從而降低了FE、QO等組件的開銷,提高寫入的性能。相關技術原理請參見如何支持超高QPS在線服務(點查)場景。
JDBC、Holo Client使用Prepared Statement模式寫入數據請參見JDBC。
Flink寫入調優
各類型表的注意事項如下。
Binlog源表
Flink消費Hologres Binlog支持的數據類型有限,對不支持的數據類型(如SMALLINT)即使不消費此字段,仍然可能導致作業無法上線。從Flink引擎VVR-6.0.3-Flink-1.15 版本開始,支持通過JDBC模式消費Hologres Binlog,此模式下支持更多數據類型,詳情請參見Flink全托管。
開啟Binlog的Hologres表建議使用行存表。列存表開啟Binlog會使用較多的資源,影響寫入性能。
維表
維表必須使用行存表或行列共存表,列存表對于點查場景性能開銷較大。
創建行存表時必須設置主鍵,并且將主鍵配置為Clustering Key時性能較好。
維表的主鍵必須是Flink Join ON的字段,Flink Join ON的字段也必須是表的完整主鍵,兩者必須完全匹配。
結果表
寬表Merge或局部更新功能對應的Hologres表必須有主鍵,且每個結果表都必須聲明和寫入主鍵字段,必須使用
InsertOrUpdate
的寫入模式。每個結果表的ignoredelete
屬性都必須設置為true
,防止回撤消息產生Delete請求。列存表的寬表Merge場景在高RPS的情況下,CPU使用率會偏高,建議關閉表中字段的
Dictionary Encoding
。結果表有主鍵場景,建議設置
segment_key
,可以在寫入和更新時快速定位到數據所在的底層文件。推薦使用時間戳、日期等字段作為segment_key
,并且在寫入時使對應字段的數據與寫入時間有強相關性。
Flink參數配置建議。
Hologres Connector各參數的默認值是大多數情況下的最佳配置。如果出現以下情況,可以酌情修改參數。
Binlog消費延遲比較高:
默認讀取Binlog批量大小(
binlogBatchReadSize
)為100,如果單行數據的byte size
并不大,可以增加此參數,可以優化消費延遲。維表點查性能較差:
設置
async
參數為true
開啟異步模式。此模式可以并發地處理多個請求和響應,從而連續的請求之間不需要阻塞等待,提高查詢的吞吐。但在異步模式下,無法保證請求的絕對順序。有關異步請求的原理請參見維表JOIN與異步優化。維表數據量較大且更新不頻繁時,推薦使用維表緩存優化查詢性能。相應參數設置為
cache = 'LRU'
,同時默認的cacheSize
較保守,為10000行,推薦根據實際情況調大一些。
連接數不足:
connector
默認使用JDBC方式實現,如果Flink作業較多,可能會導致Hologres的連接數不足,此時可以使用connectionPoolName
參數實現同一個TaskManager中,連接池相同的表可以共享連接。
作業開發推薦。
Flink SQL相對DataStream來說,可維護性高且可移植性強,因此推薦使用Flink SQL來實現作業。如果業務需要使用DataStream,更推薦使用Hologres DataStream Connector,詳情請參見Hologres DataStream Connector。如果需要開發自定義Datastream作業,則推薦使用Holo Client而不是JDBC,即推薦使用的作業開發方式排序為:
Flink SQL > Flink DataStream(connector) > Flink DataStream(holo-client) > Flink DataStream(JDBC)
。寫入慢的原因排查。
很多情況下,寫入慢也可能是Flink作業中其他步驟的問題。您可以拆分Flink作業的節點,并觀察Flink作業的反壓情況,是否在讀數據源或一些復雜的計算節點已經反壓,數據進入到Hologres結果表的速率已經很慢,此時優先排查Flink側是否有可以優化的地方。
如果Hologres實例的CPU使用率很高(如長時間達到100%),寫入延遲也比較高,則可以考慮是Hologres側的問題。
其他常見異常信息和排查方法請參見Blink和Flink常見問題及診斷。
數據集成調優
并發配置與連接的關系。
數據集成中非腳本模式作業的連接數為每個并發三個連接,腳本模式作業可通過
maxConnectionCount
參數配置任務的總連接數,或者insertThreadCount
參數配置單并發的連接數。一般情況下,無需修改并發和連接數就能達到很好的性能,可根據實際業務情況適當修改。獨享資源組。
數據集成大部分作業都需要使用獨享資源組,因此獨享資源組的規格也決定著任務的性能上限。 為了保證性能,推薦作業一個并發對應獨享資源組1 Core。 如果資源組規格過小,但任務并發高可能會存在JVM內存不足等問題。同樣的如果獨享資源組的帶寬打滿也會影響寫入任務的性能上限,如果發生此現象,建議對任務進行拆解,把大任務拆成小任務并分配到不同的資源組上。關于數據集成獨享資源組的規格指標請參見性能指標。
寫入慢時如何排查是數據集成或上游慢還是Hologres側的問題?
數據集成寫Hologres時,如果數據集成讀端的等待時間比寫端的等待時間大,通常情況是讀端慢導致。
如果Hologres實例的CPU使用率很高(如長時間達到100%),寫入延遲也比較高,則可以考慮是Hologres側的問題。
高級調優手段
基本調優手段已經覆蓋提升寫入性能的基本方法,若是使用正確就能達到很好的寫入性能。但是在實際情況中,還有一些其他因素影響性能,比如索引的設置、數據的分布等,高級調優將會介紹在基本調優手段的基礎上,如何進一步的排查并提升寫入性能,適用于對Hologres原理有進一步了解的業務。
數據傾斜導致寫入慢。
如果數據傾斜或Distribution Key設置的不合理,就會導致Hologres實例的計算資源出現傾斜,導致資源無法高效使用從而影響寫入性能,排查數據傾斜和對應問題解決方法請參見查看Worker傾斜關系。
Segment Key設置不合理導致寫入慢。
寫入列存表時,設置了不合理的Segment Key可能會極大的影響寫入性能,表已有數據量越多性能下降越明顯。這是因為Segment Key用于底層文件分段,在寫入或更新時Hologres會根據主鍵反查對應的舊數據,列存表的反查操作需要通過Segment Key快速定位到數據所在的底層文件。如果這張列存表沒有配置Segment Key或者Segment Key配置了不合理的字段或者Segment Key對應的字段在寫入時沒有與時間有強相關性(比如基本亂序),那反查時需要掃描的文件將會非常之多,不僅會有大量的IO操作,而且也可大量占用CPU,影響寫入性能和整個實例的負載。此時管控臺監控頁面的
IO吞吐
指標往往表現為即使主要是寫入作業,其Read
指標也非常高。因此推薦使用時間戳、日期等字段作為Segment Key,并且在寫入時使對應字段的數據與寫入時間有強相關性。
Clustering Key設置不合理導致寫入慢
有主鍵(PK)的情況下,在寫入或更新時,Hologres會根據主鍵反查對應的舊數據。
對于行存表來說,當Clustering Key與PK不一致時,反查就會需要反查兩次,即分別按照PK索引和Clustering Key索引,這種行為會增加寫入的延時,所以對于行存表推薦Clustering Key和PK保持一致。
對于列存表,Clustering Key的設置主要會影響查詢性能,不會影響寫入性能,可以暫不考慮。