Bagaimana cara saya memaksa Postgres untuk menggunakan indeks yang sebaliknya akan bersikeras melakukan pemindaian berurutan?
Bagaimana cara saya memaksa Postgres untuk menggunakan indeks yang sebaliknya akan bersikeras melakukan pemindaian berurutan?
Jawaban:
Dengan asumsi Anda bertanya tentang fitur "petunjuk indeks" yang umum ditemukan di banyak database, PostgreSQL tidak menyediakan fitur seperti itu. Ini adalah keputusan sadar yang dibuat oleh tim PostgreSQL. Gambaran umum yang baik tentang mengapa dan apa yang dapat Anda lakukan dapat ditemukan di sini . Alasannya pada dasarnya karena ini adalah peretasan kinerja yang cenderung menyebabkan lebih banyak masalah di kemudian hari seiring dengan perubahan data Anda, sedangkan pengoptimal PostgreSQL dapat mengevaluasi ulang rencana berdasarkan statistik. Dengan kata lain, apa yang mungkin menjadi rencana kueri yang baik saat ini mungkin tidak akan menjadi rencana kueri yang baik untuk semua waktu, dan petunjuk indeks memaksa rencana kueri tertentu untuk sepanjang waktu.
Sebagai palu yang sangat tumpul, berguna untuk pengujian, Anda dapat menggunakan parameter enable_seqscan
dan enable_indexscan
. Lihat:
Ini tidak cocok untuk penggunaan produksi yang berkelanjutan . Jika Anda memiliki masalah dengan pilihan paket kueri, Anda akan melihat dokumentasi untuk melacak masalah kinerja kueri . Jangan hanya mengatur enable_
parameter dan pergi begitu saja.
Kecuali Anda memiliki alasan yang sangat bagus untuk menggunakan indeks, Postgres mungkin membuat pilihan yang tepat. Mengapa?
Lihat juga posting grup berita lama ini .
Mungkin satu-satunya alasan yang valid untuk menggunakan
set enable_seqscan=false
adalah saat Anda menulis kueri dan ingin segera melihat apa rencana kueri sebenarnya jika ada data dalam jumlah besar di tabel. Atau tentu saja jika Anda perlu segera mengonfirmasi bahwa kueri Anda tidak menggunakan indeks hanya karena kumpulan data terlalu kecil.
set enable_seqscan=false
, jalankan kueri Anda, lalu jalankan dengan cepat set enable_seqscan=true
untuk mengembalikan postgresql ke perilaku yang semestinya (dan jelas jangan lakukan ini dalam produksi, hanya dalam pengembangan!)
SET SESSION enable_seqscan=false
untuk hanya mempengaruhi diri sendiri
Terkadang PostgreSQL gagal membuat pilihan indeks terbaik untuk kondisi tertentu. Sebagai contoh, misalkan ada tabel transaksi dengan beberapa juta baris, yang jumlahnya beberapa ratus untuk hari tertentu, dan tabel tersebut memiliki empat indeks: transaction_id, client_id, date, dan description. Anda ingin menjalankan kueri berikut:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description = 'Refund'
GROUP BY client_id
PostgreSQL dapat memilih untuk menggunakan indeks transaction_description_idx daripada transaction_date_idx, yang dapat menyebabkan kueri membutuhkan waktu beberapa menit alih-alih kurang dari satu detik. Jika demikian, Anda dapat memaksa menggunakan indeks pada tanggal dengan memalsukan kondisi seperti ini:
SELECT client_id, SUM(amount)
FROM transactions
WHERE date >= 'yesterday'::timestamp AND date < 'today'::timestamp AND
description||'' = 'Refund'
GROUP BY client_id
your_wanted_index
, bisa jadi mesin postgresql hanya akan melakukan pemindaian urutan / kunci primer saja. Kesimpulan - tidak ada metode yang 100% dapat diandalkan untuk memaksa beberapa penggunaan indeks untuk server PostgreSql.
where
kondisi kecuali dua tabel atau bergabung dan Postgres gagal mengambil indeks.
Masalah ini biasanya terjadi ketika perkiraan biaya pemindaian indeks terlalu tinggi dan tidak mencerminkan kenyataan dengan benar. Anda mungkin perlu menurunkan random_page_cost
parameter konfigurasi untuk memperbaikinya. Dari dokumentasi Postgres :
Mengurangi nilai ini [...] akan menyebabkan sistem memilih pemindaian indeks; menaikkannya akan membuat pemindaian indeks terlihat relatif lebih mahal.
Anda dapat memeriksa apakah nilai yang lebih rendah benar-benar akan membuat Postgres menggunakan indeks (tetapi gunakan ini hanya untuk pengujian ):
EXPLAIN <query>; # Uses sequential scan
SET random_page_cost = 1;
EXPLAIN <query>; # May use index scan now
Anda dapat mengembalikan nilai default dengan SET random_page_cost = DEFAULT;
lagi.
Pemindaian indeks memerlukan pengambilan halaman disk yang tidak berurutan. Postgres menggunakan random_page_cost
untuk memperkirakan biaya pengambilan tidak berurutan dalam kaitannya dengan pengambilan berurutan. Nilai defaultnya adalah 4.0
, dengan demikian mengasumsikan faktor biaya rata - rata 4 dibandingkan dengan pengambilan berurutan (dengan mempertimbangkan efek cache).
Namun masalahnya adalah bahwa nilai default ini tidak sesuai dalam skenario penting kehidupan nyata berikut ini:
1) Drive solid-state
Seperti yang diakui dalam dokumentasi:
Penyimpanan yang memiliki biaya pembacaan acak yang rendah relatif terhadap sekuensial, misalnya solid-state drive, mungkin lebih baik dimodelkan dengan nilai yang lebih rendah
random_page_cost
.
Menurut poin terakhir slide ini dari pidato di PostgresConf 2018, random_page_cost
harus diatur ke sesuatu antara 1.0
dan 2.0
untuk solid-state drive.
2) Data cache
Jika data indeks yang diperlukan sudah disimpan dalam cache dalam RAM, pemindaian indeks akan selalu jauh lebih cepat daripada pemindaian sekuensial. Dokumentasinya mengatakan:
Sejalan dengan itu, jika data Anda kemungkinan besar berada dalam cache, [...] penurunan
random_page_cost
dapat dilakukan.
Masalahnya adalah Anda tentu tidak dapat dengan mudah mengetahui apakah data yang relevan sudah di-cache. Namun, jika indeks tertentu sering ditanyakan, dan jika sistem memiliki RAM yang memadai, maka data kemungkinan besar akan di-cache, dan random_page_cost
harus disetel ke nilai yang lebih rendah. Anda harus bereksperimen dengan nilai yang berbeda dan melihat mana yang berhasil untuk Anda.
Anda mungkin juga ingin menggunakan ekstensi pg_prewarm untuk cache data eksplisit.
Pertanyaan itu sendiri sangat tidak valid. Memaksa (dengan melakukan enable_seqscan = off misalnya) adalah ide yang sangat buruk. Mungkin berguna untuk memeriksa apakah itu akan lebih cepat, tetapi kode produksi tidak boleh menggunakan trik seperti itu.
Sebagai gantinya - jelaskan analisis kueri Anda, baca, dan cari tahu mengapa PostgreSQL memilih paket yang buruk (menurut pendapat Anda).
Ada alat di web yang membantu membaca menjelaskan hasil analisis - salah satunya adalah menjelaskan.depesz.com - yang ditulis oleh saya.
Pilihan lainnya adalah bergabung dengan saluran #postgresql di jaringan irc freenode , dan berbicara dengan orang-orang di sana untuk membantu Anda - karena mengoptimalkan kueri bukanlah masalah "ajukan pertanyaan, dapatkan jawaban dengan senang hati". Ini lebih seperti percakapan, dengan banyak hal untuk diperiksa, banyak hal untuk dipelajari.
Ada trik untuk mendorong postgres agar lebih memilih seqscan yang menambahkan a OFFSET 0
di subquery
Ini berguna untuk mengoptimalkan permintaan yang menautkan tabel besar / besar ketika yang Anda butuhkan hanyalah n elemen pertama / terakhir.
Katakanlah Anda mencari 20 elemen pertama / terakhir yang melibatkan banyak tabel yang memiliki 100k (atau lebih) entri, tidak ada gunanya membangun / menghubungkan semua kueri di semua data ketika apa yang akan Anda cari ada di 100 atau 1000 pertama entri. Dalam skenario ini misalnya, ternyata lebih dari 10x lebih cepat untuk melakukan pemindaian berurutan.
lihat Bagaimana cara mencegah Postgres agar tidak menyejajarkan subquery?