32 poin oleh xguru 2024-06-27 | Belum ada komentar. | Bagikan ke WhatsApp
  • Database Postgres menggunakan RAM dalam jumlah besar. Saat membuat result set, Postgres melewati tahapan seperti pencocokan indeks, pencarian baris terkait dari tabel, serta penggabungan/pemfilteran/agregasi/pengurutan tuple, dan semua tahapan ini bergantung pada memori
  • Untuk mengoptimalkan penggunaan memori Postgres, kita perlu memanfaatkan RAM yang tersedia semaksimal mungkin sambil menyeimbangkan berbagai jenis alokasi memori secara efisien, serta mencegah OS mematikan proses karena penggunaan memori yang berlebihan

Berbagi Itu Peduli

  • Porsi RAM terbesar yang terkait dengan Postgres disebut shared_buffers, yang merepresentasikan baris dari semua tabel dan indeks yang paling sering diambil. Ini didukung oleh heuristik yang memberi skor berdasarkan frekuensi penggunaan
    • shared_buffers adalah nilai tetap yang dialokasikan saat Postgres dijalankan, sehingga tidak berkontribusi pada masalah memori yang tidak terduga
    • Nilai bawaannya adalah 128MB
    • Namun OS mungkin tidak menganggapnya sebagai memori yang telah dialokasikan sebelumnya, sehingga menetapkan nilai yang sangat tinggi hingga sebesar RAM instance bisa berisiko
  • Rekomendasi paling umum untuk shared_buffers pada sistem produksi adalah 25% dari RAM yang tersedia. Ini diskalakan sesuai perangkat keras, sehingga menjadi titik awal yang cocok untuk sebagian besar sistem
    • Hasil benchmark menunjukkan saran 25% umumnya cukup baik, tetapi hasilnya dapat berbeda tergantung cara database digunakan
    • Sebagai contoh, sistem pelaporan sering memiliki cache hit rate yang rendah karena kueri ad hoc yang kompleks, sehingga justru menunjukkan performa yang sedikit lebih baik dengan pengaturan yang lebih rendah
  • Dengan ekstensi pg_buffercache, kita bisa mengetahui secara akurat tabel dan indeks apa saja yang dialokasikan ke shared buffer. Kita juga bisa menyesuaikan nilai shared_buffers dengan melihat jumlah halaman buffer yang digunakan
    • Jika buffer cache tidak terpakai 100%, pengaturannya mungkin terlalu tinggi, sehingga ukuran instance atau nilai shared_buffers bisa dikurangi
    • Jika terpakai 100% dan hanya sebagian dari banyak tabel yang tersimpan di cache, mungkin menguntungkan untuk menaikkan nilainya secara bertahap sampai mulai terjadi diminishing returns
  • View baru pg_stat_io di Postgres 16 juga dapat membantu menyesuaikan shared_buffers. Kita bisa melihat hit rate serta pembacaan/penulisan backend klien
    • Jika rasio baca terhadap tulis mendekati 1, itu bisa menandakan Postgres terus memutar halaman yang sama di shared_buffers. Menambah shared_buffers dapat membantu mengurangi thrashing seperti ini
  • Jika mulai melewati 50% RAM sistem, pertimbangkan untuk menaikkan ukuran instance. Postgres masih membutuhkan memori untuk sesi pengguna dan kueri terkait

