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

Ketika autovacuum tidak vakum

Beberapa minggu yang lalu saya menjelaskan dasar-dasar penyetelan autovacuum. Di akhir posting itu saya berjanji untuk segera mengatasi masalah dengan menyedot debu. Yah, butuh sedikit lebih lama dari yang saya rencanakan, tapi ini dia.

Untuk rekap cepat, autovacuum adalah proses latar belakang membersihkan baris mati, mis. versi baris lama yang dihapus. Anda juga dapat melakukan pembersihan secara manual dengan menjalankan VACUUM , tapi autovacuum melakukannya secara otomatis tergantung pada jumlah baris mati dalam tabel, pada saat yang tepat – tidak terlalu sering tetapi cukup sering untuk menjaga jumlah “sampah” tetap terkendali.

Secara umum, autovacuum tidak dapat berjalan terlalu sering – pembersihan hanya dilakukan setelah mencapai beberapa jumlah baris mati yang terakumulasi dalam tabel. Tapi mungkin tertunda karena berbagai alasan, sehingga tabel dan indeks menjadi lebih besar dari yang diinginkan. Dan itulah topik posting ini. Jadi apa penyebab umum dan bagaimana mengidentifikasi mereka?

Pembatasan

Sebagaimana dijelaskan dalam dasar-dasar penyetelan, autovacuum pekerja dibatasi untuk hanya melakukan sejumlah pekerjaan per interval waktu. Batas defaultnya cukup rendah – sekitar 4MB/dtk penulisan, 8MB/dtk membaca. Itu cocok untuk mesin kecil seperti Raspberry Pi atau server kecil dari 10 tahun yang lalu, tetapi mesin saat ini jauh lebih kuat (baik dalam hal CPU dan I/O) dan menangani lebih banyak data.

Bayangkan Anda memiliki beberapa meja besar dan beberapa meja kecil. Jika ketiga autovacuum pekerja mulai membersihkan meja besar, tidak ada meja kecil yang akan disedot terlepas dari jumlah baris mati yang mereka kumpulkan. Mengidentifikasi ini tidak terlalu sulit, dengan asumsi Anda memiliki pemantauan yang cukup. Cari periode ketika semua autovacuum pekerja sibuk sementara meja tidak disedot meskipun banyak baris mati menumpuk.

Semua informasi yang diperlukan ada di pg_stat_activity (jumlah autovacuum proses pekerja) dan pg_stat_all_tables (last_autovacuum dan n_dead_tup ).

Meningkatkan jumlah autovacuum pekerja bukanlah solusi, karena jumlah total pekerjaan tetap sama. Anda dapat menentukan batas pembatasan per tabel, mengecualikan pekerja tersebut dari batas total, tetapi itu tetap tidak menjamin akan ada pekerja yang tersedia saat dibutuhkan.

Solusi yang tepat adalah menyetel pelambatan, menggunakan batas yang wajar sehubungan dengan konfigurasi perangkat keras dan pola beban kerja. Beberapa rekomendasi pelambatan dasar disebutkan di posting sebelumnya. (Jelas, jika Anda dapat mengurangi jumlah baris mati yang dihasilkan dalam database, itu akan menjadi solusi yang ideal.)

Dari titik ini, kami akan menganggap pelambatan bukanlah masalahnya, yaitu autovacuum pekerja tidak jenuh untuk jangka waktu yang lama, dan pembersihan dipicu di semua tabel tanpa penundaan yang tidak masuk akal.

Transaksi panjang

Nah, kalau meja divakum secara rutin, tentunya tidak bisa menumpuk banyak dead row, kan? Sayangnya tidak ada. Baris sebenarnya tidak "dapat dihapus" segera setelah dihapus, tetapi hanya jika tidak ada transaksi yang mungkin melihatnya. Perilaku yang tepat tergantung pada apa yang dilakukan transaksi lain dan tingkat serialisasi, tetapi secara umum:

BACA KOMITMEN

  • menjalankan pembersihan blok kueri
  • pembersihan blok transaksi yang tidak aktif hanya jika mereka melakukan penulisan
  • transaksi yang tidak aktif (tanpa penulisan apa pun) tidak akan memblokir pembersihan (tapi tetap saja bukan praktik yang baik untuk menyimpannya)

DAPAT DISERIALISASI

  • menjalankan pembersihan blok kueri
  • pembersihan blok transaksi menganggur (meskipun hanya membaca)

Dalam praktiknya tentu saja lebih bernuansa, tetapi menjelaskan semua bit yang berbeda akan membutuhkan penjelasan terlebih dahulu bagaimana XID dan snapshot bekerja, dan itu bukan tujuan dari posting ini. Apa yang harus Anda ambil dari sini adalah bahwa transaksi panjang adalah ide yang buruk, terutama jika transaksi tersebut mungkin dilakukan secara tertulis.

