EXPLAIN deyimi, MaxCompute SQL'de bir SELECT sorgusunun yürütme planını görüntüler. Bu sayede sorgu ifadelerindeki veya tablo yapılarındaki performans darboğazlarını tespit edebilirsiniz.
Bir sorgu, her biri bir veya daha fazla görev içeren bir veya daha fazla işe (job) karşılık gelir. EXPLAIN çıktısı, işlerin, görevlerin ve operatörlerin birbirleriyle olan ilişkisini ortaya koyarak SQL kodunuzu anlamanıza ve optimize etmenize yardımcı olur.
Söz dizimi
EXPLAIN <query>;
query: Zorunludur. Bir SELECT ifadesidir. Daha fazla bilgi için bkz. SELECT söz dizimi.
Çıktı yapısı
EXPLAIN çıktısı üç bölümden oluşur:
İş bağımlılıkları — Tüm işleri ve bunların yürütülme sırasını listeler.
job0 is root jobifadesi, sorgunun tek bir kök iş gerektirdiğini gösterir.-
Görev bağımlılıkları — Her iş içindeki görevleri ve bunların bağımlılıklarını listeler. Aşağıdaki çıktı,
job0işininM1,M2veJ3_1_2_Stg1olmak üzere üç görev içerdiğini; ayrıca MaxCompute'ünJ3_1_2_Stg1görevini yalnızcaM1veM2tamamlandıktan sonra çalıştırdığını gösterir.In Job job0: root Tasks: M1, M2 J3_1_2_Stg1 depends on: M1, M2 -
Operatör ayrıntıları — Her görev içindeki operatörleri ve yürütme semantiğini açıklar.
Data sourcesatırı görevin giriş verisini tanımlar. Sonraki her satır bir operatörü ve parametrelerini temsil eder; girintiler operatör işlem hattındaki hiyerarşiyi gösterir.In Task M2: Data source: mf_mc_bj.sale_detail_jt/sale_date=2013/region=china TS: mf_mc_bj.sale_detail_jt/sale_date=2013/region=china FIL: ISNOTNULL(customer_id) RS: order: + nullDirection: * optimizeOrderBy: False valueDestLimit: 0 dist: HASH keys: customer_id values: customer_id (string) total_price (double) partitions: customer_id
Görev adlandırma kuralları
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Operatörler
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Her operatör satırı, işlem hattının o noktasındaki tahmini satır sayısını ve veri boyutunu gösteren bir Statistics satırı da üretir. Örneğin, Statistics: Num rows: 3.0, Data size: 324.0 bilgisi, iyileştiricinin operatörün kaç satır üreteceğini beklediğini gösterir. Bu tahminleri kullanarak, beklenenden çok daha fazla veri işleyen operatörleri tespit edebilirsiniz; bu durum genellikle eksik bir filtrenin veya eğri bir birleştirme anahtarının göstergesidir.
ReduceSinkOperator (RS) çıktısı aşağıdaki alanları içerir:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Sınırlamalar
Bir sorgu karmaşık olduğunda ve EXPLAIN çıktısı 4 MB'ı aştığında, üst katman uygulama API limiti aşılır ve çıktı kesilir. Bu durumu aşmak için sorguyu daha küçük alt sorgulara ayırın ve her biri için EXPLAIN komutunu ayrı ayrı çalıştırın.
Örnekler
Örnek verileri hazırlama
sale_detail ve sale_detail_jt adlı iki bölümlenmiş tablo oluşturun ve örnek veriler ekleyin.
-- Create two partitioned tables named sale_detail and sale_detail_jt.
CREATE TABLE if NOT EXISTS sale_detail
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
CREATE TABLE if NOT EXISTS sale_detail_jt
(
shop_name STRING,
customer_id STRING,
total_price DOUBLE
)
PARTITIONED BY (sale_date STRING, region STRING);
-- Add partitions to the two tables.
ALTER TABLE sale_detail ADD PARTITION (sale_date='2013', region='china') PARTITION (sale_date='2014', region='shanghai');
ALTER TABLE sale_detail_jt ADD PARTITION (sale_date='2013', region='china');
-- Insert data into the tables.
INSERT INTO sale_detail PARTITION (sale_date='2013', region='china') VALUES ('s1','c1',100.1),('s2','c2',100.2),('s3','c3',100.3);
INSERT INTO sale_detail PARTITION (sale_date='2014', region='shanghai') VALUES ('null','c5',null),('s6','c6',100.4),('s7','c7',100.5);
INSERT INTO sale_detail_jt PARTITION (sale_date='2013', region='china') VALUES ('s1','c1',100.1),('s2','c2',100.2),('s5','c2',100.2);
Verileri doğrulayın:
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail;
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s3 | c3 | 100.3 | 2013 | china |
| null | c5 | NULL | 2014 | shanghai |
| s6 | c6 | 100.4 | 2014 | shanghai |
| s7 | c7 | 100.5 | 2014 | shanghai |
+------------+-------------+-------------+------------+------------+
SET odps.sql.allow.fullscan=true;
SELECT * FROM sale_detail_jt;
+------------+-------------+-------------+------------+------------+
| shop_name | customer_id | total_price | sale_date | region |
+------------+-------------+-------------+------------+------------+
| s1 | c1 | 100.1 | 2013 | china |
| s2 | c2 | 100.2 | 2013 | china |
| s5 | c2 | 100.2 | 2013 | china |
+------------+-------------+-------------+------------+------------+
JOIN işlemi için bölümlenmemiş bir tablo oluşturun:
SET odps.sql.allow.fullscan=true;
CREATE TABLE shop AS SELECT shop_name, customer_id, total_price FROM sale_detail;
Örnek 1: Standart JOIN ve toplama işlemi
Bu örnekte, sale_detail_jt ve sale_detail tabloları arasında INNER JOIN gerçekleştiren, sonuçları gruplayan ve limit ile sıralama uygulayan bir sorgu üzerinde EXPLAIN çalıştırılır.
Sorgu:
SELECT a.customer_id AS ashop, SUM(a.total_price) AS ap,COUNT(b.total_price) AS bp
FROM (SELECT * FROM sale_detail_jt WHERE sale_date='2013' AND region='china') a
INNER JOIN (SELECT * FROM sale_detail WHERE sale_date='2013' AND region='china') b
ON a.customer_id=b.customer_id
GROUP BY a.customer_id
ORDER BY a.customer_id
LIMIT 10;
EXPLAIN çalıştırın:
EXPLAIN
SELECT a.customer_id AS ashop, SUM(a.total_price) AS ap,COUNT(b.total_price) AS bp
FROM (SELECT * FROM sale_detail_jt WHERE sale_date='2013' AND region='china') a
INNER JOIN (SELECT * FROM sale_detail WHERE sale_date='2013' AND region='china') b
ON a.customer_id=b.customer_id
GROUP BY a.customer_id
ORDER BY a.customer_id
LIMIT 10;
Çıktı:
job0 is root job
In Job job0:
root Tasks: M1
M2_1 depends on: M1
R3_2 depends on: M2_1
R4_3 depends on: R3_2
In Task M1:
Data source: doc_****.default.sale_detail/sale_date=2013/region=china
TS: doc_****.default.sale_detail/sale_date=2013/region=china
Statistics: Num rows: 3.0, Data size: 324.0
FIL: ISNOTNULL(customer_id)
Statistics: Num rows: 2.7, Data size: 291.6
RS: valueDestLimit: 0
dist: BROADCAST
keys:
values:
customer_id (string)
total_price (double)
partitions:
Statistics: Num rows: 2.7, Data size: 291.6
In Task M2_1:
Data source: doc_****.default.sale_detail_jt/sale_date=2013/region=china
TS: doc_****.default.sale_detail_jt/sale_date=2013/region=china
Statistics: Num rows: 3.0, Data size: 324.0
FIL: ISNOTNULL(customer_id)
Statistics: Num rows: 2.7, Data size: 291.6
HASHJOIN:
Filter1 INNERJOIN StreamLineRead1
keys:
0:customer_id
1:customer_id
non-equals:
0:
1:
bigTable: Filter1
Statistics: Num rows: 3.6450000000000005, Data size: 787.32
RS: order: +
nullDirection: *
optimizeOrderBy: False
valueDestLimit: 0
dist: HASH
keys:
customer_id
values:
customer_id (string)
total_price (double)
total_price (double)
partitions:
customer_id
Statistics: Num rows: 3.6450000000000005, Data size: 422.82000000000005
In Task R3_2:
AGGREGATE: group by:customer_id
UDAF: SUM(total_price) (__agg_0_sum)[Complete],COUNT(total_price) (__agg_1_count)[Complete]
Statistics: Num rows: 1.0, Data size: 116.0
RS: order: +
nullDirection: *
optimizeOrderBy: True
valueDestLimit: 10
dist: HASH
keys:
customer_id
values:
customer_id (string)
__agg_0 (double)
__agg_1 (bigint)
partitions:
Statistics: Num rows: 1.0, Data size: 116.0
In Task R4_3:
SEL: customer_id,__agg_0,__agg_1
Statistics: Num rows: 1.0, Data size: 116.0
SEL: customer_id ashop, __agg_0 ap, __agg_1 bp, customer_id
Statistics: Num rows: 1.0, Data size: 216.0
FS: output: Screen
schema:
ashop (string)
ap (double)
bp (bigint)
Statistics: Num rows: 1.0, Data size: 116.0
OK
Yürütme planı dört görev göstermektedir:
M1 —
sale_detailtablosunu tarar, nullcustomer_iddeğerlerini filtreler ve verileri tüm aşağı akış görevlerine yayınlar (dist: BROADCAST). Boşkeysvepartitionsalanları, verilerin bölümlenmediğini — yani her satırın her yere gönderildiğini — doğrular.M2_1 —
sale_detail_jttablosunu tarar, nullcustomer_iddeğerlerini filtreler ve M1'den gelen yayınlanmış verilerle map tarafında hash birleştirme (HASHJOIN) gerçekleştirir. Sonuçlar, aşağı akış reduce görevi içincustomer_idanahtarına göre hash ile bölümlenir.R3_2 —
GROUP BY customer_idile toplama işlemi yapar;SUM(total_price)veCOUNT(total_price)hesaplar. Her ikisi deCompletemodunda çalışır; bu, kısmi ön toplama olmadan tam toplamanın tek aşamada gerçekleştiği anlamına gelir.R4_3 — Nihai sütunları seçer, bunlara takma ad atar (
ashop,ap,bp) ve çıktıyı ekrana yazar.
Örnek 2: Map tarafı birleştirme (MAPJOIN ipucu)
Bu örnekte, eşitsizlik içeren bir birleştirme koşuluyla (a.total_price < b.total_price) map tarafı birleştirmeyi zorlamak için /*+ mapjoin(a) */ ipucu kullanılır.
Sorgu:
SELECT /*+ mapjoin(a) */
a.customer_id AS ashop, SUM(a.total_price) AS ap,COUNT(b.total_price) AS bp
FROM (SELECT * FROM sale_detail_jt
WHERE sale_date='2013' AND region='china') a
INNER JOIN (SELECT * FROM sale_detail WHERE sale_date='2013' AND region='china') b
ON a.total_price<b.total_price
GROUP BY a.customer_id
ORDER BY a.customer_id
LIMIT 10;
EXPLAIN çalıştırın:
EXPLAIN
SELECT /*+ mapjoin(a) */
a.customer_id AS ashop, SUM(a.total_price) AS ap,COUNT(b.total_price) AS bp
FROM (SELECT * FROM sale_detail_jt
WHERE sale_date='2013' AND region='china') a
INNER JOIN (SELECT * FROM sale_detail WHERE sale_date='2013' AND region='china') b
ON a.total_price<b.total_price
GROUP BY a.customer_id
ORDER BY a.customer_id
LIMIT 10;
Çıktı:
job0 is root job
In Job job0:
root Tasks: M1
M2_1 depends on: M1
R3_2 depends on: M2_1
R4_3 depends on: R3_2
In Task M1:
Data source: doc_****.sale_detail_jt/sale_date=2013/region=china
TS: doc_****.sale_detail_jt/sale_date=2013/region=china
Statistics: Num rows: 3.0, Data size: 324.0
RS: valueDestLimit: 0
dist: BROADCAST
keys:
values:
customer_id (string)
total_price (double)
partitions:
Statistics: Num rows: 3.0, Data size: 324.0
In Task M2_1:
Data source: doc_****.sale_detail/sale_date=2013/region=china
TS: doc_****.sale_detail/sale_date=2013/region=china
Statistics: Num rows: 3.0, Data size: 24.0
HASHJOIN:
StreamLineRead1 INNERJOIN TableScan2
keys:
0:
1:
non-equals:
0:
1:
bigTable: TableScan2
Statistics: Num rows: 9.0, Data size: 1044.0
FIL: LT(total_price,total_price)
Statistics: Num rows: 6.75, Data size: 783.0
AGGREGATE: group by:customer_id
UDAF: SUM(total_price) (__agg_0_sum)[Partial_1],COUNT(total_price) (__agg_1_count)[Partial_1]
Statistics: Num rows: 2.3116438356164384, Data size: 268.1506849315069
RS: order: +
nullDirection: *
optimizeOrderBy: False
valueDestLimit: 0
dist: HASH
keys:
customer_id
values:
customer_id (string)
__agg_0_sum (double)
__agg_1_count (bigint)
partitions:
customer_id
Statistics: Num rows: 2.3116438356164384, Data size: 268.1506849315069
In Task R3_2:
AGGREGATE: group by:customer_id
UDAF: SUM(__agg_0_sum)[Final] __agg_0,COUNT(__agg_1_count)[Final] __agg_1
Statistics: Num rows: 1.6875, Data size: 195.75
RS: order: +
nullDirection: *
optimizeOrderBy: True
valueDestLimit: 10
dist: HASH
keys:
customer_id
values:
customer_id (string)
__agg_0 (double)
__agg_1 (bigint)
partitions:
Statistics: Num rows: 1.6875, Data size: 195.75
In Task R4_3:
SEL: customer_id,__agg_0,__agg_1
Statistics: Num rows: 1.6875, Data size: 195.75
SEL: customer_id ashop, __agg_0 ap, __agg_1 bp, customer_id
Statistics: Num rows: 1.6875, Data size: 364.5
FS: output: Screen
schema:
ashop (string)
ap (double)
bp (bigint)
Statistics: Num rows: 1.6875, Data size: 195.75
OK
1. Örnekten temel farklar şunlardır:
M1, mapjoin ipucuyla belirtilen
sale_detail_jttablosunu filtre uygulamadan yayınlar. Bunun nedeni, eşitsizlik içeren birleştirme koşulunun birleştirmeden önce değil, sonra değerlendirilmesidir.M2_1, HASHJOIN işlemini gerçekleştirir, ardından
LT(total_price, total_price)filtresini (a.total_price < b.total_pricekoşulunu temsil eder) uygular ve kısmi toplama (Partial_1aşaması) çalıştırır.Partial_1aşaması, verileri map tarafında önceden toplayarak reduce görevine gönderilen veri hacmini azaltır.R3_2, M2_1'den gelen kısmi sonuçları birleştirerek toplamayı
Finalaşamasında tamamlar. İki aşamalı (Partial_1->Final) yaklaşım, map tarafındaki veri hacmi büyük olduğunda 1. Örnekteki tek aşamalıCompletemoduna göre daha verimlidir.R4_3, 1. Örnekle aynı şekilde nihai çıktı sütunlarını seçer ve takma ad atar.