Memori Kerja

  • Separuh lain dari memori yang digunakan Postgres untuk benar-benar melakukan pekerjaan adalah working memory yang dikendalikan oleh parameter work_mem
    • Nilai bawaannya 4MB, dan ini adalah salah satu nilai pertama yang biasanya diubah pengguna untuk mempercepat eksekusi kueri
    • Namun jika OS mematikan Postgres dengan pesan "kehabisan memori", Anda mungkin tergoda untuk menaikkan work_mem, padahal itu hanya akan memperburuk masalah. RAM yang digunakan Postgres akan meningkat, sehingga kemungkinan proses dimatikan justru makin tinggi
  • Banyak orang menafsirkan "working memory" sebagai satu alokasi tunggal untuk semua pekerjaan yang dilakukan Postgres saat menjalankan kueri, padahal kenyataannya bisa lebih dari itu
    • Setiap tahap (node) mendapat instance work_mem yang terpisah. Misalnya, jika menggunakan nilai default work_mem 4MB, kueri yang membutuhkan 4 node bisa menghabiskan hingga 16MB RAM
    • Pada server sibuk, jika ada 100 kueri seperti ini berjalan bersamaan, perhitungan hasil saja bisa menggunakan hingga 1,6GB RAM. Kueri yang lebih kompleks bisa memerlukan RAM lebih besar tergantung jumlah node yang dibutuhkan saat eksekusi
  • Dengan perintah EXPLAIN, kita bisa melihat execution plan sebuah kueri, termasuk cara Postgres mengeksekusinya dan semua node yang diperlukan untuk menghasilkan output
    • Jika digunakan bersama ekstensi pg_stat_statements, kita bisa mengisolasi kueri yang paling aktif dan memperkirakan total penggunaan memori akibat work_mem
  • Jika work_mem disetel terlalu rendah, baris atau hasil antara yang tidak muat di RAM akan spill ke disk, yang jauh lebih lambat
    • Kita bisa memeriksa view pg_stat_database untuk melihat ukuran kumulatif dan jumlah semua file sementara yang ditulis ke disk, lalu jika ukuran rata-ratanya masuk akal, work_mem dapat dinaikkan sebesar jumlah itu
  • Untuk memperkirakan jumlah RAM yang tersedia per sesi, gunakan rumus berikut: (80% dari total RAM - shared_buffers) / (max_connections)
    • Misalnya, jika ada RAM 16GB, shared buffer 4GB, dan maksimum 100 koneksi, maka tersedia sekitar 88MB per sesi
    • Nilai ini dapat dibagi dengan jumlah rata-rata node dalam query plan untuk mendapatkan pengaturan work_mem yang baik

Pemeliharaan Berkelanjutan

  • Bagian terakhir dari penggunaan RAM Postgres yang bisa dituning mirip dengan working memory tetapi khusus untuk pemeliharaan, dengan nama parameter maintenance_work_mem
    • Nilai bawaannya 64MB, dan menentukan jumlah RAM yang didedikasikan untuk operasi seperti VACUUM, CREATE INDEX, dan ALTER TABLE ADD FOREIGN KEY
  • Karena dibatasi satu pekerjaan per sesi dan kecil kemungkinan banyak pekerjaan berjalan bersamaan, penggunaan nilai yang lebih tinggi umumnya dianggap cukup aman
    • Tugas pemeliharaan ini bisa menggunakan memori sangat besar, dan jika dapat berjalan sepenuhnya di RAM, penyelesaiannya bisa jauh lebih cepat, sehingga pengaturan 1GB atau 2GB sangat umum
  • Catatan penting di sini adalah proses autovacuum Postgres yang menandai tuple mati agar dapat digunakan kembali nanti
    • Autovacuum memulai pekerjaan latar belakang hingga batas autovacuum_max_workers, dan masing-masing dapat menggunakan satu instance penuh dari maintenance_work_mem
    • Server dengan RAM longgar umumnya aman dengan maintenance working memory 1GB, tetapi jika RAM terbatas Anda harus lebih berhati-hati
    • Ada juga parameter terpisah autovacuum_work_mem jika ingin membatasi worker autovacuum secara khusus
    • Worker autovacuum Postgres tidak dapat menggunakan lebih dari 1GB, jadi mengatur autovacuum_work_mem di atas nilai itu tidak memberikan efek apa pun

