Saya punya tabel PostgreSQL 9.3 dengan beberapa angka dan beberapa data tambahan:
CREATE TABLE mytable (
myid BIGINT,
somedata BYTEA
)
Tabel ini saat ini memiliki sekitar 10 juta catatan dan membutuhkan 1GB ruang disk. myid
tidak berturut-turut.
Saya ingin menghitung berapa baris di setiap blok yang berisi 100000 angka berurutan:
SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
Ini mengembalikan sekitar 3500 baris.
Saya perhatikan bahwa keberadaan indeks tertentu secara signifikan mempercepat kueri ini meskipun rencana kueri tidak menyebutkannya sama sekali. Paket permintaan tanpa indeks:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------
GroupAggregate (cost=1636639.92..1709958.65 rows=496942 width=8) (actual time=6783.763..8888.841 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Sort (cost=1636639.92..1659008.91 rows=8947594 width=8) (actual time=6783.752..8005.831 rows=8947557 loops=1)
Output: ((myid / 100000))
Sort Key: ((mytable.myid / 100000))
Sort Method: external merge Disk: 157440kB
-> Seq Scan on public.mytable (cost=0.00..236506.92 rows=8947594 width=8) (actual time=0.020..1674.838 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 8914.780 ms
(9 rows)
Indeks:
db=> CREATE INDEX myindex ON mytable ((myid/100000));
db=> VACUUM ANALYZE;
Paket kueri baru:
db=> EXPLAIN (ANALYZE TRUE, VERBOSE TRUE) SELECT myid/100000 AS block, count(*) AS total FROM mytable GROUP BY myid/100000;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=281242.99..281285.97 rows=3439 width=8) (actual time=3190.189..3190.800 rows=3460 loops=1)
Output: ((myid / 100000)), count(*)
-> Seq Scan on public.mytable (cost=0.00..236505.56 rows=8947485 width=8) (actual time=0.026..1659.571 rows=8947557 loops=1)
Output: (myid / 100000)
Total runtime: 3190.975 ms
(5 rows)
Jadi, rencana kueri dan runtime berbeda secara signifikan (hampir tiga kali) tetapi tidak disebutkan indeksnya. Perilaku ini dapat direproduksi dengan sempurna di mesin dev saya: Saya mengalami beberapa siklus menjatuhkan indeks, menguji kueri beberapa kali, membuat ulang indeks, dan lagi menguji kueri beberapa kali. Apa yang sedang terjadi disini?
HashAggregate
metode ini (dan tidak diperlukan penyortiran), sehingga Anda mendapatkan kinerja yang lebih baik. Mengapa indeks tidak disebutkan dalam rencana, saya belum tahu.explain (analyze true, verbose true) ...
?Jawaban:
VACUUM ANALYZE
membuat perbedaan dalam contoh Anda. Plus, seperti yang disediakan @jjanes , statistik tambahan untuk indeks fungsional. Per dokumentasi:Namun, membuat indeks tidak dengan sendirinya menyebabkan Postgres mengumpulkan statistik. Mencoba:
Tidak mengembalikan apa pun hingga Anda menjalankan yang pertama
ANALYZE
(atauVACUUM ANALYZE
, daemon autovacuum masuk).Sekarang Anda akan melihat statistik tambahan.
Karena seluruh tabel harus tetap dibaca, Postgres akan menggunakan pemindaian berurutan kecuali ia mengharapkan perhitungan
myid/100000
menjadi cukup mahal untuk beralih, padahal sebenarnya tidak.Satu-satunya kesempatan Anda lainnya adalah pemindaian hanya indeks jika indeks jauh lebih kecil dari tabel - dan prasyarat untuk pemindaian hanya indeks terpenuhi. Detail dalam Postgres Wiki dan manual .
Selama indeks fungsional tidak digunakan, manfaat jaminan dari statistik tambahan adalah moderat. Jika tabel ini hanya dapat dibaca, biayanya akan rendah - tetapi sekali lagi, kami mungkin akan segera memindai indeks saja.
Mungkin Anda juga dapat mencapai rencana kueri yang lebih baik dengan menetapkan target statistik yang lebih tinggi
mytable.myid
. Itu hanya akan menimbulkan biaya kecil. Lebih:sumber
myid/100000 BETWEEN somevalue AND othervalue
kondisi tambahan , jadi indeks akan digunakan dalam rencana kueri — saya baru saja menanyakan pertanyaan ini karena saya tidak mengerti mengapa indeks berguna dalam seluruh tabel.WHERE myid BETWEEN somevalue*100000 AND othervalue*100000
(mempertimbangkan pembulatan efek tergantung pada jenis Anda), dan Anda mungkin sudah memiliki indeks polos padamyid
, sehingga Anda dapat melakukannya tanpa indeks khusus tambahan. Mungkin lebih efisien.Ketika Anda membuat indeks ekspresi, itu menyebabkan PostgreSQL untuk mengumpulkan statistik pada ekspresi itu. Dengan statistik tersebut, sekarang memiliki perkiraan akurat untuk jumlah baris teragregasi yang akan dikembalikan oleh kueri, yang mengarahkannya untuk membuat pilihan rencana yang lebih baik.
Khususnya dalam kasus ini, tanpa statistik tambahan itu menurutnya tabel hash akan terlalu besar untuk muat di work_mem, jadi itu tidak memilih metode itu.
sumber
work_mem
. Jika Anda menaikkannya sehingga jenisnya cocok dengan memori, apakah masih akan menggunakan paket yang sama. Biarkan saya perhatikan di sini bahwa perbedaan waktu (sebagian besar) berasal dari jenis disk eksternal.