優化器
MaxCompute的優化器是基于代價的優化器,需要基于數據的一些特征(即元數據),例如行數、字符串平均長度,準確估算代價。本文為您介紹MaxCompute收集元數據的方法,為優化查詢性能提供幫助。
背景信息
如果獲取不到準確的元數據,優化器會對代價產生誤判,生成不良的執行計劃,因此元數據對于優化器至關重要。表的元數據主要是通過對其數據收集統計信息(Column stats)來獲取,該元數據是推算其它元數據的基礎。
MaxCompute提供了如下兩種收集方式:
異步收集框架(Analyze):用戶異步通過
analyze
命令收集。需要用戶主動收集。說明MaxCompute客戶端版本要求在0.35以上。
同步收集框架(Freeride):在數據生成的同時,自動收集Column stats,更加自動化,但對查詢時延有影響。
MaxCompute對不同數據類型的數據收集的Column stats指標如下。
Column stats指標/數據類型 | 數值類型(TINYINT、SMALLINT、INT、BIGINT、DOUBLE、DECIMAL、NUMERIC) | 字符類型(STRING、VARCHAR、CHAR) | 二進制類型(BINARY) | 布爾類型(BOOLEAN) | 日期類型(TIMESTAMP、DATE、INTERVAL) | 復雜類型(MAP、STRUCT、ARRAY) |
min(最小值) | Y | N | N | N | Y | N |
max(最大值) | Y | N | N | N | Y | N |
nNulls(空值個數) | Y | Y | Y | Y | Y | Y |
avgColLen(平均列長度) | N | Y | Y | N | N | N |
maxColLen(最大列長度) | N | Y | Y | N | N | N |
ndv(不同值個數) | Y | Y | Y | Y | Y | N |
topK(出現頻率最高的前K個值) | Y | Y | Y | Y | Y | N |
Y表示支持,N表示不支持。
使用場景
Column stats指標的使用場景如下:
Column stats指標 | 功能 | 場景 | 說明 |
min(最小值)或max(最大值) | 獲取最小值或最大值提升性能優化準確率。 | 場景1:估算輸出記錄數。 | 只提供數據類型時,值域很大。當提供了最大最小值時,優化器可以對過濾條件的選擇度有更合理的估計,從而提供更優的執行計劃。 |
場景2:將過濾條件下推至存儲層,減少讀取的數據量。 | 在MaxCompute中,過濾條件 | ||
nNulls(空值個數) | 根據是否為空值信息提高判斷效率。 | 場景1:運行作業時減少NULL判斷。 | 在運行作業時,對于任何類型數據都需要判斷是否為NULL,如果能準確得到nNulls=0,此判斷邏輯可以被忽略以提高計算性能。 |
場景2:基于過濾條件裁剪數據。 | 如果整列數據值都為NULL,一般的過濾條件可以直接變成 | ||
avgColLen(平均列長度)或maxColLen(最大列長度) | 獲取列長度信息,預估資源消耗,減少Shuffle。 | 場景1:Hash聚簇表內存估計。 | 例如,根據avgColLen,可以估計變長字段的內存消耗,得到Record的內存消耗。從而可以選擇性進行Auto Mapjoin,即建立Hash聚簇表Broadcast處理機制,減少一次Shuffle操作。對于輸入為大表的場景,減少Shuffle的代價非常明顯,能有效提升性能。 |
場景2:減少Shuffle的數據量。 | 無。 | ||
ndv(不同值個數) | 根據基數信息提高執行計劃的質量。 | 場景1:JOIN的輸出記錄數推算。 |
|
場景2:JOIN排序。 | 基于估算的輸出記錄數,優化器還可以自動調整JOIN順序。例如把會有數據過濾的JOIN操作往前調,把有數據膨脹的JOIN操作往后調。 | ||
topK(出現頻率最高的前K個值) | 估算數據分布減少數據傾斜帶來的性能影響。 | 場景1:傾斜數據進行JOIN的優化處理。 | 當JOIN的輸入均較大,且無法通過Mapjoin方式將非大表全裝載至內存時,在一路中某些數據存在傾斜狀態,而其它路比較有限。MaxCompute可以自動轉換成Skew Data使用MAP JOIN處理,非傾斜數據使用MERGE JOIN進行處理,最后再合并兩部分的計算結果。此功能對于大數據量JOIN,收益非常明顯,降低失敗后的人工處理成本。 |
場景2:估算輸出記錄數。 | 利用ndv、min、max進行輸出記錄數的估算是基于數據“平均”的假設。在用戶數據存在明顯傾斜時,基于前面假設的推論會存在“失真”。需要對傾斜數據進行特殊處理,而其它數據利用平均假設更合適。 |
Analyze使用說明
收集Column Stats
以分區表和非分區表為例介紹Analyze使用方法。
非分區表
支持對指定的列或全部列收集Column Stats。
通過MaxCompute客戶端創建一張非分區表analyze2_test,命令示例如下:
create table if not exists analyze2_test (tinyint1 tinyint, smallint1 smallint, int1 int, bigint1 bigint, double1 double, decimal1 decimal, decimal2 decimal(20,10), string1 string, varchar1 varchar(10), boolean1 boolean, timestamp1 timestamp, datetime1 datetime ) lifecycle 30;
向表中插入數據,命令示例如下:
insert overwrite table analyze2_test select * from values (1Y, 20S, 4, 8L, 123452.3, 12.4, 52.5, 'str1', 'str21', false, timestamp '2018-09-17 00:00:00', datetime '2018-09-17 00:59:59') ,(10Y, 2S, 7, 11111118L, 67892.3, 22.4, 42.5, 'str12', 'str200', true, timestamp '2018-09-17 00:00:00', datetime '2018-09-16 00:59:59') ,(20Y, 7S, 4, 2222228L, 12.3, 2.4, 2.57, 'str123', 'str2', false, timestamp '2018-09-18 00:00:00', datetime '2018-09-17 00:59:59') ,(null, null, null, null, null, null, null, null, null, null, null , null) as t(tinyint1, smallint1, int1, bigint1, double1, decimal1, decimal2, string1, varchar1, boolean1, timestamp1, datetime1);
執行
analyze
命令收集某一列、多列或全部列的Column Stats,命令示例如下:--收集tinyint1列的Column Stats。 analyze table analyze2_test compute statistics for columns (tinyint1); --收集smallint1、string1、boolean1和timestamp1列的Column Stats。 analyze table analyze2_test compute statistics for columns (smallint1, string1, boolean1, timestamp1); --收集全部列的Column Stats。 analyze table analyze2_test compute statistics for columns;
執行
show statistic
命令測試Column Stats收集結果,命令示例如下:--測試tinyint1列的Column Stats收集結果。 show statistic analyze2_test columns (tinyint1); --測試smallint1、string1、boolean1和timestamp1列的Column Stats收集結果。 show statistic analyze2_test columns (smallint1, string1, boolean1, timestamp1); --測試全部列的Column Stats收集結果。 show statistic analyze2_test columns;
返回結果如下:
--tinyint1列的Column Stats收集結果。 ID = 20201126085225150gnqo**** tinyint1:MaxValue: 20 --對應max。 tinyint1:DistinctNum: 4.0 --對應ndv。 tinyint1:MinValue: 1 --對應min。 tinyint1:NullNum: 1.0 --對應nNulls。 tinyint1:TopK: {1=1.0, 10=1.0, 20=1.0} --對應topK。10=1.0表示列值10出現的頻次為1。topK最多顯示前20個最高頻次的值。 --smallint1、string1、boolean1和timestamp1列的Column Stats收集結果。 ID = 20201126091636149gxgf**** smallint1:MaxValue: 20 smallint1:DistinctNum: 4.0 smallint1:MinValue: 2 smallint1:NullNum: 1.0 smallint1:TopK: {2=1.0, 7=1.0, 20=1.0} string1:MaxLength 6.0 --對應maxColLen。 string1:AvgLength: 3.0 --對應avgColLen。 string1:DistinctNum: 4.0 string1:NullNum: 1.0 string1:TopK: {str1=1.0, str12=1.0, str123=1.0} boolean1:DistinctNum: 3.0 boolean1:NullNum: 1.0 boolean1:TopK: {false=2.0, true=1.0} timestamp1:DistinctNum: 3.0 timestamp1:NullNum: 1.0 timestamp1:TopK: {2018-09-17 00:00:00.0=2.0, 2018-09-18 00:00:00.0=1.0} --全部列的Column Stats收集結果。 ID = 20201126092022636gzm1**** tinyint1:MaxValue: 20 tinyint1:DistinctNum: 4.0 tinyint1:MinValue: 1 tinyint1:NullNum: 1.0 tinyint1:TopK: {1=1.0, 10=1.0, 20=1.0} smallint1:MaxValue: 20 smallint1:DistinctNum: 4.0 smallint1:MinValue: 2 smallint1:NullNum: 1.0 smallint1:TopK: {2=1.0, 7=1.0, 20=1.0} int1:MaxValue: 7 int1:DistinctNum: 3.0 int1:MinValue: 4 int1:NullNum: 1.0 int1:TopK: {4=2.0, 7=1.0} bigint1:MaxValue: 11111118 bigint1:DistinctNum: 4.0 bigint1:MinValue: 8 bigint1:NullNum: 1.0 bigint1:TopK: {8=1.0, 2222228=1.0, 11111118=1.0} double1:MaxValue: 123452.3 double1:DistinctNum: 4.0 double1:MinValue: 12.3 double1:NullNum: 1.0 double1:TopK: {12.3=1.0, 67892.3=1.0, 123452.3=1.0} decimal1:MaxValue: 22.4 decimal1:DistinctNum: 4.0 decimal1:MinValue: 2.4 decimal1:NullNum: 1.0 decimal1:TopK: {2.4=1.0, 12.4=1.0, 22.4=1.0} decimal2:MaxValue: 52.5 decimal2:DistinctNum: 4.0 decimal2:MinValue: 2.57 decimal2:NullNum: 1.0 decimal2:TopK: {2.57=1.0, 42.5=1.0, 52.5=1.0} string1:MaxLength 6.0 string1:AvgLength: 3.0 string1:DistinctNum: 4.0 string1:NullNum: 1.0 string1:TopK: {str1=1.0, str12=1.0, str123=1.0} varchar1:MaxLength 6.0 varchar1:AvgLength: 3.0 varchar1:DistinctNum: 4.0 varchar1:NullNum: 1.0 varchar1:TopK: {str2=1.0, str200=1.0, str21=1.0} boolean1:DistinctNum: 3.0 boolean1:NullNum: 1.0 boolean1:TopK: {false=2.0, true=1.0} timestamp1:DistinctNum: 3.0 timestamp1:NullNum: 1.0 timestamp1:TopK: {2018-09-17 00:00:00.0=2.0, 2018-09-18 00:00:00.0=1.0} datetime1:DistinctNum: 3.0 datetime1:NullNum: 1.0 datetime1:TopK: {1537117199000=2.0, 1537030799000=1.0}
分區表
支持對指定的某個分區收集Column Stats。
通過MaxCompute客戶端創建一張分區表srcpart,命令示例如下:
create table if not exists srcpart_test (key string, value string) partitioned by (ds string, hr string) lifecycle 30;
向表中插入數據,命令示例如下:
insert into table srcpart_test partition(ds='20201220', hr='11') values ('123', 'val_123'), ('76', 'val_76'), ('447', 'val_447'), ('1234', 'val_1234'); insert into table srcpart_test partition(ds='20201220', hr='12') values ('3', 'val_3'), ('12331', 'val_12331'), ('42', 'val_42'), ('12', 'val_12'); insert into table srcpart_test partition(ds='20201221', hr='11') values ('543', 'val_543'), ('2', 'val_2'), ('4', 'val_4'), ('9', 'val_9'); insert into table srcpart_test partition(ds='20201221', hr='12') values ('23', 'val_23'), ('56', 'val_56'), ('4111', 'val_4111'), ('12333', 'val_12333');
執行
analyze
命令收集指定分區的Column Stats,命令示例如下:analyze table srcpart_test partition(ds='20201221') compute statistics for columns (key , value);
執行
show statistic
命令測試Column Stats收集結果,命令示例如下:show statistic srcpart_test partition (ds='20201221') columns (key , value);
返回結果如下:
ID = 20210105121800689g28p**** (ds=20201221,hr=11) key:MaxLength 3.0 (ds=20201221,hr=11) key:AvgLength: 1.0 (ds=20201221,hr=11) key:DistinctNum: 4.0 (ds=20201221,hr=11) key:NullNum: 0.0 (ds=20201221,hr=11) key:TopK: {2=1.0, 4=1.0, 543=1.0, 9=1.0} (ds=20201221,hr=11) value:MaxLength 7.0 (ds=20201221,hr=11) value:AvgLength: 5.0 (ds=20201221,hr=11) value:DistinctNum: 4.0 (ds=20201221,hr=11) value:NullNum: 0.0 (ds=20201221,hr=11) value:TopK: {val_2=1.0, val_4=1.0, val_543=1.0, val_9=1.0} (ds=20201221,hr=12) key:MaxLength 5.0 (ds=20201221,hr=12) key:AvgLength: 3.0 (ds=20201221,hr=12) key:DistinctNum: 4.0 (ds=20201221,hr=12) key:NullNum: 0.0 (ds=20201221,hr=12) key:TopK: {12333=1.0, 23=1.0, 4111=1.0, 56=1.0} (ds=20201221,hr=12) value:MaxLength 9.0 (ds=20201221,hr=12) value:AvgLength: 7.0 (ds=20201221,hr=12) value:DistinctNum: 4.0 (ds=20201221,hr=12) value:NullNum: 0.0 (ds=20201221,hr=12) value:TopK: {val_12333=1.0, val_23=1.0, val_4111=1.0, val_56=1.0}
刷新元數據中表的記錄數
MaxCompute中多種任務都可能會影響表的記錄數,而大部分任務只統計任務本身影響的記錄數,并且一些任務因為分布式任務的動態性和數據更新關系在時間上的不確定性,并不保證對影響的記錄數統計全部準確,因此可以使用Analyze命令刷新元數據中表的記錄數統計值,保證記錄數的準確性。表的記錄數支持在DataWorks數據地圖中查看,詳情請參見查看表詳情。
刷新全表的記錄數。
set odps.sql.analyze.table.stats=only; analyze table <table_name> compute statistics for columns;
table_name
為表名稱。刷新表中某列的記錄數。
set odps.sql.analyze.table.stats=only; analyze table <table_name> compute statistics for columns (<column_name>);
table_name
為表名稱,column_name
為列名稱。刷新分區中某列的記錄數。
set odps.sql.analyze.table.stats=only; analyze table <table_name> partition(<pt_spec>) compute statistics for columns (<column_name>);
table_name
為表名稱,pt_spec
為分區值,column_name
為列名稱。
Freeride使用說明
您需要在Session級別同時執行如下兩個命令設置屬性:
set odps.optimizer.stat.collect.auto=true;
:啟用Freeride功能,自動收集表的Column Stats。set odps.optimizer.stat.collect.plan=xx;
:配置收集計劃,收集指定列的指定Column Stats指標。--收集target_table表中列名為key的avgColLen指標。 set odps.optimizer.stat.collect.plan={"target_table":"{\"key\":\"AVG_COL_LEN\"}"} --收集target_table表中列名為s_binary的min和max,以及列名為s_int的topK和nNulls指標。 set odps.optimizer.stat.collect.plan={"target_table":"{\"s_binary\":\"MIN,MAX\",\"s_int\":\"TOPK,NULLS\"}"};
如果出現配置上述屬性后,無法收集到信息的問題,可能是Freeride功能未生效。您需要查看Logview的json summary頁簽中是否可以找到odps.optimizer.stat.collect.auto
屬性。如果沒有找到該屬性,說明當前服務器版本較低,無法使用該功能。MaxCompute會陸續將服務器版本升級至支持Freeride功能的版本。
Column Stats指標在set odps.optimizer.stat.collect.plan=xx;
中的標識對照關系為:
min:MIN
max:MAX
nNulls:NULLS
avgColLen:AVG_COL_LEN
maxColLen:MAX_COL_LEN
ndv:NDV
topK:TOPK
MaxCompute支持通過create table
、insert into
、insert overwrite
三種方式觸發Freeride收集Column Stats。
為呈現上述三種方式的實現,假設先創建一個源表src_test并插入數據,命令示例如下:
create table if not exists src_test (key string, value string);
insert overwrite table src_test values ('100', 'val_100'), ('100', 'val_50'), ('200', 'val_200'), ('200', 'val_300');
create table
:在創建目標表target的同時,收集對應的Column Stats。命令示例如下:--創建目標表。 set odps.optimizer.stat.collect.auto=true; set odps.optimizer.stat.collect.plan={"target_test":"{\"key\":\"AVG_COL_LEN,NULLS\"}"}; create table target_test as select key, value from src_test; --測試Column Stats收集結果。 show statistic target_test columns;
返回結果如下:
key:AvgLength: 3.0 key:NullNum: 0.0
insert into
:在使用insert into
追加數據時,收集對應的Column Stats。命令示例如下:--創建一個目標表。 create table freeride_insert_into_table like src_test; --追加數據。 set odps.optimizer.stat.collect.auto=true; set odps.optimizer.stat.collect.plan={"freeride_insert_into_table":"{\"key\":\"AVG_COL_LEN,NULLS\"}"}; insert into table freeride_insert_into_table select key, value from src order by key, value limit 10; --測試Column Stats收集結果。 show statistic freeride_insert_into_table columns;
insert overwrite
:在使用insert overwrite
覆蓋數據時,收集對應的Column Stats。命令示例如下:--創建一個目標表。 create table freeride_insert_overwrite_table like src_test; --覆蓋數據。 set odps.optimizer.stat.collect.auto=true; set odps.optimizer.stat.collect.plan={"freeride_insert_overwrite_table":"{\"key\":\"AVG_COL_LEN,NULLS\"}"}; insert overwrite table freeride_insert_overwrite_table select key, value from src_test order by key, value limit 10; --測試Column Stats收集結果。 show statistic freeride_insert_overwrite_table columns;