Session Pooling

  • Cara termudah untuk mengurangi konsumsi memori adalah memberi batas logis pada alokasi potensial
    • Postgres saat ini adalah engine berbasis proses, jadi setiap sesi pengguna dialokasikan proses fisik, bukan thread
    • Karena itu, setiap koneksi membawa overhead RAM tertentu dan menambah context switching
    • Akibatnya, rekomendasi umum adalah menetapkan max_connections tidak lebih dari 4 kali jumlah thread CPU yang tersedia. Ini meminimalkan waktu yang dihabiskan untuk memindahkan sesi aktif antar-CPU dan secara alami membatasi total RAM yang dapat dikonsumsi sesi
  • Jika semua sesi sedang menjalankan kueri dan setiap node merepresentasikan satu alokasi work_mem, maka penggunaan working memory maksimum teoritis adalah connections * nodes * work_mem
    • Mengurangi kompleksitas kueri tidak selalu memungkinkan, tetapi biasanya jumlah koneksi bisa dikurangi
    • Jika aplikasi selalu membuka sejumlah sesi yang besar atau beberapa microservice terpisah bergantung pada Postgres, ini mungkin tidak semudah kedengarannya
  • Rumus work_mem * max_connections * 5 adalah estimasi kasar jumlah RAM maksimum yang dapat dialokasikan instance Postgres ke sesi pengguna untuk menangani kueri dasar dengan asumsi semua koneksi aktif
    • Jika server tidak memiliki RAM yang cukup untuk nilai ini, pertimbangkan untuk mengurangi salah satu faktornya atau menambah RAM
    • Estimasi 5 node per kueri rata-rata mungkin tidak cocok untuk aplikasi Anda, jadi perlu disesuaikan setelah lebih memahami execution plan kueri
  • Langkah berikutnya adalah memperkenalkan connection pooler seperti PgBouncer
    • Ini memisahkan koneksi klien dari database dan menggunakan kembali sesi Postgres yang mahal antar-klien
    • Jika dikonfigurasi dengan benar, ratusan klien dapat berbagi puluhan koneksi Postgres tanpa memengaruhi aplikasi
    • PgBouncer terbukti dapat memultipleks lebih dari 1000 koneksi menjadi 40-50 koneksi dengan cara ini, sehingga secara signifikan mengurangi konsumsi memori total akibat overhead proses

Mengurangi Bloat

  • Aspek yang mungkin paling sulit dilacak dalam penggunaan memori adalah bloat tabel
    • Postgres menggunakan multi-version concurrency control (MVCC) untuk merepresentasikan data dalam sistem penyimpanannya
    • Artinya, setiap kali baris tabel diubah, Postgres membuat salinan lain dari baris itu di suatu tempat dalam tabel dan menandainya dengan nomor versi baru
    • Proses VACUUM Postgres menandai versi baris lama sebagai ruang "tidak terpakai" agar versi baris baru dapat ditempatkan di sana
  • Postgres memiliki proses latar belakang autovacuum yang terus mencari alokasi yang bisa dipakai ulang agar tabel tidak terus membesar tanpa batas
    • Namun kadang-kadang, terutama pada sistem besar, konfigurasi bawaannya mungkin tidak cukup dan pemeliharaan ini bisa tertinggal
    • Akibatnya tabel bisa terisi lebih banyak baris mati daripada baris hidup, menghasilkan tabel yang "membengkak" oleh data lama
  • Jika sebuah tabel sangat membengkak, dampaknya terhadap shared buffer perlu dipertimbangkan
    • Jika setiap halaman hanya berisi satu baris aktif dan beberapa baris mati, maka ketika kueri membutuhkan 10 baris tertentu, 10 halaman harus dimuat ke shared buffer, sehingga banyak memori terbuang yang seharusnya bisa digunakan untuk keperluan lain
    • Jika permintaan terhadap baris-baris itu sangat tinggi, frekuensi penggunaannya dapat membuatnya tetap berada di shared buffer dan menurunkan efisiensi cache secara signifikan
  • Ada banyak kueri di internet yang mengklaim bisa memperkirakan bloat tabel, tetapi satu-satunya cara untuk benar-benar melihat seperti apa halaman tabel adalah dengan menggunakan ekstensi pgstattuple
  • Jika free_percent lebih besar dari 30%, Anda mungkin perlu membuat autovacuum lebih agresif. Jika jauh di atas 30%, lebih baik menghilangkan bloat sepenuhnya
    • Saat ini satu-satunya metode yang didukung untuk itu adalah perintah VACUUM FULL, yang pada dasarnya membangun ulang tabel. Ini memindahkan semua baris aktif ke lokasi baru dan membuang salinan lama yang sudah membengkak
    • Proses ini memberikan kunci akses eksklusif selama berjalan, sehingga dalam hampir semua kasus akan memerlukan semacam downtime
  • Alternatifnya adalah ekstensi pg_repack yang didukung oleh Tembo
    • Alat command-line ini dapat mereorganisasi tabel untuk menghilangkan bloat sepenuhnya secara online tanpa kunci eksklusif
    • Karena alat ini berada di luar inti Postgres dan memodifikasi penyimpanan tabel serta indeks, alat ini sering dianggap penggunaan tingkat lanjut
    • Disarankan untuk mengujinya secara memadai di lingkungan non-produksi sebelum digunakan
  • Anda bahkan bisa melangkah lebih jauh dengan menata ulang urutan kolom untuk memaksimalkan jumlah baris per halaman, semacam permainan tetris kolom
    • Ini mungkin termasuk optimasi yang sangat ekstrem, tetapi dalam lingkungan yang memberi kebebasan membangun ulang tabel dengan cara ini, strategi tersebut tetap layak dipertimbangkan

