Perang api minggu ini pada daftar kinerja pgsql sekali lagi berkisar pada fakta bahwa PostgreSQL tidak memiliki sintaks petunjuk tradisional yang tersedia di database lain. Ada campuran alasan teknis dan pragmatis di baliknya:
- Memperkenalkan petunjuk adalah sumber umum dari masalah di kemudian hari, karena memperbaiki tempat kueri sekali dalam kasus khusus bukanlah pendekatan yang sangat kuat. Saat kumpulan data Anda tumbuh, dan mungkin juga mengubah distribusi, ide yang Anda usulkan saat masih kecil bisa menjadi ide yang semakin buruk.
- Menambahkan antarmuka petunjuk yang berguna akan memperumit kode pengoptimal, yang cukup sulit untuk dipertahankan sebagaimana adanya. Sebagian alasan PostgreSQL berfungsi sebaik menjalankan kueri adalah karena kode yang menyenangkan (“kita dapat memeriksa petunjuk pada daftar fitur perbandingan vendor kita!”) yang sebenarnya tidak membayar untuk dirinya sendiri, dalam hal pembuatan database cukup baik untuk membenarkan pemeliharaan lanjutannya, ditolak oleh kebijakan. Jika tidak berhasil, itu tidak akan ditambahkan. Dan ketika dievaluasi secara objektif, petunjuk rata-rata merupakan masalah daripada solusi.
- Jenis masalah yang mengisyaratkan pekerjaan dapat berupa bug pengoptimal. Komunitas PostgreSQL merespons bug sebenarnya di pengoptimal lebih cepat daripada siapa pun di industri ini. Bertanya-tanya dan Anda tidak perlu bertemu banyak pengguna PostgreSQL sebelum menemukan satu yang telah melaporkan bug dan menyaksikannya diperbaiki pada hari berikutnya.
Sekarang, respons utama yang benar-benar valid untuk menemukan petunjuk yang hilang, biasanya dari DBA yang terbiasa dengannya, adalah "bagaimana cara menangani bug pengoptimal ketika saya mengalaminya?" Seperti semua pekerjaan teknologi saat ini, biasanya ada tekanan besar untuk mendapatkan perbaikan secepat mungkin ketika masalah kueri yang buruk muncul.
Jika PostgreSQL tidak memiliki beberapa cara untuk menangani situasi itu, tidak akan ada produksi database PostgreSQL yang serius . Perbedaannya adalah bahwa hal-hal yang Anda sesuaikan dalam database ini lebih berakar dalam memengaruhi keputusan yang telah dibuat oleh pengoptimal dengan cara yang cukup halus, daripada hanya Anda yang memberi tahu apa yang harus dilakukan. Ini adalah petunjuk dalam arti kata yang sebenarnya, mereka hanya tidak memiliki beberapa antarmuka pengguna untuk mengisyaratkan bahwa pengguna database lain yang baru menggunakan PostgreSQL sedang mencari.
Dengan mengingat hal itu, mari kita lihat apa yang dapat Anda lakukan di PostgreSQL untuk mengatasi rencana kueri yang buruk dan bug pengoptimal, terutama hal-hal yang menurut banyak orang hanya dapat diselesaikan dengan petunjuk:
- join_collapse_limit: Ini menyesuaikan seberapa banyak fleksibilitas yang dimiliki pengoptimal untuk menyusun ulang gabungan beberapa tabel. Biasanya ia mencoba setiap kombinasi yang mungkin ketika gabungan dapat diatur ulang (yang sebagian besar waktu, kecuali jika Anda menggunakan gabungan luar). Menurunkan join_collapse_limit, bahkan mungkin ke 1, menghilangkan beberapa atau semua fleksibilitas ini. Dengan disetel ke 1, Anda akan mendapatkan gabungan dalam urutan yang Anda tulis, titik. Merencanakan penggabungan dalam jumlah besar adalah salah satu hal tersulit yang harus dilakukan oleh pengoptimal; setiap gabungan memperbesar kesalahan dalam perkiraan, dan meningkatkan waktu perencanaan kueri. Jika sifat dasar data Anda memperjelas penggabungan urutan apa yang harus terjadi, dan Anda tidak mengharapkan hal itu berubah, setelah Anda mengetahui urutan yang benar, Anda dapat menguncinya menggunakan parameter ini.
- random_page_cost: Secara default ke 4.0, parameter ini menetapkan seberapa mahal pencarian disk untuk menemukan halaman acak pada disk, relatif terhadap nilai referensi 1.0. Sekarang, pada kenyataannya, jika Anda mengukur rasio I/O acak dan berurutan pada hard drive biasa, Anda akan menemukan angka ini mendekati 50. Jadi mengapa 4.0? Pertama, karena ini berhasil lebih baik daripada nilai yang lebih besar dalam pengujian komunitas. Kedua, dalam banyak kasus, data indeks khususnya akan di-cache dalam memori, membuat biaya efektif untuk membaca nilai-nilai tersebut lebih rendah. Jika, misalnya, indeks Anda 90% di-cache dalam RAM, itu berarti 10% dari waktu Anda akan melakukan operasi yang 50X lebih mahal; yang akan membuat random_page_cost efektif Anda sekitar 5. Situasi dunia nyata semacam ini adalah alasan mengapa default masuk akal di mana itu berada. Saya biasanya melihat indeks populer mendapatkan> 95% cache di memori. Jika indeks Anda sebenarnya jauh lebih mungkin daripada itu untuk semua berada di RAM, mengurangi random_page_cost hingga tepat di atas 1,0 bisa menjadi pilihan yang masuk akal, untuk mencerminkan bahwa itu tidak lebih mahal daripada bacaan lainnya. Pada saat yang sama, pencarian acak pada sistem yang sangat sibuk bisa jauh lebih mahal daripada yang Anda harapkan dari hanya melihat simulasi pengguna tunggal. Saya harus menetapkan random_page_cost setinggi 60 agar database berhenti menggunakan indeks ketika perencana salah memperkirakan seberapa mahal harganya. Biasanya situasi itu berasal dari kesalahan estimasi sensitivitas di pihak perencana – jika Anda memindai lebih dari sekitar 20% tabel, perencana tahu menggunakan Pemindaian Berurutan akan jauh lebih efisien daripada Pemindaian Indeks. Situasi buruk di mana saya harus memaksa perilaku itu terjadi jauh lebih awal daripada yang terjadi ketika perencana mengharapkan 1% dari baris dikembalikan, tetapi sebenarnya mendekati 15%.
- work_mem: Menyesuaikan jumlah memori yang tersedia untuk kueri yang melakukan pengurutan, hashing, dan operasi berbasis memori serupa. Ini hanya pedoman kasar untuk kueri, bukan batasan yang pasti, dan satu klien dapat menggunakan banyak work_mem saat menjalankan kueri. Oleh karena itu, Anda harus berhati-hati untuk tidak menetapkan nilai ini terlalu tinggi di file postgresql.conf. Apa yang dapat Anda lakukan sebagai gantinya, itu mengaturnya sebelum menjalankan kueri yang benar-benar diuntungkan dari memiliki memori ekstra untuk menyimpan penyortiran atau hash data. Terkadang Anda dapat menemukan kueri ini dari mencatat yang lambat menggunakan log_min_duration_statement. Anda juga dapat menemukannya dengan mengaktifkan log_temp_files, yang akan mencatat log setiap kali work_mem terlalu kecil, dan oleh karena itu operasi pengurutan tumpah ke disk alih-alih terjadi di memori.
- OFFSET 0: PostgreSQL akan mengatur ulang subkueri ke dalam bentuk gabungan, sehingga kemudian dapat menggunakan logika urutan gabungan biasa untuk mengoptimalkannya. Dalam beberapa kasus, keputusan itu bisa menjadi keputusan yang sangat buruk, karena hal-hal yang cenderung ditulis orang sebagai subkueri tampaknya sedikit lebih sulit untuk diperkirakan karena alasan tertentu (saya katakan itu berdasarkan jumlah kueri yang merepotkan seperti itu yang saya lihat). Salah satu trik licik yang dapat Anda lakukan untuk mencegah logika ini adalah meletakkan OFFSET 0 di akhir subquery. Ini tidak mengubah hasil apa pun, tetapi memasukkan jenis node kueri Batas yang digunakan untuk menjalankan OFFSET akan mencegah penataan ulang. Subquery kemudian akan selalu dieksekusi dengan cara yang diharapkan kebanyakan orang–sebagai node kuerinya sendiri yang terisolasi.
- enable_seqscan, enable_indexscan, enable_bitmapscan: Menonaktifkan salah satu fitur ini untuk mencari baris dalam tabel adalah palu yang cukup besar untuk sangat menyarankan menghindari jenis pemindaian tersebut (tidak selalu mencegahnya–jika tidak ada cara untuk menjalankan rencana Anda tetapi seqscan, Anda akan mendapatkan seqscan meskipun parameter dimatikan). Hal utama yang saya sarankan untuk ini bukanlah untuk memperbaiki kueri, melainkan untuk bereksperimen dengan MENJELASKAN dan melihat mengapa jenis pemindaian lain lebih disukai.
- enable_nestloop, enable_hashjoin, enable_mergejoin: Jika Anda menduga masalah Anda adalah jenis gabungan yang digunakan daripada cara tabel dibaca, coba nonaktifkan jenis yang Anda lihat di paket menggunakan salah satu parameter ini, lalu jalankan EXPLAIN lagi. Kesalahan dalam perkiraan sensitivitas dapat dengan mudah membuat gabungan tampak lebih atau kurang efisien daripada yang sebenarnya. Dan, sekali lagi, melihat bagaimana rencana berubah dengan menonaktifkan metode bergabung saat ini dapat menjadi sangat informatif tentang alasan mengapa hal itu diputuskan sejak awal.
- enable_hashagg, enable_material: Fitur-fitur ini relatif baru di PostgreSQL. Penggunaan Hash Aggregation secara agresif diperkenalkan di versi 8.4, dan materialisasi yang lebih agresif di 9.0. Jika Anda melihat jenis node tersebut di output EXPLAIN
Anda dan sepertinya mereka melakukan sesuatu yang salah, karena kode ini jauh lebih baru, kemungkinan besar memiliki batasan atau bug daripada beberapa fitur lama. Jika Anda memiliki rencana yang berfungsi dengan baik di versi PostgreSQL yang lebih lama, tetapi menggunakan salah satu dari jenis simpul ini dan tampaknya berkinerja jauh lebih buruk sebagai hasilnya, menonaktifkan fitur ini terkadang dapat mengembalikan Anda ke perilaku sebelumnya – serta menyoroti mengapa pengoptimal melakukan hal yang salah sebagai umpan balik yang berguna. Perhatikan bahwa ini biasanya cara fitur yang lebih canggih cenderung diperkenalkan ke PostgreSQL: dengan opsi untuk menonaktifkannya untuk tujuan pemecahan masalah, jika terbukti ada regresi rencana relatif terhadap cara versi sebelumnya mengeksekusi sesuatu. - cursor_tuple_fraction: Jika Anda tidak bermaksud membaca kembali semua baris dari kueri, Anda harus menggunakan kursor untuk menerapkannya. Dalam hal ini, pengoptimal mencoba memprioritaskan apakah ia mengembalikan baris pertama dengan cepat, atau lebih memilih untuk mengoptimalkan seluruh kueri, berdasarkan parameter ini. Secara default, database mengasumsikan bahwa Anda akan membaca kembali 10% kueri saat Anda menggunakan kursor. Menyesuaikan parameter ini memungkinkan Anda mencondongkannya ke arah mengharapkan Anda membaca kurang atau lebih dari itu.
Semua parameter dan tweak kueri ini harus mempertimbangkan penyesuaian triase. Anda tidak ingin menjalankan ini selamanya (kecuali mungkin untuk join_collapse_limit). Anda menggunakannya untuk keluar dari kemacetan, dan kemudian mudah-mudahan Anda akan mengetahui apa penyebab sebenarnya dari rencana buruk-statistik buruk, batasan/bug pengoptimal, atau sesuatu yang lain-dan kemudian mengatasi masalah dari arah itu. Semakin Anda mendorong perilaku pengoptimal ke suatu arah, semakin terbuka Anda terhadap perubahan di masa mendatang dalam data Anda yang membuat dorongan itu tidak lagi benar. Jika Anda menggunakannya dengan benar, sebagai cara untuk mempelajari mengapa Anda mendapatkan rencana yang salah (pendekatan yang saya gunakan dalam bab optimisasi kueri PostgreSQL 9.0 High Performance), cara Anda memberi petunjuk pada hal-hal di PostgreSQL akan membuat Anda meninggalkan setiap run- dengan perilaku pengoptimal yang buruk sedikit lebih paham tentang cara menghindari kelas masalah itu di masa mendatang