全部产品
Search
文档中心

Hologres:EXPLAIN dan EXPLAIN ANALYZE

更新时间:Jul 02, 2025

Jika performa kueri SQL buruk atau hasil kueri tidak sesuai harapan, Anda dapat menjalankan pernyataan EXPLAIN dan EXPLAIN ANALYZE di Hologres untuk mendapatkan rencana eksekusi dari pernyataan kueri. Dengan cara ini, Anda dapat mengoptimalkan pernyataan kueri atau struktur database. Topik ini menjelaskan cara menjalankan pernyataan EXPLAIN dan EXPLAIN ANALYZE di Hologres serta arti dari setiap operator dalam output.

Pengenalan Rencana Eksekusi

Di Hologres, query optimizer (QO) menghasilkan rencana eksekusi untuk setiap pernyataan SQL. Query engine (QE) menghasilkan rencana eksekusi akhir berdasarkan rencana yang dihasilkan oleh QO. QE kemudian mengeksekusi pernyataan SQL dan mengembalikan hasil. Rencana eksekusi berisi informasi seperti statistik SQL, operator, dan durasi eksekusi operator. Rencana eksekusi yang baik membantu Anda mengeksekusi pernyataan SQL dengan lebih sedikit sumber daya dan mendapatkan hasil lebih cepat. Rencana eksekusi sangat penting untuk pengembangan data rutin karena membantu mengidentifikasi masalah dalam pernyataan SQL dan mengoptimalkannya.

Hologres kompatibel dengan PostgreSQL. Anda dapat menanyakan rencana eksekusi pernyataan SQL dengan menjalankan pernyataan EXPLAIN dan EXPLAIN ANALYZE.

  • EXPLAIN: Pernyataan ini mengembalikan rencana eksekusi SQL yang diperkirakan oleh QO berdasarkan karakteristik pernyataan SQL, bukan rencana eksekusi sebenarnya. Rencana eksekusi yang diperkirakan ini memberikan referensi untuk eksekusi pernyataan SQL.

  • EXPLAIN ANALYZE: Pernyataan ini mengembalikan rencana eksekusi SQL sebenarnya. Dibandingkan dengan rencana eksekusi yang diperkirakan oleh pernyataan EXPLAIN, rencana eksekusi sebenarnya yang dikembalikan oleh pernyataan EXPLAIN ANALYZE berisi informasi eksekusi sebenarnya seperti operator yang dieksekusi dan durasi eksekusi akurat dari setiap operator. Berdasarkan durasi eksekusi setiap operator, Anda dapat melakukan optimasi pernyataan SQL.

Catatan

Di Hologres V1.3.4x dan versi selanjutnya, hasil yang dikembalikan oleh pernyataan EXPLAIN dan EXPLAIN ANALYZE dioptimalkan. Jika Anda ingin menanyakan rencana eksekusi dengan keterbacaan lebih tinggi, kami sarankan Anda meningkatkan instance Hologres Anda ke V1.3.4x atau lebih baru.

EXPLAIN

  • Sintaksis

    Anda dapat menjalankan pernyataan EXPLAIN berikut untuk menanyakan rencana eksekusi yang diperkirakan oleh QO:

    EXPLAIN <sql>;
  • Contoh

    Dalam contoh ini, digunakan pernyataan kueri TPC-H.

    Catatan

    Nilai yang dikembalikan dalam contoh ini tidak dapat digunakan sebagai hasil yang dipublikasikan oleh TPC-H.

    EXPLAIN SELECT
            l_returnflag,
            l_linestatus,
            sum(l_quantity) AS sum_qty,
            sum(l_extendedprice) AS sum_base_price,
            sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
            sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
            avg(l_quantity) AS avg_qty,
            avg(l_extendedprice) AS avg_price,
            avg(l_discount) AS avg_disc,
            count(*) AS count_order
    FROM
            lineitem
    WHERE
            l_shipdate <= date '1998-12-01' - interval '120' day
    GROUP BY
            l_returnflag,
            l_linestatus
    ORDER BY
            l_returnflag,
            l_linestatus;
  • Hasil yang Dikembalikan

    QUERY PLAN
    Sort  (cost=0.00..7795.30 rows=3 width=80)
      Sort Key: l_returnflag, l_linestatus
      ->  Gather  (cost=0.00..7795.27 rows=3 width=80)
            ->  Project  (cost=0.00..7795.27 rows=3 width=80)
                  ->  Project  (cost=0.00..7794.27 rows=3 width=104)
                        ->  Final HashAggregate  (cost=0.00..7793.27 rows=3 width=76)
                              Group Key: l_returnflag, l_linestatus
                              ->  Redistribution  (cost=0.00..7792.95 rows=1881 width=76)
                                    Hash Key: l_returnflag, l_linestatus
                                    ->  Partial HashAggregate  (cost=0.00..7792.89 rows=1881 width=76)
                                          Group Key: l_returnflag, l_linestatus
                                          ->  Local Gather  (cost=0.00..7791.81 rows=44412 width=76)
                                                ->  Decode  (cost=0.00..7791.80 rows=44412 width=76)
                                                      ->  Partial HashAggregate  (cost=0.00..7791.70 rows=44412 width=76)
                                                            Group Key: l_returnflag, l_linestatus
                                                            ->  Project  (cost=0.00..3550.73 rows=584421302 width=33)
                                                                  ->  Project  (cost=0.00..2585.43 rows=584421302 width=33)
                                                                        ->  Index Scan using Clustering_index on lineitem  (cost=0.00..261.36 rows=584421302 width=25)
                                                                              Segment Filter: (l_shipdate <= '1998-08-03 00:00:00+08'::timestamp with time zone)
                                                                              Cluster Filter: (l_shipdate <= '1998-08-03 00:00:00+08'::timestamp with time zone)
    
  • Deskripsi Hasil

    Anda perlu melihat rencana eksekusi dari bawah ke atas. Setiap panah (->) menunjukkan sebuah node. Setiap node berisi informasi seperti operator dan jumlah baris yang diperkirakan akan dikembalikan. Tabel berikut menjelaskan parameter dari sebuah operator.

    Parameter

    Deskripsi

    cost

    Durasi eksekusi diperkirakan dari sebuah operator. Nilai cost dari node induk mencakup nilai cost dari node anaknya. Parameter cost berisi biaya mulai diperkirakan dan biaya total diperkirakan yang dipisahkan oleh dua titik (..).

    • Biaya mulai diperkirakan: biaya sebelum fase output dimulai.

    • Biaya total diperkirakan: biaya total diperkirakan dari eksekusi operator.

    Dalam hasil yang dikembalikan sebelumnya, biaya mulai diperkirakan dari node Final HashAggregate adalah 0.00, dan biaya total diperkirakan dari node tersebut adalah 7793.27.

    rows

    Jumlah baris yang dikembalikan oleh sebuah operator, yang diperkirakan berdasarkan statistik tabel.

    Nilai parameter rows untuk node Index Scan adalah 1000 secara default.

    Catatan

    Dalam banyak kasus, jika rows=1000 dikembalikan, statistik tabel tidak valid, dan tidak ada estimasi yang dilakukan berdasarkan statistik tabel. Anda dapat menjalankan pernyataan analyze <tablename> untuk memperbarui statistik tabel.

    width

    Panjang rata-rata kolom yang diperkirakan dikembalikan oleh sebuah operator. Nilai besar menunjukkan panjang kolom yang panjang. Unit: byte.