Tindakan Menyeimbangkan

  • Mengonfigurasi semua parameter dan sumber daya ini dengan benar adalah perpaduan antara seni dan sains
    • Kita telah melihat cara mengukur penggunaan nyata shared buffer dan cara memeriksa apakah working memory terlalu rendah
    • Tetapi seperti pada kebanyakan hal, bagaimana jika perangkat keras atau anggaran yang tersedia terbatas? Di sinilah sisi "seni" diperlukan
  • Dalam kondisi kekurangan memori, Anda mungkin perlu sedikit mengurangi shared_buffers untuk memberi ruang lebih besar bagi work_mem. Atau mungkin keduanya perlu diturunkan
    • Jika aplikasi membutuhkan jumlah sesi yang besar, mungkin lebih masuk akal untuk menurunkan work_mem atau memperkenalkan connection pool guna mencegah sesi bersamaan menumpuk alokasi RAM yang luas
    • Jika di masa lalu Anda menaikkan maintenance_work_mem dengan asumsi RAM cukup untuk semuanya, mungkin sekarang lebih masuk akal untuk menurunkannya. Banyak hal yang perlu dipertimbangkan
  • Pada instance dengan memori rendah, bahkan rekomendasi di atas mungkin belum cukup. Dalam situasi seperti ini, sebaiknya ikuti urutan kerja berikut untuk memaksimalkan penggunaan memori dan menghindari kehabisan resource:
    1. Tambahkan connection pooler dan turunkan max_connections. Ini adalah cara tercepat dan termudah untuk menurunkan konsumsi resource maksimum
    2. Gunakan EXPLAIN pada kueri yang paling sering muncul menurut pg_stat_statements untuk menemukan jumlah node maksimum pada kueri, bukan rata-ratanya. Lalu setel work_mem ke nilai tidak lebih dari (80% dari total RAM - shared_buffers) / (max_connections * jumlah maksimum plan node)
    3. Kembalikan maintenance_work_mem dan autovacuum_work_mem ke nilai default 64MB. Jika tugas pemeliharaan terlalu lambat dan lebih banyak RAM masih tersedia, pertimbangkan menaikkannya dalam kelipatan 8MB
    4. Gunakan ekstensi pg_buffercache untuk melihat jumlah tabel yang disimpan di shared_buffers. Tinjau setiap tabel dan indeks dengan cermat, lalu cari cara untuk menguranginya, misalnya dengan mengarsipkan data atau mengubah kueri agar memakai informasi lebih sedikit. Ini bisa mencakup VACUUM FULL atau pg_repack untuk memadatkan halaman yang digunakan tabel bloat aktif
    5. Jika pg_buffercache menunjukkan shared_buffers sudah penuh dan tak bisa lagi dikurangi tanpa menghapus halaman aktif, gunakan kolom usagecount untuk memprioritaskan halaman yang paling aktif. Karena nilai kolom ini berada pada rentang 1-5, berfokus pada halaman yang digunakan 3-5 kali dapat membantu mengurangi shared_buffers tanpa dampak besar pada performa
    6. Terakhir, sediakan perangkat keras yang lebih kuat. Jika database membutuhkan lebih banyak RAM untuk workload saat ini dan menurunkan parameter di atas akan terlalu merusak performa sistem, biasanya upgrade adalah pilihan yang lebih masuk akal

Belum ada komentar.

Belum ada komentar.