本文為您介紹如何通過阿里云性能測試PTS對Hologres的性能進行壓測,幫助您快速驗證Hologres的性能。
Hologres簡介
Hologres是兼容PostgreSQL協議的實時交互式分析引擎。支持海量數據實時寫入、實時更新、實時分析,既支持PB級數據多維分析(OLAP)與即席分析(Ad Hoc),又支持高并發低延遲的在線數據服務(Serving)。
Hologres支持連接多種開發工具,您可以使用HoloWeb登錄Hologres實例,創建數據庫并在SQL編輯器中執行如下語句,完成內部表、外部表的創建和寫入測試數據。關于HoloWeb的簡介與使用方法,請參見HoloWeb簡介。
Hologres支持多種數據源的數據同步,包括MaxCompute數據同步、OSS數據湖同步、通過Copy命令導入本地文件、通過DataWorks同步MySQL等數據庫數據,詳情參見數據同步概述。
創建Hologres內部表的同時,需要為其設置合適的索引,以獲得最優的查詢性能。關于索引的創建,詳情請參見CREATE TABLE。
前提條件
已購買并開通Hologres實例,開通方法請參見購買Hologres。
已開通PTS服務。
本示例使用RAM用戶
pts-test
來做測試,請確保您已創建RAM用戶并授予AliyunHologresReadOnlyAccess
權限。且已在HoloWeb授權該用戶訪問目標DB。
方案概覽
本實踐通過性能測試PTS使用TPC-H(商業智能計算測試)對Hologres在OLAP查詢場景、Key/Value點查場景和數據更新場景的性能進行測試。使用PTS進行性能壓測,您無需準備測試所需的基礎環境,但您仍需針對不同的測試場景準備相應數據及測試語句。
OLAP查詢場景:需要創建列存表,從數據源中同步數據,設計OLAP查詢測試語句。
Key/Value點查場景:需要創建行存表,從數據源中同步數據,設計Key/Value點查測試語句。
數據更新場景:需要創建表,寫入原始數據,并準備需要進行更新的數據。
本文的TPC-H的實現基于TPC-H的基準測試,并不能與已發布的TPC-H基準測試結果相比較,本文中的測試并不符合TPC-H基準測試的所有要求。
本文以MaxCompute數據源為例,將MaxCompute公共空間
MAXCOMPUTE_PUBLIC_DATA
中的TPC-H 100 GB數據寫入Hologres,作為本文的測試數據。
OLAP查詢場景
主要使用列存表,使用TPC-H測試中的22條查詢語句進行測試。
1. 配置Hologres測試數據
創建外部表
DROP FOREIGN TABLE IF EXISTS odps_customer_100g; DROP FOREIGN TABLE IF EXISTS odps_lineitem_100g; DROP FOREIGN TABLE IF EXISTS odps_nation_100g; DROP FOREIGN TABLE IF EXISTS odps_orders_100g; DROP FOREIGN TABLE IF EXISTS odps_part_100g; DROP FOREIGN TABLE IF EXISTS odps_partsupp_100g; DROP FOREIGN TABLE IF EXISTS odps_region_100g; DROP FOREIGN TABLE IF EXISTS odps_supplier_100g; IMPORT FOREIGN SCHEMA "MAXCOMPUTE_PUBLIC_DATA#default" LIMIT to ( odps_customer_100g, odps_lineitem_100g, odps_nation_100g, odps_orders_100g, odps_part_100g, odps_partsupp_100g, odps_region_100g, odps_supplier_100g ) FROM SERVER odps_server INTO public OPTIONS(if_table_exist 'error',if_unsupported_type 'error');
創建內部表
OLAP場景測試需要使用列存表(默認即為列存),還需要創建合適的索引以達到更優的查詢性能。更多關于表屬性的信息請參見CREATE TABLE。
DROP TABLE IF EXISTS LINEITEM; BEGIN; CREATE TABLE LINEITEM ( L_ORDERKEY BIGINT NOT NULL, L_PARTKEY INT NOT NULL, L_SUPPKEY INT NOT NULL, L_LINENUMBER INT NOT NULL, L_QUANTITY DECIMAL(15,2) NOT NULL, L_EXTENDEDPRICE DECIMAL(15,2) NOT NULL, L_DISCOUNT DECIMAL(15,2) NOT NULL, L_TAX DECIMAL(15,2) NOT NULL, L_RETURNFLAG TEXT NOT NULL, L_LINESTATUS TEXT NOT NULL, L_SHIPDATE TIMESTAMPTZ NOT NULL, L_COMMITDATE TIMESTAMPTZ NOT NULL, L_RECEIPTDATE TIMESTAMPTZ NOT NULL, L_SHIPINSTRUCT TEXT NOT NULL, L_SHIPMODE TEXT NOT NULL, L_COMMENT TEXT NOT NULL, PRIMARY KEY (L_ORDERKEY,L_LINENUMBER) ); CALL set_table_property('LINEITEM', 'clustering_key', 'L_SHIPDATE,L_ORDERKEY'); CALL set_table_property('LINEITEM', 'segment_key', 'L_SHIPDATE'); CALL set_table_property('LINEITEM', 'distribution_key', 'L_ORDERKEY'); CALL set_table_property('LINEITEM', 'bitmap_columns', 'L_ORDERKEY,L_PARTKEY,L_SUPPKEY,L_LINENUMBER,L_RETURNFLAG,L_LINESTATUS,L_SHIPINSTRUCT,L_SHIPMODE,L_COMMENT'); CALL set_table_property('LINEITEM', 'dictionary_encoding_columns', 'L_RETURNFLAG,L_LINESTATUS,L_SHIPINSTRUCT,L_SHIPMODE,L_COMMENT'); COMMIT; DROP TABLE IF EXISTS ORDERS; BEGIN; CREATE TABLE ORDERS ( O_ORDERKEY BIGINT NOT NULL PRIMARY KEY, O_CUSTKEY INT NOT NULL, O_ORDERSTATUS TEXT NOT NULL, O_TOTALPRICE DECIMAL(15,2) NOT NULL, O_ORDERDATE timestamptz NOT NULL, O_ORDERPRIORITY TEXT NOT NULL, O_CLERK TEXT NOT NULL, O_SHIPPRIORITY INT NOT NULL, O_COMMENT TEXT NOT NULL ); CALL set_table_property('ORDERS', 'segment_key', 'O_ORDERDATE'); CALL set_table_property('ORDERS', 'distribution_key', 'O_ORDERKEY'); CALL set_table_property('ORDERS', 'bitmap_columns', 'O_ORDERKEY,O_CUSTKEY,O_ORDERSTATUS,O_ORDERPRIORITY,O_CLERK,O_SHIPPRIORITY,O_COMMENT'); CALL set_table_property('ORDERS', 'dictionary_encoding_columns', 'O_ORDERSTATUS,O_ORDERPRIORITY,O_CLERK,O_COMMENT'); COMMIT; DROP TABLE IF EXISTS PARTSUPP; BEGIN; CREATE TABLE PARTSUPP ( PS_PARTKEY INT NOT NULL, PS_SUPPKEY INT NOT NULL, PS_AVAILQTY INT NOT NULL, PS_SUPPLYCOST DECIMAL(15,2) NOT NULL, PS_COMMENT TEXT NOT NULL, PRIMARY KEY(PS_PARTKEY,PS_SUPPKEY) ); CALL set_table_property('PARTSUPP', 'distribution_key', 'PS_PARTKEY'); CALL set_table_property('PARTSUPP', 'bitmap_columns', 'PS_PARTKEY,PS_SUPPKEY,PS_AVAILQTY,PS_COMMENT'); CALL set_table_property('PARTSUPP', 'dictionary_encoding_columns', 'PS_COMMENT'); COMMIT; DROP TABLE IF EXISTS PART; BEGIN; CREATE TABLE PART ( P_PARTKEY INT NOT NULL PRIMARY KEY, P_NAME TEXT NOT NULL, P_MFGR TEXT NOT NULL, P_BRAND TEXT NOT NULL, P_TYPE TEXT NOT NULL, P_SIZE INT NOT NULL, P_CONTAINER TEXT NOT NULL, P_RETAILPRICE DECIMAL(15,2) NOT NULL, P_COMMENT TEXT NOT NULL ); CALL set_table_property('PART', 'distribution_key', 'P_PARTKEY'); CALL set_table_property('PART', 'bitmap_columns', 'P_PARTKEY,P_SIZE,P_NAME,P_MFGR,P_BRAND,P_TYPE,P_CONTAINER,P_COMMENT'); CALL set_table_property('PART', 'dictionary_encoding_columns', 'P_NAME,P_MFGR,P_BRAND,P_TYPE,P_CONTAINER,P_COMMENT'); COMMIT; DROP TABLE IF EXISTS CUSTOMER; BEGIN; CREATE TABLE CUSTOMER ( C_CUSTKEY INT NOT NULL PRIMARY KEY, C_NAME TEXT NOT NULL, C_ADDRESS TEXT NOT NULL, C_NATIONKEY INT NOT NULL, C_PHONE TEXT NOT NULL, C_ACCTBAL DECIMAL(15,2) NOT NULL, C_MKTSEGMENT TEXT NOT NULL, C_COMMENT TEXT NOT NULL ); CALL set_table_property('CUSTOMER', 'distribution_key', 'C_CUSTKEY'); CALL set_table_property('CUSTOMER', 'bitmap_columns', 'C_CUSTKEY,C_NATIONKEY,C_NAME,C_ADDRESS,C_PHONE,C_MKTSEGMENT,C_COMMENT'); CALL set_table_property('CUSTOMER', 'dictionary_encoding_columns', 'C_NAME,C_ADDRESS,C_PHONE,C_MKTSEGMENT,C_COMMENT'); COMMIT; DROP TABLE IF EXISTS SUPPLIER; BEGIN; CREATE TABLE SUPPLIER ( S_SUPPKEY INT NOT NULL PRIMARY KEY, S_NAME TEXT NOT NULL, S_ADDRESS TEXT NOT NULL, S_NATIONKEY INT NOT NULL, S_PHONE TEXT NOT NULL, S_ACCTBAL DECIMAL(15,2) NOT NULL, S_COMMENT TEXT NOT NULL ); CALL set_table_property('SUPPLIER', 'distribution_key', 'S_SUPPKEY'); CALL set_table_property('SUPPLIER', 'bitmap_columns', 'S_SUPPKEY,S_NAME,S_ADDRESS,S_NATIONKEY,S_PHONE,S_COMMENT'); CALL set_table_property('SUPPLIER', 'dictionary_encoding_columns', 'S_NAME,S_ADDRESS,S_PHONE,S_COMMENT'); COMMIT; DROP TABLE IF EXISTS NATION; BEGIN; CREATE TABLE NATION( N_NATIONKEY INT NOT NULL PRIMARY KEY, N_NAME text NOT NULL, N_REGIONKEY INT NOT NULL, N_COMMENT text NOT NULL ); CALL set_table_property('NATION', 'distribution_key', 'N_NATIONKEY'); CALL set_table_property('NATION', 'bitmap_columns', 'N_NATIONKEY,N_NAME,N_REGIONKEY,N_COMMENT'); CALL set_table_property('NATION', 'dictionary_encoding_columns', 'N_NAME,N_COMMENT'); COMMIT; DROP TABLE IF EXISTS REGION; BEGIN; CREATE TABLE REGION ( R_REGIONKEY INT NOT NULL PRIMARY KEY, R_NAME TEXT NOT NULL, R_COMMENT TEXT ); CALL set_table_property('REGION', 'distribution_key', 'R_REGIONKEY'); CALL set_table_property('REGION', 'bitmap_columns', 'R_REGIONKEY,R_NAME,R_COMMENT'); CALL set_table_property('REGION', 'dictionary_encoding_columns', 'R_NAME,R_COMMENT'); COMMIT;
測試數據寫入并收集統計信息
說明Hologres從V2.1.17版本起支持Serverless Computing能力,針對大數據量離線導入、大型ETL作業、外表大數據量查詢等場景,使用Serverless Computing執行該類任務可以直接使用額外的Serverless資源,避免使用實例自身資源,無需為實例預留額外的計算資源,顯著提升實例穩定性、減少OOM概率,且僅需為任務單獨付費。Serverless Computing詳情請參見Serverless Computing概述,Serverless Computing使用方法請參見Serverless Computing使用指南。
-- (可選)推薦使用Serverless Computing執行大數據量離線導入和ETL作業 SET hg_computing_resource = 'serverless'; INSERT INTO public.customer SELECT * FROM public.odps_customer_100g ; INSERT INTO public.lineitem SELECT * FROM public.odps_lineitem_100g ; INSERT INTO public.nation SELECT * FROM public.odps_nation_100g ; INSERT INTO public.orders SELECT * FROM public.odps_orders_100g ; INSERT INTO public.part SELECT * FROM public.odps_part_100g ; INSERT INTO public.partsupp SELECT * FROM public.odps_partsupp_100g ; INSERT INTO public.region SELECT * FROM public.odps_region_100g ; INSERT INTO public.supplier SELECT * FROM public.odps_supplier_100g ; -- 清理寫入文件 vacuum nation; vacuum region; vacuum supplier; vacuum customer; vacuum part; vacuum partsupp; vacuum orders; vacuum lineitem; -- 收集表的統計信息 analyze nation; analyze region; analyze lineitem; analyze orders; analyze customer; analyze part; analyze partsupp; analyze supplier; -- 針對非主鍵的JOIN KEY收集統計信息 analyze lineitem (l_orderkey,l_partkey,l_suppkey); analyze orders (o_custkey); analyze partsupp(ps_partkey,ps_suppkey); -- 重置配置,保證非必要的SQL不會使用serverless資源。 RESET hg_computing_resource;
2. 配置PTS壓測場景
登錄PTS控制臺,選擇 ,然后單擊PTS壓測。
刪除默認HTTP節點。
添加一個JDBC節點并配置,如下所示:
說明在實際測試過程中,一個業務會話內的壓測節點順序執行,不同業務會話間并行執行。在本文的測試場景中,只需保留一個業務會話,通過配置后續的并發數來模擬并行場景。
基本請求信息頁簽重要配置項說明:
數據庫類型:選擇PostgreSQL。
數據庫URL:格式為:
{ENDPOINT}:{PORT}/{DBNAME}
,您可從 頁面獲取。用戶名:輸入RAM用戶
pts-test
的AccessKey ID。密碼:輸入RAM用戶
pts-test
的AccessKey Secret。SQL:待執行的SQL語句,本文使用TPC-H 22條查詢語句,SQL語句詳情請參見TPC-H 22條查詢語句。
連接池配置頁簽配置項說明:
初始化連接數:初始化時建立物理連接的個數,本示例設置為
1
。獲取連接最大等待時間:這里保持默認即可。
最大連接數:最大活躍連接數量,本示例設置為
15
,以匹配單并發與多并發多種測試場景。最小連接數:池中最小空閑連接數量,本示例設置為
1
。
根據步驟c,配置其他查詢語句,針對此OLAP查詢場景測試,TPC-H包含22條查詢語句。您可采用API復制功能,在同一條業務會話下創建22個JDBC節點,并依次添加TPC-H 22條查詢語句。在壓測過程中,PTS會依次循環執行這22條查詢語句。
施壓配置
本示例選擇阿里云VPC內網壓測,其他施壓配置參數需要根據測試目的的不同進行調整。使用TPC-H數據進行OLAP場景測試,主要關注的是單條Query的執行時長,參數配置如下:
參數
說明
最大虛擬用戶數
本示例設置為1,以確保每條query執行時有充足的資源。
遞增模式
選擇手動調速,由于此處不涉及并發數的變化,因此只需保證并發數始終為1即可。
壓測總時長
此處涉及22條查詢語句,因此測試時長選擇60分鐘。
指定IP數
指發起壓測流量的IP地址數量,即施壓機數量。此處只需設置1個施壓IP。
3. 調試并啟動壓測
調試場景可驗證配置是否合理,避免壓測失敗,建議您先調試場景。
單擊保存去壓測,在溫馨提示頁面,選擇立即執行并勾選確認本次壓測已獲得準許并遵守當地法律,然后單擊啟動壓測。
4. 壓測報告查詢與分析
壓測完成后,在壓測報告頁簽單擊查看。
在報告詳情頁面的概覽頁簽,可以查看壓測場景運行成功與否。
壓測運行成功,可以查看成功率、平均RT、TPS/虛擬用戶、異常數、總請求數等信息。其中,平均RT對應平均查詢延遲,TPS指包含連接時間的每秒事務處理量。在本文的性能測試中,TPS與QPS大小一致。
壓測運行失敗,請求數等指標均為0,可以通過選擇
查看報錯信息,進行問題排查。
在報告詳情頁面單擊明細,在壓測報告明細頁簽查看每個壓測節點(對應每個Query)的相關指標。
如本文的OLAP查詢場景,針對單個業務會話中包含多個壓測節點的場景,查看單個壓測節點的指標。
在不配置虛擬用戶數時,OLAP查詢場景關注的指標為Query的查詢時長,即對應下圖的平均RT,本文測試得到的TPC-H 22條Query執行時長之和約為
25 s
。在配置虛擬用戶數后的OLAP場景測試結果如下,此時需要同時關注查詢時間及QPS兩項指標。可以看出,隨著虛擬用戶數的逐步提升,測試的QPS結果保持在5附近。您也可以前往明細頁,選擇對應時間段來查看每個虛擬用戶數下的性能結果。
更多關于壓測報告的內容查看與分析,詳情請參見查看JDBC壓測報告。
Key/Value點查場景
主要使用行存表,針對ORDERS使用行存表后,進行主鍵過濾的點查。
1. 配置Hologres準備測試數據
Key/Value點查場景繼續使用OLAP場景創建的數據庫,使用TPC-H數據集中的ORDERS
表進行測試。您在登錄Hologres實例后,可以執行如下語句建表并直接從OLAP場景的ORDERS
表中寫入數據。
創建內部表
由于點查場景需要使用行存表,所以不能使用OLAP查詢場景中創建的內部表,需要重新創建一張內部表。Key/Value點查場景測試需要設置主鍵并使用行存表,還需要設置合適的索引以達到更優的查詢性能。更多關于表屬性的信息請參見CREATE TABLE。
DROP TABLE IF EXISTS public.orders_row; BEGIN; CREATE TABLE public.orders_row( O_ORDERKEY INT NOT NULL PRIMARY KEY ,O_CUSTKEY INT NOT NULL ,O_ORDERSTATUS TEXT NOT NULL ,O_TOTALPRICE DECIMAL(15,2) NOT NULL ,O_ORDERDATE TIMESTAMPTZ NOT NULL ,O_ORDERPRIORITY TEXT NOT NULL ,O_CLERK TEXT NOT NULL ,O_SHIPPRIORITY INT NOT NULL ,O_COMMENT TEXT NOT NULL ); CALL SET_TABLE_PROPERTY('public.orders_row', 'orientation', 'row'); CALL SET_TABLE_PROPERTY('public.orders_row', 'clustering_key', 'o_orderkey'); CALL SET_TABLE_PROPERTY('public.orders_row', 'distribution_key', 'o_orderkey'); COMMIT;
寫入數據
-- (可選)推薦使用Serverless Computing執行大數據量離線導入和ETL作業 SET hg_computing_resource = 'serverless'; INSERT INTO public.orders_row SELECT * FROM public.orders; -- 重置配置,保證非必要的SQL不會使用serverless資源。 RESET hg_computing_resource;
2. 配置PTS壓測場景
登錄PTS控制臺,選擇 ,然后單擊PTS壓測。
刪除默認HTTP節點。
添加一個JDBC節點并配置,如下所示:
說明在實際測試過程中,一個業務會話內的壓測節點順序執行,不同業務會話間并行執行。在本文的測試場景中,只需保留一個業務會話,通過配置后續的并發數來模擬并行場景。
基本請求信息頁簽重要配置項說明:
數據庫類型:選擇PostgreSQL。
數據庫URL:格式為:
{ENDPOINT}:{PORT}/{DBNAME}
,您可從 頁面獲取。用戶名:輸入RAM用戶
pts-test
的AccessKey ID。密碼:輸入RAM用戶
pts-test
的AccessKey Secret。SQL:Key/Value點查場景的SQL與OLAP場景不同,查詢方式可以分為單值點查與多值點查,樣例SQL如下,詳情請參見測試方案介紹。
單值點查
SELECT O_ORDERKEY, O_CUSTKEY, O_ORDERSTATUS, O_TOTALPRICE, O_ORDERDATE, O_ORDERPRIORITY, O_CLERK, O_SHIPPRIORITY, O_COMMENT FROM public.orders_row WHERE o_orderkey = ?;
多值點查,此處以9個值為例。
SELECT O_ORDERKEY, O_CUSTKEY, O_ORDERSTATUS, O_TOTALPRICE, O_ORDERDATE, O_ORDERPRIORITY, O_CLERK, O_SHIPPRIORITY, O_COMMENT FROM public.orders_row WHERE o_orderkey IN (?, ?, ?, ?, ?, ?, ?, ?, ?);
占位符頁簽配置項說明:
Type:本示例占位符Type使用
bigint
。由于Key/Value點查場景需要隨機生成待查詢的主鍵值,在PTS中可以通過配置占位符實現。在SQL中使用?
作為占位符,并在占位符中依次配置?
代表的值。重要SQL中
?
的數量與占位符的數量需要相等,且二者按順序一一對應。即本實踐中需要為多值點查的SQL示例配置九行占位符。Value:PTS支持若干系統函數,本示例使用
random
函數,即Value值為:${sys.random(1,99999999)}
。
連接池配置頁簽配置項說明:
初始化連接數:初始化時建立物理連接的個數,Key/Value點查場景涉及并發,這里先將壓測節點的連接數配置為
20
,對應后文施壓配置中的每一臺施壓機都會產生20個連接。獲取連接最大等待時間:這里保持默認即可。
最大連接數:最大活躍連接數量,本示例設置為
20
。最小連接數:池中最小空閑連接數量,本示例設置為
20
。
施壓配置
本示例選擇阿里云VPC內網壓測,其他施壓配置參數需要根據測試目的的不同進行調整。
參數
說明
壓力模式
本示例選擇并發模式。
最大并發
由于本文Key/Value點查場景針對Hologres 64CU規格實例的測試并發數為
500
,本示例設為500
。因此,上文的連接池配置中,每臺機器在壓測節點上產生的連接數配置為20
,以達到需要測試的500
并發數。遞增模式
由于本文的測試場景不涉及并發數的變化,因此該部分只需保證并發數始終為
500
即可,即本示例選擇手動調速。壓測總時長
結合測試場景需要進行填寫。本場景僅涉及
1
條測試語句,因此測試時長選擇5
分鐘。指定IP數
指發起壓測流量的IP地址數量,即施壓機數量。本場景預估達到的QPS為
10
萬,單臺施壓機可以提供的QPS上限為4000
,因此本示例選擇25
臺施壓機。說明PTS在并發模式下,單個施壓機可以提供的QPS上限為4000,因此在實際測試場景中,需要結合可能達到的QPS值來確定施壓機數量,而后確定連接池配置。
3. 調試并啟動壓測
調試場景可驗證配置是否合理,避免壓測失敗,建議您先調試場景。
單擊保存去壓測,在溫馨提示頁面,選擇立即執行并勾選確認本次壓測已獲得準許并遵守當地法律,然后單擊啟動壓測。
4. 壓測結果查詢與分析
對于單值點查,本實踐壓測得到的QPS平均值超過
100000
。對于多值點查,以30個值的批量點查為例,本實踐得到的測試結果如下,其中QPS平均值為
34819
。最終的QPS性能為圖示平均QPS值乘以批量點查數量30
,即34819 * 30 =1044570
。
數據更新場景
主要用于測試OLAP引擎在有主鍵的情況下數據更新的性能。
本文的數據更新場景使用與Key/Value點查場景相同的數據表。
在配置PTS壓測場景內容中,僅JDBC壓測節點信息中的SQL語句與 Key/Value點查場景單值點查不同(此處忽略多值點查情況),其余配置項均可保持一致。
INSERT INTO public.orders_row (o_orderkey, o_custkey, o_orderstatus, o_totalprice, o_orderdate, o_orderpriority, o_clerk, o_shippriority, o_comment) VALUES (?, 1, 'demo', 1.1, '2021-01-01', 'demo', 'demo', 1, 'demo') ON CONFLICT (o_orderkey) DO UPDATE SET (o_orderkey, o_custkey, o_orderstatus, o_totalprice, o_orderdate, o_orderpriority, o_clerk, o_shippriority, o_comment) = ROW (excluded.*);
啟動壓測和報告分析請參考Key/Value點查場景。
常見報錯
在調試過程中,可能會遇到如下錯誤,報錯原因可能是數據庫URL錯誤、用戶名密碼錯誤等,導致連接失敗。