EXPLAIN ANALYZE

  • Sintaksis

    Anda dapat menjalankan pernyataan EXPLAIN ANALYZE berikut untuk menanyakan rencana eksekusi sebenarnya dari pernyataan SQL dan durasi eksekusi setiap operator. Ini membantu Anda mendiagnosis masalah performa SQL.

    EXPLAIN ANALYZE <sql>;
  • Contoh

    Dalam contoh ini, digunakan pernyataan kueri TPC-H.

    EXPLAIN ANALYZE SELECT
            l_returnflag,
            l_linestatus,
            sum(l_quantity) AS sum_qty,
            sum(l_extendedprice) AS sum_base_price,
            sum(l_extendedprice * (1 - l_discount)) AS sum_disc_price,
            sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge,
            avg(l_quantity) AS avg_qty,
            avg(l_extendedprice) AS avg_price,
            avg(l_discount) AS avg_disc,
            count(*) AS count_order
    FROM
            lineitem
    WHERE
            l_shipdate <= date '1998-12-01' - interval '120' day
    GROUP BY
            l_returnflag,
            l_linestatus
    ORDER BY
            l_returnflag,
            l_linestatus;
  • Hasil yang Dikembalikan

    QUERY PLAN
    Sort  (cost=0.00..7795.30 rows=3 width=80)
      Sort Key: l_returnflag, l_linestatus
    [id=21 dop=1 time=2427/2427/2427ms rows=4(4/4/4) mem=3/3/3KB open=2427/2427/2427ms get_next=0/0/0ms]
      ->  Gather  (cost=0.00..7795.27 rows=3 width=80)
          [20:1 id=100003 dop=1 time=2426/2426/2426ms rows=4(4/4/4) mem=1/1/1KB open=0/0/0ms get_next=2426/2426/2426ms]
            ->  Project  (cost=0.00..7795.27 rows=3 width=80)
                [id=19 dop=20 time=2427/2426/2425ms rows=4(1/0/0) mem=87/87/87KB open=2427/2425/2425ms get_next=1/0/0ms]
                  ->  Project  (cost=0.00..7794.27 rows=0 width=104)
                        ->  Final HashAggregate  (cost=0.00..7793.27 rows=3 width=76)
                              Group Key: l_returnflag, l_linestatus
                            [id=16 dop=20 time=2427/2425/2424ms rows=4(1/0/0) mem=574/570/569KB open=2427/2425/2424ms get_next=1/0/0ms]
                              ->  Redistribution  (cost=0.00..7792.95 rows=1881 width=76)
                                    Hash Key: l_returnflag, l_linestatus
                                  [20:20 id=100002 dop=20 time=2427/2424/2423ms rows=80(20/4/0) mem=3528/1172/584B open=1/0/0ms get_next=2426/2424/2423ms]
                                    ->  Partial Hashaggregate  (cost=0.00..7792.89 rows=1881 width=76)
                                          Group Key: l_returnflag, l_linestatus
                                        [id=12 dop=20 time=2428/2357/2256ms rows=80(4/4/4) mem=574/574/574KB open=2428/2357/2256ms get_next=1/0/0ms]
                                          ->  Local Gather  (cost=0.00..7791.81 rows=44412 width=76)
                                              [id=11 dop=20 time=2427/2356/2255ms rows=936(52/46/44) mem=7/6/6KB open=0/0/0ms get_next=2427/2356/2255ms pull_dop=9/9/9]
                                                ->  Decode  (cost=0.00..7791.80 rows=44412 width=76)
                                                    [id=8 dop=234 time=2435/1484/5ms rows=936(4/4/4) mem=0/0/0B open=2435/1484/5ms get_next=4/0/0ms]
                                                      ->  Partial Hashaggregate  (cost=0.00..7791.70 rows=44412 width=76)
                                                            Group Key: l_returnflag, l_linestatus
                                                          [id=5 dop=234 time=2435/1484/3ms rows=936(4/4/4) mem=313/312/168KB open=2435/1484/3ms get_next=0/0/0ms]
                                                            ->  Project  (cost=0.00..3550.73 rows=584421302 width=33)
                                                                [id=4 dop=234 time=2145/1281/2ms rows=585075720(4222846/2500323/3500) mem=142/141/69KB open=10/1/0ms get_next=2145/1280/2ms]
                                                                  ->  Project  (cost=0.00..2585.43 rows=584421302 width=33)
                                                                      [id=3 dop=234 time=582/322/2ms rows=585075720(4222846/2500323/3500) mem=142/142/69KB open=10/1/0ms get_next=582/320/2ms]
                                                                        ->  Index Scan using Clustering_index on lineitem  (cost=0.00..261.36 rows=584421302 width=25)
                                                                              Segment Filter: (l_shipdate <= '1998-08-03 00:00:00+08'::timestamp with time zone)
                                                                              Cluster Filter: (l_shipdate <= '1998-08-03 00:00:00+08'::timestamp with time zone)
                                                                            [id=2 dop=234 time=259/125/1ms rows=585075720(4222846/2500323/3500) mem=1418/886/81KB open=10/1/0ms get_next=253/124/0ms]
    
    ADVICE: 
    [node id : 1000xxx] distribution key miss match!  table lineitem defined distribution keys : l_orderkey; request distribution columns : l_returnflag, l_linestatus; 
    shuffle data skew in different shards!  max rows is 20, min rows is 0
    
    Query id:[300200511xxxx]
    ======================cost======================
    Total cost:[2505] ms
    Optimizer cost:[47] ms
    Init gangs cost:[4] ms
    Build gang desc table cost:[2] ms
    Start query cost:[18] ms
    - Wait schema cost:[0] ms
    - Lock query cost:[0] ms
    - Create dataset reader cost:[0] ms
    - Create split reader cost:[0] ms
    Get the first block cost:[2434] ms
    Get result cost:[2434] ms
    ====================resource====================
    Memory: 921(244/230/217) MB,  straggler worker id: 72969760xxx
    CPU time: 149772(38159/37443/36736) ms, straggler worker id: 72969760xxx
    Physical read bytes: 3345(839/836/834) MB, straggler worker id: 72969760xxx
    Read bytes: 41787(10451/10446/10444) MB, straggler worker id: 72969760xxx
    DAG instance count: 41(11/10/10), straggler worker id: 72969760xxx
    Fragment instance count: 275(70/68/67), straggler worker id: 72969760xxx
  • Deskripsi Hasil

    Output dari pernyataan EXPLAIN ANALYZE memberikan jalur eksekusi sebenarnya dari pernyataan SQL. Output ditampilkan dalam struktur pohon dan berisi informasi eksekusi terperinci dari setiap operator pada setiap fase. Output dari pernyataan EXPLAIN ANALYZE mencakup bagian-bagian berikut: rencana kueri, saran, biaya, dan sumber daya.

Rencana Kueri

