Mengapa Set Returning Function (SRF) berjalan lebih lambat dalam klausa FROM?

8

Ini adalah pertanyaan database-internal. Saya menggunakan PostgreSQL 9.5, saya bertanya-tanya mengapa Set Returning Functions (SRFs), juga dikenal sebagai Table Valued Functions (TVFs) berjalan lebih lambat ketika dalam FROMklausa, misalnya ketika saya menjalankan perintah ini,

CREATE TABLE foo AS SELECT * FROM generate_series(1,1e7);
SELECT 10000000
Time: 5573.574 ms

Itu selalu jauh lebih lambat daripada,

CREATE TABLE foo AS SELECT generate_series(1,1e7);
SELECT 10000000
Time: 4622.567 ms

Apakah ada aturan umum yang dapat dibuat di sini, sehingga kita harus selalu menjalankan Fungsi Set-Returning di luar FROMklausa?

Evan Carroll
sumber

Jawaban:

13

Mari kita mulai dengan membandingkan rencana eksekusi:

tinker=> EXPLAIN ANALYZE SELECT * FROM generate_series(1,1e7);
                                                           QUERY PLAN                                                           
--------------------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series  (cost=0.00..10.00 rows=1000 width=32) (actual time=2382.582..4291.136 rows=10000000 loops=1)
 Planning time: 0.022 ms
 Execution time: 5539.522 ms
(3 rows)

tinker=> EXPLAIN ANALYZE SELECT generate_series(1,1e7);
                                           QUERY PLAN                                            
-------------------------------------------------------------------------------------------------
 Result  (cost=0.00..5.01 rows=1000 width=0) (actual time=0.008..2622.365 rows=10000000 loops=1)
 Planning time: 0.045 ms
 Execution time: 3858.661 ms
(3 rows)

Oke, jadi sekarang kita tahu bahwa SELECT * FROM generate_series()dieksekusi menggunakan Function Scannode, sementara SELECT generate_series()dieksekusi menggunakan Resultnode. Apa pun yang menyebabkan kueri ini berkinerja berbeda bermuara pada perbedaan antara dua node ini, dan kami tahu persis ke mana harus mencari.

Satu hal lain yang menarik dalam EXPLAIN ANALYZEoutput: perhatikan timing. SELECT generate_series()adalah actual time=0.008..2622.365, sementara SELECT * FROM generate_series()ini actual time=2382.582..4291.136. The Function Scansimpul dimulai kembali catatan sekitar waktu Resultsimpul selesai kembali catatan.

Apa yang PostgreSQL lakukan di antara t=0dan t=2382di dalam Function Scanrencana? Rupanya itu tentang berapa lama waktu yang dibutuhkan untuk menjalankan generate_series(), jadi saya bertaruh itulah yang dilakukannya. Jawabannya mulai berbentuk: sepertinya Resultmengembalikan hasil dengan segera, sementara itu sepertinya Function Scanmematerialisasi hasil dan kemudian memindai mereka.

Dengan EXPLAINkeluar dari jalan, mari kita periksa implementasinya. The Resultsimpul tinggal di nodeResult.c, yang mengatakan:

 * DESCRIPTION
 *
 *      Result nodes are used in queries where no relations are scanned.

Kode ini cukup sederhana.

Function Scantinggal di nodeFunctionScan.c, dan memang tampaknya mengambil strategi eksekusi dua fase :

/*
 * If first time through, read all tuples from function and put them
 * in a tuplestore. Subsequent calls just fetch tuples from
 * tuplestore.
 */

Dan untuk kejelasan, mari kita lihat apa tuplestoreitu :

 * tuplestore.h
 *    Generalized routines for temporary tuple storage.
 *
 * This module handles temporary storage of tuples for purposes such
 * as Materialize nodes, hashjoin batch files, etc.  It is essentially
 * a dumbed-down version of tuplesort.c; it does no sorting of tuples
 * but can only store and regurgitate a sequence of tuples.  However,
 * because no sort is required, it is allowed to start reading the sequence
 * before it has all been written.  This is particularly useful for cursors,
 * because it allows random access within the already-scanned portion of
 * a query without having to process the underlying scan to completion.
 * Also, it is possible to support multiple independent read pointers.
 *
 * A temporary file is used to handle the data if it exceeds the
 * space limit specified by the caller.

Hipotesis dikonfirmasi. Function Scanmengeksekusi dimuka, mematerialisasi hasil fungsi, yang untuk hasil besar menetapkan hasil tumpah ke disk. Resulttidak terwujud apa pun, tetapi juga hanya mendukung operasi sepele.

willglynn
sumber