Tentu saja, ada alasan yang sangat valid mengapa Anda mungkin perlu menyimpan transaksi untuk jangka waktu yang lama (misalnya jika Anda perlu memastikan ACID untuk semua perubahan). Tetapi pastikan itu tidak terjadi secara tidak perlu, mis. karena desain aplikasi yang buruk.

Konsekuensi yang agak tidak terduga dari hal ini adalah penggunaan CPU dan I/O yang tinggi, karena autovacuum berjalan berulang-ulang, tanpa membersihkan baris mati (atau hanya beberapa dari mereka). Karena itu tabel masih memenuhi syarat untuk dibersihkan di babak berikutnya, menyebabkan lebih banyak kerugian daripada kebaikan.

Bagaimana cara mendeteksi ini? Pertama, Anda perlu memantau transaksi yang berjalan lama, terutama yang menganggur. Yang perlu Anda lakukan hanyalah membaca data dari pg_stat_activity . Definisi tampilan sedikit berubah dengan versi PostgreSQL, jadi Anda mungkin perlu mengubahnya sedikit:

SELECT xact_start, state FROM pg_stat_activity;

-- count 'idle' transactions longer than 15 minutes (since BEGIN)
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - xact_start) > interval '15 minutes'

-- count transactions 'idle' for more than 5 minutes
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - state_change) > interval '5 minutes'

Anda juga dapat menggunakan beberapa plugin pemantauan yang ada, mis. check_postgres.pl. Itu sudah termasuk jenis pemeriksaan kewarasan ini. Anda harus memutuskan berapa lama transaksi/kueri yang wajar, yang khusus untuk aplikasi.

Sejak PostgreSQL 9.6 Anda juga dapat menggunakan idle_in_transaction_session_timeout sehingga transaksi idle terlalu lama dihentikan secara otomatis. Demikian pula, untuk kueri panjang ada statement_timeout .

Hal lain yang berguna adalah VACUUM VERBOSE yang sebenarnya akan memberi tahu Anda berapa banyak baris mati yang belum dapat dihapus:

db=# VACUUM verbose z;
INFO:  vacuuming "public.z"
INFO:  "z": found 0 removable, 66797 nonremovable row versions in 443 out of 443 pages
DETAIL:  12308 dead row versions cannot be removed yet.
...

Ini tidak akan memberi tahu Anda backend mana yang mencegah pembersihan, tetapi ini adalah tanda yang cukup jelas tentang apa yang terjadi.

Catatan: . Anda tidak dapat dengan mudah mendapatkan informasi ini dari autovacuum karena hanya login dengan DEBUG2 secara default (dan Anda pasti tidak ingin menjalankan dengan level log itu dalam produksi).

Permintaan panjang saat siaga panas

Mari kita asumsikan tabel sedang disedot pada waktu yang tepat, tetapi tidak menghapus tupel mati, menghasilkan tabel dan indeks mengasapi. Anda sedang memantau pg_stat_activity dan tidak ada transaksi yang berjalan lama. Apa yang mungkin menjadi masalah?

Jika Anda memiliki replika streaming, kemungkinan masalahnya ada di sana. Jika replika menggunakan hot_standby_feedback=on , kueri pada replika bertindak hampir sama dengan transaksi pada primer, termasuk pembersihan pemblokiran. Tentu saja, hot_standby_feedback=on digunakan persis saat menjalankan kueri panjang (misalnya, beban kerja analitik dan BI) pada replika, untuk mencegah pembatalan karena konflik replikasi.

Sayangnya, Anda harus memilih – tetap hot_standby_feedback=on dan menerima penundaan dalam pembersihan, atau menangani kueri yang dibatalkan. Anda juga dapat menggunakan max_standby_streaming_delay untuk membatasi dampak, meskipun itu tidak mencegah pembatalan sepenuhnya (jadi Anda masih perlu mencoba lagi kueri).

Sebenarnya, ada opsi ketiga sekarang – replikasi logis. Alih-alih menggunakan replikasi streaming fisik untuk replika BI, Anda dapat menyalin perubahan menggunakan replikasi logis baru, tersedia di PostgreSQL 10. Replikasi logis melonggarkan sambungan antara primer dan replika, dan membuat sebagian besar cluster independen (dibersihkan secara independen, dll.).

Ini memecahkan dua masalah yang terkait dengan replikasi streaming fisik – pembersihan tertunda pada kueri utama atau yang dibatalkan pada replika BI. Untuk replika yang melayani tujuan DR, replikasi streaming tetap menjadi pilihan yang tepat. Namun replika tersebut tidak (atau tidak seharusnya) menjalankan kueri yang panjang.