Bagian rencana kueri memberikan informasi eksekusi terperinci dari setiap operator. Mirip dengan pernyataan EXPLAIN, Anda juga perlu melihat rencana kueri dari bawah ke atas. Setiap panah (->) menunjukkan sebuah node.

Contoh

Deskripsi

(cost=0.00..2585.43 rows=584421302 width=33)

Parameter-parameter ini menunjukkan nilai-nilai yang diperkirakan oleh QO dan memiliki arti yang sama seperti yang ada dalam output pernyataan Explain.

  • cost: durasi eksekusi yang diperkirakan dari sebuah operator.

  • rows: jumlah baris yang diperkirakan dikembalikan oleh sebuah operator. Jika nilai parameter rows jauh berbeda dari jumlah baris yang sebenarnya dikembalikan, statistik tabel mungkin belum diperbarui. Dalam hal ini, kami sarankan Anda menjalankan pernyataan analyze <tablename> untuk memperbarui statistik tabel.

  • width: panjang rata-rata kolom yang diperkirakan dikembalikan oleh sebuah operator. Nilai besar menunjukkan panjang kolom yang panjang. Unit: byte.

[20:20 id=100002 dop=20 time=2427/2424/2423ms rows=80(20/4/0) mem=3528/1172/584B open=1/0/0ms get_next=2426/2424/2423ms]

Parameter-parameter ini menunjukkan nilai-nilai sebenarnya untuk eksekusi setiap operator.

  • 20:20: rasio derajat paralelisme (DOP) input DAG terhadap DOP output DAG, yang menunjukkan pemetaan paralelisme antara input dan output operator. Dalam kebanyakan kasus, parameter ini digunakan bersama dengan output dari sebuah operator. Contohnya:

    • 21:1 dengan operator gather menunjukkan bahwa 21 input digabungkan untuk memberikan 1 output.

    • 21:21 dengan operator Redistribution menunjukkan bahwa operasi shuffling dilakukan dengan paralelisme 21.

    • 1:21 dengan operator broadcast menunjukkan bahwa paralelisme input adalah 1 dan paralelisme output adalah 21.

  • id: ID unik dari sebuah operator.

    Dalam contoh ini, id=100002 dikembalikan.

  • dop: DOP sebenarnya dari sebuah operator selama runtime. Nilai dari parameter ini sama dengan jumlah shard untuk instance tersebut. Dalam contoh ini, dop=20 dikembalikan. Namun, parameter dop untuk node Local Gather menunjukkan jumlah file yang discan.

  • time: durasi eksekusi sebenarnya dalam fase open dan get_next. Unit: milidetik. Di Hologres, setiap operator memiliki API open dan API get_next. Nilai dari parameter ini dalam format durasi maksimum/rata-rata/minimum ms. Contoh: time=2427/2424/2423ms.

    • open: Operasi API ini dipanggil untuk menginisialisasi operator. Untuk beberapa operator, API ini bergantung pada data dari operator hilir. Misalnya, untuk operator hash, API open membuat tabel hash dan menarik data dari semua operator hilir. Nilai dari parameter ini menunjukkan durasi dalam fase open dan dalam format durasi maksimum/rata-rata/minimum ms. Contoh: open=1/0/0ms.

    • get_next: Operasi API ini dipanggil untuk mendapatkan batch rekaman dari operator hilir untuk komputasi. Implementasi operasi API ini berbeda berdasarkan jenis operator. Operasi API ini dipanggil berkali-kali hingga data dari semua operator hilir ditarik. Nilai dari parameter ini menunjukkan durasi dalam fase next dan dalam format durasi maksimum/rata-rata/minimum ms. Contoh: get_next=2426/2424/2423ms.

  • row: jumlah baris yang dikembalikan oleh sebuah operator. Anda dapat membandingkan nilai maksimum, minimum, dan rata-rata. Jika nilainya tidak hampir sama, data didistribusikan secara tidak merata.

    Nilai dari parameter ini dalam format total baris(jumlah baris maksimum/jumlah baris rata-rata/jumlah baris minimum). Contoh: rows=80(20/4/0).

  • mem: konsumsi sumber daya memori maksimum, rata-rata, dan minimum yang dikonsumsi oleh sebuah operator selama runtime.

    Nilai dari parameter ini dalam format konsumsi sumber daya memori maksimum/konsumsi sumber daya memori rata-rata/konsumsi sumber daya memori minimum. Contoh: mem=3528/1172/584B.

Pernyataan SQL mungkin melibatkan beberapa operator. Untuk informasi lebih lanjut tentang operator, lihat Operator.

Catatan

Ketika menggunakan parameter time, row, dan mem, perhatikan hal-hal berikut:

  • Nilai dari parameter time diakumulasikan. Oleh karena itu, jika Anda ingin mendapatkan durasi eksekusi dari sebuah operator, Anda perlu mengurangi nilai waktu untuk operator hilir dari nilai waktu untuk operator ini.

  • Nilai dari parameter row dan mem tidak diakumulasikan.

Saran

Bagian saran memberikan saran optimasi yang dihasilkan secara otomatis oleh sistem berdasarkan output dari pernyataan EXPLAIN ANALYZE. Saran-saran berikut tersedia:

  • Table xxx misses bitmap index: Kami sarankan Anda mengonfigurasi kunci distribusi, kunci pengelompokan, atau indeks bitmap untuk tabel tersebut.

  • Table xxx Miss Stats! please run 'analyze xxx';: Tidak ada statistik yang tersedia untuk tabel tersebut.

  • shuffle data xxx in different shards! max rows is 20, min rows is 0: Kemungkinan adanya skew data.

Catatan

Bagian saran hanya memberikan saran untuk pernyataan SQL dan mungkin tidak berlaku. Anda perlu melakukan tindakan optimasi berdasarkan skenario bisnis.

Biaya

Bagian biaya memberikan durasi eksekusi total dari pernyataan SQL dan durasi eksekusi setiap fase. Anda dapat menemukan hambatan performa berdasarkan durasi eksekusi setiap fase.

