PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Postgres tidak menggunakan indeks ketika pemindaian indeks adalah opsi yang jauh lebih baik

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 menggunakan ALTER 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
perkiraan rows=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);


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana memulihkan data dari wadah Docker yang dihapus? Bagaimana cara menyambungkannya kembali ke data?

  2. Bagaimana Sind() Bekerja di PostgreSQL

  3. Bagaimana cara menambahkan Kolom baru di tabel setelah kolom ke-2 atau ke-3 di Tabel menggunakan postgres?

  4. Mengotomatiskan Tugas Harian PostgreSQL Menggunakan Jenkins

  5. Mengubah port server kontainer postgres di Docker Compose