Catatan: Sementara saya menyebutkan bahwa replikasi logis akan tersedia di PostgreSQL 10, sebagian besar infrastruktur tersedia di rilis sebelumnya (terutama PostgreSQL 9.6). Jadi, Anda mungkin dapat melakukan ini bahkan pada rilis yang lebih lama (kami melakukannya untuk beberapa pelanggan kami), tetapi PostgreSQL 10 akan membuatnya jauh lebih nyaman dan nyaman.

Masalah dengan autoanalyze

Detail yang mungkin Anda lewatkan adalah autovacuum pekerja benar-benar melakukan dua tugas yang berbeda. Pertama-tama pembersihan (seolah-olah menjalankan VACUUM ), tetapi juga mengumpulkan statistik (seolah-olah menjalankan ANALYZE ). Dan keduanya bagian dicekik menggunakan autovacuum_cost_limit .

Tapi ada perbedaan besar dalam menangani transaksi. Kapan pun VACUUM bagian mencapai autovacuum_cost_limit , pekerja melepaskan snapshot dan tidur sebentar. ANALYZE namun harus dijalankan dalam satu snapshot/transaksi, yang melakukannya blokir pembersihan.

Ini adalah cara yang elegan untuk menembak kaki Anda sendiri, terutama jika Anda juga melakukan beberapa hal berikut:

  • tambahkan default_statistics_target untuk membuat statistik yang lebih akurat dari sampel yang lebih besar
  • bawah autovacuum_analyze_scale_factor untuk mengumpulkan statistik lebih sering

Konsekuensi yang tidak diinginkan tentu saja adalah ANALYZE akan terjadi lebih sering, akan memakan waktu lebih lama dan akan (tidak seperti VACUUM bagian) mencegah pembersihan. Solusinya biasanya cukup sederhana – jangan turunkan autovacuum_analyze_scale_factor terlalu banyak. Menjalankan ANALYZE setiap kali 10% dari tabel perubahan harus lebih dari cukup dalam banyak kasus.

n_dead_tup

Satu hal terakhir yang ingin saya sebutkan adalah tentang perubahan pg_stat_all_tables.n_dead_tup nilai-nilai. Anda mungkin berpikir bahwa nilainya adalah penghitung sederhana, bertambah setiap kali tupel mati baru dibuat dan dikurangi setiap kali dibersihkan. Tapi itu sebenarnya hanya perkiraan jumlah tupel mati, diperbarui oleh ANALYZE . Untuk tabel kecil (kurang dari 240MB) perbedaannya tidak terlalu besar, karena ANALYZE membaca seluruh tabel sehingga cukup tepat. Namun untuk tabel besar, ini mungkin sedikit berubah tergantung pada subset tabel apa yang diambil sampelnya. Dan menurunkan autovacuum_vacuum_scale_factor membuatnya lebih acak.

Jadi berhati-hatilah saat melihat n_dead_tup dalam sistem pemantauan. Penurunan atau peningkatan nilai yang tiba-tiba mungkin hanya karena ANALYZE menghitung ulang perkiraan yang berbeda, dan bukan karena pembersihan aktual dan/atau tupel mati baru yang muncul di tabel.

Ringkasan

Untuk meringkas ini menjadi beberapa poin sederhana:

  • autovacuum hanya dapat berfungsi jika tidak ada transaksi yang mungkin memerlukan tupel mati.
  • Kueri yang berjalan lama memang memblokir pembersihan. Pertimbangkan untuk menggunakan statement_timeout untuk membatasi kerusakan.
  • Transaksi yang berjalan lama dapat memblokir pembersihan. Perilaku yang tepat tergantung pada hal-hal seperti tingkat isolasi atau apa yang terjadi dalam transaksi. Pantau dan hentikan jika memungkinkan.
  • Kueri yang berjalan lama pada replika dengan hot_standby_feedback=on juga dapat memblokir pembersihan.
  • autoanalyze juga dicekik, tetapi tidak seperti VACUUM bagian itu menyimpan satu snapshot (dan dengan demikian memblokir pembersihan).
  • n_dead_tup hanyalah perkiraan yang dikelola oleh ANALYZE , jadi perkirakan beberapa fluktuasi (terutama pada tabel besar).

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Menggunakan Otomatisasi untuk Mempercepat Tes Rilis di PostgreSQL

  2. Bagaimana Anda membuat string acak yang cocok untuk ID sesi di PostgreSQL?

  3. SQL:Pilih catatan di mana SEMUA catatan yang digabungkan memenuhi beberapa kondisi

  4. Cara Menghapus Trailing Zeros dari Desimal di PostgreSQL

  5. Postgres homebrew rusak