Total cost: durasi eksekusi total dari pernyataan SQL dalam satuan milidetik. Ini terdiri dari bagian-bagian berikut:

  • Optimizer cost: durasi yang dibutuhkan oleh QO untuk menghasilkan rencana eksekusi. Unit: milidetik.

  • Build gang desc table cost: durasi yang dibutuhkan untuk mengonversi rencana eksekusi yang dihasilkan oleh QO menjadi data yang dalam format yang diminta oleh QE. Unit: milidetik.

  • Init gangs cost: durasi yang dibutuhkan untuk memproses rencana eksekusi yang dihasilkan oleh QO dan mengirim permintaan kueri ke QE untuk memicu fase start query. Unit: milidetik.

  • Start query cost: durasi fase inisialisasi, yang dimulai ketika langkah Init gangs selesai dan berakhir ketika operasi kueri dimulai. Fase inisialisasi melibatkan operasi seperti akuisisi kunci dan penyelarasan versi skema dan terdiri dari bagian-bagian berikut:

    • Wait schema cost: durasi yang dibutuhkan untuk menyelaraskan versi storage engine (SE) dan versi node FE dengan versi skema. Jika skema tabel berubah, baik versi node FE maupun versi SE perlu diperbarui. Jika versi node FE dan versi SE tidak sesuai dengan versi skema, latency skema mungkin terjadi. Jika banyak pernyataan DDL dieksekusi pada tabel induk partisi, kecepatan pemrosesan SE lambat dan latency tinggi. Akibatnya, penulisan dan pembacaan data menjadi lambat. Dalam hal ini, Anda dapat mengoptimalkan frekuensi eksekusi pernyataan DDL.

    • Lock query cost: durasi yang dibutuhkan untuk mendapatkan kunci oleh kueri. Jika durasinya lama, kueri sedang menunggu kunci.

    • Create dataset reader cost: durasi yang dibutuhkan untuk membuat pembaca data indeks. Jika durasinya lama, cache mungkin tidak tercapai.

    • Create split reader cost: durasi yang dibutuhkan untuk membuka file. Jika durasinya lama, metadata file tidak mencapai cache. Jika ini terjadi, overhead I/O tinggi.

  • Get result cost: durasi dari waktu ketika fase start query berakhir hingga semua hasil dikembalikan. Get result cost mencakup Get the first block cost. Unit: milidetik.

    • Get the first block cost: durasi dari waktu ketika fase start query berakhir hingga batch rekaman pertama dikembalikan. Dalam beberapa skenario, Get the first block cost sangat dekat atau konsisten dengan Get result cost. Misalnya, jika operator hash aggregate digunakan dalam langkah pertama rencana kueri, data penuh dari operator hilir diperlukan untuk membuat tabel hash untuk operasi agregasi. Untuk kueri umum dengan kondisi filter, data dihitung dan dikembalikan secara real-time. Dalam skenario ini, Get the first block cost sangat berbeda dari Get result cost. Perbedaan tersebut bergantung pada jumlah data.

Sumber Daya

Bagian sumber daya memberikan jumlah sumber daya yang dikonsumsi selama eksekusi kueri, dalam format total jumlah sumber daya yang dikonsumsi(jumlah sumber daya maksimum yang dikonsumsi/jumlah sumber daya rata-rata yang dikonsumsi/jumlah sumber daya minimum yang dikonsumsi).

Hologres adalah mesin terdistribusi. Instance Hologres memiliki beberapa node pekerja. Hasil komputasi dari node pekerja digabungkan dan hasil penggabungan dikembalikan ke klien. Oleh karena itu, informasi konsumsi sumber daya ditampilkan dalam format total(maks pekerja/rata-rata pekerja/min pekerja).

  • total: jumlah total sumber daya yang dikonsumsi oleh pernyataan kueri SQL.

  • max: jumlah maksimum sumber daya yang dikonsumsi oleh node pekerja.

  • avg: jumlah rata-rata sumber daya yang dikonsumsi per node pekerja, yang dihitung menggunakan rumus berikut: Total jumlah sumber daya yang dikonsumsi/Jumlah node pekerja.

  • min: jumlah minimum sumber daya yang dikonsumsi oleh node pekerja.

Tabel berikut menjelaskan metrik dalam bagian sumber daya.

Metrik

Deskripsi

Memory

Informasi konsumsi memori dari pernyataan kueri SQL, termasuk total sumber daya memori yang dikonsumsi oleh semua node pekerja, sumber daya memori maksimum yang dikonsumsi oleh node pekerja, sumber daya memori rata-rata yang dikonsumsi per node pekerja, dan sumber daya memori minimum yang dikonsumsi oleh node pekerja.

CPU time

Total waktu CPU yang dikonsumsi oleh pernyataan kueri SQL, yang tidak akurat. Unit: milidetik.

Metrik ini menunjukkan total waktu CPU yang dikonsumsi oleh semua tugas komputasi, yang merupakan jumlah waktu untuk beberapa inti CPU. Nilai dari metrik ini secara umum mencerminkan kompleksitas tugas komputasi.

Physical read bytes

Jumlah data yang dibaca dari disk. Unit: byte. Jika kueri tidak mencapai cache, data dibaca dari disk.

Read bytes

Jumlah total byte yang dibaca oleh pernyataan kueri SQL. Jumlah total byte mencakup byte data baca fisik dan byte data yang dibaca dari cache. Nilai dari metrik ini mencerminkan jumlah data yang diperoleh dari output pernyataan kueri SQL.

Affected rows

Jumlah baris yang terpengaruh oleh pernyataan bahasa manipulasi data (DML). Metrik ini hanya ditampilkan ketika pernyataan DML dieksekusi.

Dag instance count

Jumlah instance DAG dalam rencana eksekusi. Nilai besar menunjukkan kueri yang kompleks dengan DOP tinggi.

Fragment instance count

Jumlah instance fragmen dalam rencana eksekusi. Nilai besar menunjukkan jumlah rencana yang besar dan jumlah file yang besar.

straggler_worker_id

ID node pekerja yang mengonsumsi sumber daya paling banyak.

Operator

