Pemindaian Indeks (Hanya) --> Pemindaian Indeks Bitmap --> Pemindaian Berurutan
Untuk beberapa baris, ada baiknya menjalankan pemindaian indeks. Jika halaman data yang cukup dapat dilihat oleh semua (=cukup disedot, dan tidak terlalu banyak beban tulis bersamaan) dan indeks dapat menyediakan semua nilai kolom yang diperlukan, maka pemindaian indeks saja yang lebih cepat digunakan. Dengan lebih banyak baris yang diharapkan untuk dikembalikan (persentase tabel yang lebih tinggi dan bergantung pada distribusi data, frekuensi nilai, dan lebar baris), kemungkinan besar untuk menemukan beberapa baris pada satu halaman data menjadi lebih besar. Kemudian membayar untuk beralih ke scan indeks bitmap. (Atau untuk menggabungkan beberapa indeks yang berbeda.) Setelah sebagian besar halaman data harus dikunjungi, lebih murah untuk menjalankan pemindaian berurutan, memfilter baris surplus, dan melewatkan overhead untuk indeks sama sekali.
Penggunaan indeks menjadi (jauh) lebih murah dan lebih mungkin ketika mengakses halaman data secara acak tidak (jauh) lebih mahal daripada mengaksesnya secara berurutan. Itulah yang terjadi saat menggunakan SSD alih-alih memutar disk, atau bahkan lebih banyak lagi yang di-cache di RAM - dan parameter konfigurasi masing-masing random_page_cost
dan effective_cache_size
diatur sesuai.
Dalam kasus Anda, Postgres beralih ke pemindaian berurutan, berharap menemukan rows=263962
, itu sudah 3% dari seluruh tabel. (Sementara hanya rows=47935
benar-benar ditemukan, lihat di bawah.)
Lebih banyak dalam jawaban terkait ini:
- Kueri PostgreSQL yang efisien pada stempel waktu menggunakan indeks atau pemindaian indeks bitmap?
Hati-hati dengan rencana kueri yang dipaksakan
Anda tidak dapat memaksakan metode perencana tertentu secara langsung di Postgres, tetapi Anda dapat membuat lainnya metode tampaknya sangat mahal untuk tujuan debugging. Lihat Konfigurasi Metode Planner di manual.
SET enable_seqscan = off
(seperti yang disarankan dalam jawaban lain) melakukan itu untuk pemindaian berurutan. Tapi itu dimaksudkan untuk tujuan debugging di sesi Anda saja. Jangan jangan gunakan ini sebagai pengaturan umum dalam produksi kecuali Anda tahu persis apa yang Anda lakukan. Itu dapat memaksa rencana kueri yang konyol. Panduan:
Parameter konfigurasi ini menyediakan metode kasar untuk memengaruhi rencana kueri yang dipilih oleh pengoptimal kueri. Jika rencana default yang dipilih oleh pengoptimal untuk kueri tertentu tidak optimal, sementara solusinya adalah dengan menggunakan salah satu parameter konfigurasi ini untuk memaksa pengoptimal memilih paket yang berbeda. Cara yang lebih baik untuk meningkatkan kualitas rencana yang dipilih oleh pengoptimal termasuk menyesuaikan konstanta biaya perencana (lihat Bagian 19.7.2), menjalankan
ANALYZE
secara manual, meningkatkan nilaidefault_statistics_target
parameter konfigurasi, dan meningkatkan jumlah statistik yang dikumpulkan untuk kolom tertentu menggunakanALTER TABLE SET STATISTICS
.
Itu sudah sebagian besar saran yang Anda butuhkan.
- Jauhkan PostgreSQL dari terkadang memilih paket kueri yang buruk
Dalam kasus khusus ini, Postgres mengharapkan 5-6 kali lebih banyak klik di email_activities.email_recipient_id
daripada yang sebenarnya ditemukan:
perkiraan
rows=227007
vs.actual ... rows=40789
perkiraanrows=263962
vs.actual ... rows=47935
Jika Anda sering menjalankan kueri ini, Anda harus membayar ANALYZE
lihat sampel yang lebih besar untuk statistik yang lebih akurat pada kolom tertentu. Meja Anda besar (~ 10 juta baris), jadi buatlah:
ALTER TABLE email_activities ALTER COLUMN email_recipient_id
SET STATISTICS 3000; -- max 10000, default 100
Kemudian ANALYZE email_activities;
Ukuran upaya terakhir
Dalam sangat jarang kasus Anda mungkin terpaksa memaksa indeks dengan SET LOCAL enable_seqscan = off
dalam transaksi terpisah atau dalam fungsi dengan lingkungannya sendiri. Seperti:
CREATE OR REPLACE FUNCTION f_count_dist_recipients(_email_campaign_id int, _limit int)
RETURNS bigint AS
$func$
SELECT COUNT(DISTINCT a.email_recipient_id)
FROM email_activities a
WHERE a.email_recipient_id IN (
SELECT id
FROM email_recipients
WHERE email_campaign_id = $1
LIMIT $2) -- or consider query below
$func$ LANGUAGE sql VOLATILE COST 100000 SET enable_seqscan = off;
Pengaturan hanya berlaku untuk lingkup lokal fungsi.
Peringatan: Ini hanya bukti konsep. Bahkan intervensi manual yang jauh lebih radikal ini mungkin menggigit Anda dalam jangka panjang. Kardinalitas, frekuensi nilai, skema Anda, pengaturan Postgres global, semuanya berubah seiring waktu. Anda akan meningkatkan ke versi Postgres baru. Rencana kueri yang Anda paksa sekarang, mungkin akan menjadi ide yang sangat buruk nantinya.
Dan biasanya ini hanya solusi untuk masalah dengan pengaturan Anda. Lebih baik temukan dan perbaiki.
Kueri alternatif
Informasi penting tidak ada dalam pertanyaan, tetapi kueri yang setara ini mungkin lebih cepat dan lebih mungkin menggunakan indeks pada (email_recipient_id
) - semakin menjadi untuk LIMIT
yang lebih besar .
SELECT COUNT(*) AS ct
FROM (
SELECT id
FROM email_recipients
WHERE email_campaign_id = 1607
LIMIT 43000
) r
WHERE EXISTS (
SELECT FROM email_activities
WHERE email_recipient_id = r.id);