SCAN

  • seq scan

    Seq Scan digunakan untuk membaca data dari tabel secara berurutan. Pemindaian tabel penuh dilakukan. Kata kunci on setelah Seq Scan diikuti oleh nama tabel yang dipindai.

    Contoh: Jalankan pernyataan berikut untuk memeriksa rencana eksekusi kueri pada tabel internal umum. Seq Scan dikembalikan dalam rencana eksekusi pernyataan kueri.

    EXPLAIN SELECT * FROM public.holo_lineitem_100g;

    Gambar berikut menunjukkan hasil yang dikembalikan.

    image

    • Kueri data dari tabel partisi

      Jika Anda mengkueri data dari tabel partisi, kata kunci Seq Scan on Partitioned Table ditampilkan dalam rencana eksekusi kueri. Anda dapat memperoleh jumlah partisi yang dipindai dalam pernyataan kueri dari kata kunci Partitions selected.

      Contoh: Jalankan pernyataan berikut untuk memeriksa rencana eksekusi kueri pada tabel induk partisi dengan satu partisi yang dipindai.

      EXPLAIN SELECT * FROM public.hologres_parent;

      Gambar berikut menunjukkan hasil yang dikembalikan.

      image

    • Kueri data menggunakan tabel asing

      Jika Anda mengkueri data menggunakan tabel asing, kata kunci Foreign Table Type ditampilkan dalam rencana eksekusi kueri untuk menentukan sumber tabel asing. Nilai dari Foreign Table Type bisa MaxCompute, OSS, atau Hologres.

      Contoh: Jalankan pernyataan berikut untuk memeriksa rencana eksekusi kueri menggunakan tabel asing MaxCompute.

      EXPLAIN SELECT * FROM public.odps_lineitem_100;

      Gambar berikut menunjukkan hasil yang dikembalikan.

      image

  • Index Scan dan Index Seek

    Anda dapat menggunakan indeks untuk mempercepat kueri pada tabel. Hologres menggunakan indeks yang berbeda di lapisan bawah berdasarkan mode penyimpanan tabel. Berdasarkan mode penyimpanan kolom dan mode penyimpanan baris, indeks diklasifikasikan menjadi clustering_index dan Index Seek (juga disebut pk_index).

    • Clustering_index: jenis indeks yang berlaku untuk tabel berorientasi kolom, seperti kunci segmen atau kunci pengelompokan. Jika kueri pada tabel berorientasi kolom mengenai indeks, jenis indeks ini digunakan. Seq Scan Using Clustering_index biasanya digunakan bersama dengan Filter. Filter adalah node anak dan mencantumkan indeks yang dikenai. Filter bisa berupa filter kluster, filter segmen, atau filter bitmap. Untuk informasi lebih lanjut, lihat Penyimpanan berorientasi kolom.

      • Contoh 1: Kueri mengenai indeks.

        BEGIN;
        CREATE TABLE column_test (
         "id" bigint not null ,
         "name" text not null ,
         "age" bigint not null 
        );
        CALL set_table_property('column_test', 'orientation', 'column');
        CALL set_table_property('column_test', 'distribution_key', 'id');
        CALL set_table_property('column_test', 'clustering_key', 'id');
        COMMIT;
        
        INSERT INTO column_test VALUES(1,'tom',10),(2,'tony',11),(3,'tony',12);
        
        EXPLAIN SELECT * FROM column_test WHERE id>2;

        Gambar berikut menunjukkan hasil yang dikembalikan.

        image

      • Contoh 2: Kueri tidak mengenai indeks apa pun, dan indeks jenis clustering_index tidak digunakan.

        EXPLAIN SELECT * FROM column_test WHERE age>10;

        Gambar berikut menunjukkan hasil yang dikembalikan.

        image

    • Index Seek (juga disebut pk_index): jenis indeks yang berlaku untuk tabel berorientasi baris, seperti kunci utama. Dalam kebanyakan kasus, rencana tetap digunakan untuk kueri titik pada tabel berorientasi baris yang dikonfigurasi dengan kunci utama. Jika kueri pada tabel berorientasi baris yang dikonfigurasi dengan kunci utama tidak menggunakan rencana tetap, indeks jenis Index Seek digunakan. Untuk informasi lebih lanjut, lihat Penyimpanan berorientasi baris.

      Contoh: Kueri data dari tabel berorientasi baris.

      BEGIN;
      CREATE TABLE row_test_1 (
          id bigint not null,
          name text not null,
          class text ,
      PRIMARY KEY (id)
      );
      CALL set_table_property('row_test_1', 'orientation', 'row');
      CALL set_table_property('row_test_1', 'clustering_key', 'name');
      COMMIT;
      INSERT INTO row_test_1 VALUES ('1','qqq','3'),('2','aaa','4'),('3','zzz','5');
      
      BEGIN;
      CREATE TABLE row_test_2 (
          id bigint not null,
          name text not null,
          class text ,
      PRIMARY KEY (id)
      );
      CALL set_table_property('row_test_2', 'orientation', 'row');
      CALL set_table_property('row_test_2', 'clustering_key', 'name');
      COMMIT;
      INSERT INTO row_test_2 VALUES ('1','qqq','3'),('2','aaa','4'),('3','zzz','5');
      
      --pk_index
      EXPLAIN SELECT * FROM (SELECT id FROM row_test_1 WHERE id = 1) t1 JOIN row_test_2 t2 ON t1.id = t2.id;
      

      Gambar berikut menunjukkan hasil yang dikembalikan.

      image

Filter

Filter digunakan untuk memfilter data berdasarkan kondisi SQL. Dalam kebanyakan kasus, Filter adalah node anak dari Seq Scan dan dieksekusi bersama dengan node Seq Scan. Filter menentukan apakah data difilter dan apakah kondisi filter mengenai indeks. Bagian ini menjelaskan berbagai jenis Filter.

  • Filter

    Jika rencana eksekusi hanya berisi kata kunci Filter, kondisi filter tidak mengenai indeks apa pun. Dalam hal ini, Anda perlu memeriksa indeks tabel dan mengonfigurasi ulang indeks untuk mempercepat kueri SQL.

    Catatan

    Jika rencana eksekusi berisi One-Time Filter: false, output kosong.

    Contoh:

    BEGIN;
    CREATE TABLE clustering_index_test (
     "id" bigint not null ,
     "name" text not null ,
     "age" bigint not null 
    );
    CALL set_table_property('clustering_index_test', 'orientation', 'column');
    CALL set_table_property('clustering_index_test', 'distribution_key', 'id');
    CALL set_table_property('clustering_index_test', 'clustering_key', 'age');
    COMMIT;
    
    INSERT INTO clustering_index_test VALUES (1,'tom',10),(2,'tony',11),(3,'tony',12);
    
    EXPLAIN SELECT * FROM clustering_index_test WHERE id>2;

    Gambar berikut menunjukkan hasil yang dikembalikan.

    image

  • Segment Filter

    Segment Filter menunjukkan bahwa kueri mengenai kunci segmen. Segment Filter digunakan bersama dengan index_scan. Untuk informasi lebih lanjut, lihat Kolom waktu acara (kunci segmen).

  • Cluster Filter

    Cluster Filter menunjukkan bahwa kueri mengenai kunci pengelompokan. Untuk informasi lebih lanjut, lihat Kunci pengelompokan.

  • Bitmap Filter

    Bitmap Filter menunjukkan bahwa kueri mengenai indeks bitmap. Untuk informasi lebih lanjut, lihat Indeks bitmap.

  • Join Filter

    Join Filter menunjukkan bahwa data perlu difilter setelah operasi join.

Decode

Decode digunakan untuk mengkodekan atau mendekode data untuk mempercepat komputasi data teks.

Local Gather dan Gather

Di Hologres, data disimpan sebagai file dalam shard. Local Gather digunakan untuk menggabungkan data dalam beberapa file menjadi satu shard. Gather digunakan untuk merangkum data dalam beberapa shard dan mengembalikan hasilnya.

Contoh:

EXPLAIN SELECT * FROM public.lineitem;

Gambar berikut menunjukkan rencana eksekusi yang dikembalikan. Data dipindai, digabungkan menjadi satu shard menggunakan operator Local Gather, dan kemudian dirangkum menggunakan operator Gather.

image

Redistribution

Redistribution digunakan untuk meng-hash data atau mendistribusikan data secara acak ke satu atau lebih shard.

  • Operator redistribution sering digunakan dalam skenario berikut:

    • JOIN, COUNT DISTINCT (metode JOIN), dan klausa GROUP BY: Jika kunci distribusi tidak dikonfigurasikan untuk tabel atau pengaturan kunci distribusi tidak valid, data di-shuffle di antara beberapa shard selama kueri. Jika redistribusi terjadi saat Anda menggabungkan beberapa tabel, fitur local join tidak digunakan. Akibatnya, performa kueri buruk.

    • Kunci yang terlibat dalam ekspresi, seperti kunci join atau kunci group by: Jika Anda menggunakan fungsi CAST untuk mengonversi tipe data, fitur local join tidak digunakan. Dalam hal ini, redistribusi terjadi.

  • Contoh:

    • Contoh 1: Gabungkan dua tabel. Kunci distribusi tidak valid dan redistribusi terjadi.

      BEGIN;
      CREATE TABLE tbl1(
      a int not null,
      b text not null
      );
      CALL set_table_property('tbl1', 'distribution_key', 'a');
      CREATE TABLE tbl2(
      c int not null,
      d text not null
      );
      CALL set_table_property('tbl2', 'distribution_key', 'd');
      COMMIT;
      
      EXPLAIN SELECT * FROM tbl1  JOIN tbl2 ON tbl1.a=tbl2.c;

      Gambar berikut menunjukkan rencana eksekusi yang dikembalikan. Operator redistribution terkandung, menunjukkan bahwa kunci distribusi tidak valid. Dalam pernyataan SQL, tbl1.a=tbl2.c dikonfigurasikan untuk menggabungkan tabel. Namun, kunci distribusi tabel tbl1 adalah a dan kunci distribusi tabel tbl2 adalah d. Akibatnya, data di-shuffle selama operasi join.

      image

      Saran optimasi: Jika pernyataan SQL berisi operator redistribution, kami sarankan Anda memeriksa pengaturan kunci distribusi. Untuk informasi lebih lanjut tentang skenario redistribusi dan pengaturan kunci distribusi, lihat Kunci distribusi.

    • Contoh 2: Kunci join terlibat dalam ekspresi. Tipe data diubah. Akibatnya, fitur local join tidak digunakan, dan redistribusi terjadi.image

      Saran optimasi: Jangan gunakan ekspresi.

Join

Definisi operator Join dalam rencana eksekusi sama dengan definisi dalam database standar. Operasi join dapat diklasifikasikan menjadi hash join, nested loop join, dan merge join.

  • Hash Join

    Hash join adalah cara untuk menggabungkan dua tabel atau lebih. Selama hash join, tabel hash dibuat di memori berdasarkan tabel yang akan digabungkan, yang biasanya merupakan tabel kecil. Data dari kolom yang akan digabungkan di-hash dan kemudian dimasukkan ke dalam tabel hash. Data dari tabel lain yang akan digabungkan dibaca baris demi baris, di-hash, dan dicocokkan dengan data dalam tabel hash. Data yang cocok dikembalikan. Tabel berikut menjelaskan kategori-kategori hash join.

    Kategori

    Deskripsi

    Hash Left Join

    Ketika beberapa tabel digabungkan, semua baris dalam tabel kiri yang memenuhi kondisi join dikembalikan dan kemudian dicocokkan dengan tabel kanan. Jika tidak ada data yang cocok, nilai null dikembalikan.

    Hash Right Join

    Ketika beberapa tabel digabungkan, semua baris dalam tabel kanan dan baris dalam tabel kiri yang memenuhi kondisi join dikembalikan. Jika baris dalam tabel kanan tidak cocok dengan data dalam tabel kiri, nilai null dikembalikan untuk tabel kiri.

    Hash Inner Join

    Ketika beberapa tabel digabungkan, hanya baris yang memenuhi kondisi join yang dikembalikan.

    Hash Full Join

    Ketika beberapa tabel digabungkan, semua baris dalam tabel kiri dan tabel kanan dikembalikan. Jika data dalam satu tabel tidak cocok dengan data dalam tabel lain, nilai null dikembalikan untuk tabel yang datanya tidak dapat dicocokkan.

    Hash Anti Join

    Hanya data yang tidak cocok yang dikembalikan. Jenis join ini sebagian besar digunakan untuk kueri dengan klausa NOT EXISTS.

    Hash Semi Join

    Baris dikembalikan jika catatan data cocok. Baris yang dikembalikan tidak berisi data duplikat. Jenis join ini biasanya digunakan untuk kueri dengan klausa EXISTS.

    Saat Anda melihat rencana eksekusi operasi hash join, Anda juga perlu fokus pada node anak.

    • hash cond: kondisi join. Contoh: hash cond(tmp.a=tmp1.b).

    • hash key: kunci yang digunakan untuk perhitungan hash di beberapa shard. Dalam kebanyakan kasus, kunci tersebut menunjukkan kunci GROUP BY.

    Selama hash join, Anda perlu memeriksa apakah tabel dengan jumlah data kecil digunakan untuk membuat tabel hash. Anda dapat menggunakan salah satu metode berikut untuk memeriksa apakah tabel kecil digunakan untuk membuat tabel hash:

    • Dalam rencana eksekusi, tabel dengan kata kunci hash adalah tabel yang digunakan untuk membuat tabel hash.

    • Dalam rencana eksekusi, tabel bawah adalah tabel yang digunakan untuk membuat tabel hash.

    Saran optimasi:

    • Perbarui statistik

      Ide inti adalah menggunakan tabel kecil untuk membuat tabel hash. Jika tabel besar digunakan untuk membuat tabel hash di memori, lebih banyak sumber daya dikonsumsi. Dalam kebanyakan kasus, masalah ini terjadi karena statistik tabel tidak diperbarui dan QO menggunakan tabel besar untuk membuat tabel hash.

      Dalam contoh ini, tabel hash_join_test_2 (juga bernama tbl2) berisi 1.000.000 baris data, dan tabel hash_join_test_1 (juga bernama tbl1) berisi 10.000 baris data. Namun, statistik tabel tidak diperbarui dan menunjukkan bahwa tabel tbl2 berisi 1.000 baris data. Akibatnya, tabel tbl2 dianggap sebagai tabel kecil dan digunakan untuk membuat tabel hash. Efisiensi kueri rendah.

      BEGIN ;
      CREATE TABLE public.hash_join_test_1 (
          a integer not null,
          b text not null
      );
      CALL set_table_property('public.hash_join_test_1', 'distribution_key', 'a');
      CREATE TABLE public.hash_join_test_2 (
          c integer not null,
          d text not null
      );
      CALL set_table_property('public.hash_join_test_2', 'distribution_key', 'c');
      COMMIT ;
      
      INSERT INTO hash_join_test_1 SELECT i, i+1 FROM generate_series(1, 10000) AS s(i);
      INSERT INTO hash_join_test_2 SELECT i, i::text FROM generate_series(10, 1000000) AS s(i);
      
      EXPLAIN SELECT * FROM hash_join_test_1 tbl1  JOIN hash_join_test_2 tbl2 ON tbl1.a=tbl2.c;

      Gambar berikut menunjukkan rencana eksekusi. Tabel besar hash_join_test_2 digunakan untuk membuat tabel hash.image

      Jika statistik tabel tidak diperbarui, Anda dapat secara manual menjalankan pernyataan analyze <tablename> untuk memperbarui statistik. Contoh pernyataan:

      ANALYZE hash_join_test_1;
      ANALYZE hash_join_test_2;

      Gambar berikut menunjukkan rencana eksekusi setelah statistik tabel diperbarui. Tabel kecil hash_join_test_1 digunakan untuk membuat tabel hash, dan jumlah baris yang diperkirakan oleh QO benar.image

    • Atur urutan join

      Dalam kebanyakan kasus, Anda dapat menyelesaikan masalah join dengan memperbarui statistik tabel. Namun, berdasarkan mekanisme default, jika pernyataan SQL kompleks dan lima tabel atau lebih digabungkan, QO Hologres memilih rencana eksekusi optimal berdasarkan pernyataan SQL. Proses seleksi memakan waktu. Anda dapat menjalankan pernyataan berikut untuk mengonfigurasi parameter Grand Unified Configuration (GUC) dan mengontrol urutan join untuk mempercepat proses seleksi QO:

      SET optimizer_join_order = '<value>'; 

      Tabel berikut menjelaskan nilai-nilai valid dari parameter GUC.

      Nilai valid

      Deskripsi

      exhaustive (nilai default)

      Urutan join ditentukan menggunakan algoritma, dan rencana eksekusi optimal dihasilkan. Ini dapat meningkatkan overhead QO selama penggabungan multi-tabel.

      query

      Rencana eksekusi dihasilkan berdasarkan pernyataan SQL. QO tidak melakukan perubahan apa pun. Nilai ini berlaku dan membantu mengurangi overhead QO hanya jika tabel yang akan digabungkan tidak lebih dari ratusan juta baris data. Kami sarankan Anda tidak mengonfigurasi parameter GUC ini di tingkat database. Jika tidak, performa operasi penggabungan lainnya akan menurun.

      greedy

      Urutan join dihasilkan menggunakan algoritma greedy. Dalam mode ini, overhead QO moderat.

  • Nested loop join dan Materialize

    Dalam penggabungan nested loop dari beberapa tabel, data dibaca dari satu tabel menjadi tabel luar. Setiap catatan data dari tabel luar dilintasi ke dalam tabel dalam. Kemudian, tabel dalam dan luar digabungkan dalam loop bersarang. Proses ini setara dengan proses menghitung produk Kartesius. Dalam rencana eksekusi, tabel dalam pertama biasanya memiliki operator Materialize.

    Saran optimasi:

    • Prinsip nested loop adalah bahwa tabel dalam didorong oleh tabel luar. Setiap baris yang dikembalikan oleh tabel luar harus cocok dengan baris dalam tabel dalam. Oleh karena itu, set hasil yang dikembalikan tidak boleh terlalu besar. Jika tidak, banyak sumber daya akan dikonsumsi. Kami sarankan Anda menggunakan tabel yang mengembalikan hasil kecil sebagai tabel luar.

    • Gabungan non-ekuivalen biasanya menghasilkan nested loop join. Kami sarankan Anda mencegah gabungan non-ekuivalen dalam pernyataan SQL.

    • Kode berikut memberikan contoh nested loop join.

      BEGIN;
      CREATE TABLE public.nestedloop_test_1 (
          a integer not null,
          b integer not null
      );
      CALL set_table_property('public.nestedloop_test_1', 'distribution_key', 'a');
      CREATE TABLE public.nestedloop_test_2 (
          c integer not null,
          d text not null
      );
      CALL set_table_property('public.nestedloop_test_2', 'distribution_key', 'c');
      COMMIT;
      
      INSERT INTO nestedloop_test_1 SELECT i, i+1 FROM generate_series(1, 10000) AS s(i);
      INSERT INTO nestedloop_test_2 SELECT i, i::text FROM generate_series(10, 1000000) AS s(i);
      
      EXPLAIN SELECT * FROM nestedloop_test_1 tbl1,nestedloop_test_2 tbl2 WHERE tbl1.a>tbl2.c;

      Gambar berikut menunjukkan rencana eksekusi. Operator Materialize dan Nested Loop ditampilkan. Ini menunjukkan bahwa pernyataan SQL menggunakan nested loop join.image

  • Cross join

    Di Hologres V3.0 dan versi selanjutnya, cross join digunakan sebagai implementasi optimal dari nested loop join dalam skenario seperti gabungan non-ekuivalen yang melibatkan tabel kecil. Dalam nested loop join, baris data diambil dari loop luar, semua data dalam loop dalam dilintasi, dan kemudian status subkueri loop dalam diatur ulang. Dalam cross join, semua data dalam tabel kecil dimuat ke memori, dan data tersebut digabungkan dengan data yang dibaca dari tabel besar dalam mode streaming. Ini secara signifikan meningkatkan performa komputasi. Namun, cross join mengonsumsi lebih banyak sumber daya memori daripada nested loop join.

    Gambar berikut menunjukkan rencana eksekusi. Operator Cross Join ditampilkan. Ini menunjukkan bahwa pernyataan SQL menggunakan cross join.

    image

    Jika Anda ingin menonaktifkan cross join, jalankan pernyataan SQL berikut untuk menonaktifkannya.

    -- Nonaktifkan cross join pada tingkat sesi.
    SET hg_experimental_enable_cross_join_rewrite = off;
    
    -- Nonaktifkan cross join pada tingkat database. Konfigurasi ini berlaku untuk koneksi baru.
    ALTER database <database name> hg_experimental_enable_cross_join_rewrite = off;

Broadcast

Broadcast digunakan untuk mendistribusikan data ke setiap shard. Dalam kebanyakan kasus, broadcast join digunakan untuk menggabungkan tabel kecil dengan tabel besar. Ketika pernyataan SQL dikirim, QO membandingkan biaya redistribusi dan broadcast, lalu menghasilkan rencana eksekusi berdasarkan algoritma.

Saran optimasi:

  • Jika tabel yang ingin Anda kueri kecil dan instance berisi jumlah shard yang kecil, seperti 5 shard, broadcast direkomendasikan.

    Dalam contoh ini, dua tabel digabungkan. Jumlah data tabel broadcast_test_1 dan tabel broadcast_test_2 sangat berbeda.

    BEGIN;
    CREATE TABLE broadcast_test_1 (
        f1 int, 
        f2 int);
    CALL set_table_property('broadcast_test_1','distribution_key','f2');
    CREATE TABLE broadcast_test_2 (
        f1 int,
        f2 int);
    COMMIT;
    
    INSERT INTO broadcast_test_1 SELECT i AS f1, i AS f2 FROM generate_series(1, 30)i;
    INSERT INTO broadcast_test_2 SELECT i AS f1, i AS f2 FROM generate_series(1, 30000)i;
    
    ANALYZE broadcast_test_1;
    ANALYZE broadcast_test_2;
    
    EXPLAIN SELECT * FROM broadcast_test_1 t1, broadcast_test_2 t2 WHERE t1.f1=t2.f1;

    Gambar berikut menunjukkan hasil yang dikembalikan.

    image

  • Jika tabel yang akan digabungkan bukan tabel kecil tetapi operator broadcast digunakan, statistik tabel mungkin tidak diperbarui sesuai harapan. Misalnya, statistik menunjukkan bahwa tabel berisi 1.000 baris data, tetapi sebenarnya tabel berisi 1.000.000 baris data. Dalam hal ini, jalankan pernyataan analyze <tablename> untuk memperbarui statistik tabel.

Shard prune dan Shards selected

  • Shard prune

    Metode yang digunakan untuk mendapatkan shard. Nilai valid:

    • lazaily: Sistem memberi label pada shard berdasarkan ID shard dalam node dan menggunakan shard tertentu selama operasi komputasi berikutnya.

    • eagerly: Sistem memilih shard yang terkena dampak.

    QO secara otomatis memilih metode berdasarkan rencana eksekusi.

  • Shards selected

    Jumlah shard yang dipilih. Nilai 1 out of 20 menunjukkan bahwa satu shard dipilih dari 20 shard.

ExecuteExternalSQL

Sebagaimana dijelaskan dalam Arsitektur Hologres, mesin query Hologres diklasifikasikan menjadi Hologres Query Engine (HQE), PostgreSQL Query Engine (PQE), dan Seahawks Query Engine (SQE). PQE adalah mesin PostgreSQL asli. Beberapa operator dan fungsi yang tidak didukung oleh HQE dieksekusi oleh PQE. PQE kurang efisien dibandingkan HQE. Jika rencana eksekusi berisi operator ExecuteExternalSQL, PQE digunakan.

  • Contoh 1: Pernyataan SQL menggunakan PQE.

    CREATE TABLE pqe_test(a text);
    INSERT INTO pqe_test VALUES ('2023-01-28 16:25:19.082698+08');
    EXPLAIN SELECT a::timestamp FROM pqe_test;

    Dalam rencana eksekusi berikut, ExecuteExternalSQL ditampilkan, dan PQE digunakan untuk memproses operator ::timestamp.

    image

  • Contoh 2: Operator ::timestamp diubah menjadi to_timestamp. HQE digunakan.

    EXPLAIN SELECT to_timestamp(a,'YYYY-MM-DD HH24:MI:SS') FROM pqe_test;

    Dalam rencana eksekusi berikut, ExecuteExternalSQL tidak ditampilkan, dan PQE tidak digunakan.image

Saran optimasi: Gunakan rencana eksekusi untuk menemukan fungsi atau operator yang diproses oleh PQE, dan tulis ulang pernyataan SQL untuk memungkinkan HQE memproses fungsi atau operator tersebut. Ini membantu meningkatkan efisiensi kueri. Untuk informasi lebih lanjut tentang cara menulis ulang operator, lihat Optimalkan performa kueri.

Catatan

Dukungan untuk PQE terus dioptimalkan di setiap versi Hologres untuk mendorong lebih banyak fungsi PQE ke HQE. Beberapa fungsi dapat didukung secara otomatis oleh HQE setelah instance Hologres ditingkatkan. Untuk informasi lebih lanjut, lihat Catatan rilis fungsi.

Aggregate

Aggregate digunakan untuk mengumpulkan data. Aggregate bisa berupa fungsi agregat atau kombinasi dari beberapa fungsi agregat. Dalam pernyataan SQL, operator agregat diklasifikasikan menjadi jenis-jenis berikut:

  • GroupAggregate: Data telah diurutkan sebelumnya berdasarkan klausa GROUP BY.

  • HashAggregate: Data di-hash, didistribusikan ke shard yang berbeda berdasarkan nilai hash, lalu digabungkan menggunakan operator Gather. Jenis agregat ini paling sering digunakan.

    EXPLAIN SELECT l_orderkey,count(l_linenumber) FROM public.holo_lineitem_100g GROUP BY l_orderkey;
  • Multi-stage HashAggregate: Data disimpan sebagai file dalam shard. File memiliki level yang berbeda. Jika volume data besar, fase Aggregate juga dibagi menjadi beberapa fase. Jenis agregat ini mencakup operator-operator berikut:

    • Partial HashAggregate: Data di-aggregat pada level file atau level shard.

    • Final HashAggregate: Data dalam beberapa shard di-aggregat.

    Dalam contoh ini, pernyataan TPC-H Query 6 menggunakan multi-phase HashAggregate.

    EXPLAIN SELECT
            sum(l_extendedprice * l_discount) AS revenue
    FROM
            lineitem
    WHERE
            l_shipdate >= date '1996-01-01'
            AND l_shipdate < date '1996-01-01' + interval '1' year
            AND l_discount BETWEEN 0.02 - 0.01 AND 0.02 + 0.01
            AND l_quantity < 24;

    Gambar berikut menunjukkan hasil yang dikembalikan.image

    Saran optimasi: Dalam kebanyakan kasus, QO menentukan apakah akan menggunakan HashAggregate satu fase atau multi-fase berdasarkan volume data. Jika rencana eksekusi yang dikembalikan oleh Explain Analyze statement menunjukkan bahwa operator agregat memakan waktu, volume data besar. QO hanya mengaktifkan agregasi tingkat shard alih-alih agregasi tingkat file. Dalam hal ini, Anda dapat mengatur parameter GUC berikut ke on untuk melakukan multi-phase HashAggregate. Jika pernyataan SQL sudah menggunakan agregat multi-fase, tidak ada penyesuaian tambahan yang diperlukan.

    SET optimizer_force_multistage_agg = on;

Sort

Sort digunakan untuk mengurutkan data dalam urutan menaik (ASC) atau menurun (DESC), yang biasanya digunakan bersama dengan klausa ORDER BY.

Dalam contoh ini, data dalam kolom l_shipdate tabel TPC-H lineitem diurutkan.

EXPLAIN SELECT l_shipdate FROM public.lineitem ORDER BY l_shipdate;

Gambar berikut menunjukkan hasil yang dikembalikan.image

Saran optimasi: Jika klausa ORDER BY melibatkan sejumlah besar data, banyak sumber daya dikonsumsi. Kami sarankan Anda mencegah pengurutan sejumlah besar data.

Limit

Limit digunakan untuk mengontrol jumlah baris yang dapat dikembalikan oleh pernyataan SQL. Operator limit hanya mengontrol jumlah baris yang dapat dikembalikan dalam hasil akhir, dan tidak mengontrol jumlah baris yang dapat dipindai dalam perhitungan. Operator limit hanya dapat mengontrol jumlah baris yang dipindai jika operator limit didorong ke node Seq Scan.

Dalam contoh ini, limit 1 didorong ke node Seq Scan, dan hanya satu baris data yang perlu dipindai.

EXPLAIN SELECT * FROM public.lineitem limit 1;

Gambar berikut menunjukkan hasil yang dikembalikan.image

Saran optimasi:

  • Tidak semua operator limit dapat didorong. Kami sarankan Anda mengonfigurasi kondisi filter dalam kueri SQL untuk mencegah pemindaian tabel penuh.

  • Kami sarankan Anda tidak menetapkan limit ke nilai super besar, seperti 100.000 atau bahkan 1.000.000. Jika Anda menetapkan limit ke nilai besar, sejumlah besar data dipindai meskipun limit didorong. Akibatnya, proses pemindaian memakan waktu.

Append

Append digunakan untuk menggabungkan hasil subkueri, yang biasanya digunakan dalam operasi Union All.

Exchange

Exchange digunakan untuk bertukar data di antara shard. Anda tidak perlu terlalu memperhatikan operator ini.

Forward

Forward digunakan untuk mentransmisikan data operator antara HQE dan PQE atau antara HQE dan SQE.

Project

Project menunjukkan pemetaan antara subkueri dan kueri luar. Anda tidak perlu terlalu memperhatikan operator ini.

Referensi

Anda dapat melihat rencana eksekusi secara visual di HoloWeb. Untuk informasi lebih lanjut, lihat Lihat rencana eksekusi.