MVCC dalam PostgreSQL-7. Autovacuum

Untuk mengingatkan Anda, kami mulai dengan masalah yang berkaitan dengan isolasi , melakukan penyimpangan tentang struktur data tingkat rendah , membahas versi baris secara terperinci dan mengamati bagaimana snapshot data diperoleh dari versi baris.

Lalu kami menjelajahi vakum halaman (dan pembaruan HOT) dan vakum . Sekarang kita akan melihat autovacuum.

Autovacuum


Kami sudah menyebutkan bahwa biasanya (yaitu, ketika tidak ada yang memegang cakrawala transaksi untuk waktu yang lama) VACUUM biasanya melakukan tugasnya. Masalahnya adalah seberapa sering menyebutnya.

Jika kita menyedot meja ganti terlalu jarang, ukurannya akan tumbuh lebih dari yang diinginkan. Selain itu, operasi vakum berikutnya mungkin memerlukan beberapa melewati indeks jika terlalu banyak perubahan dilakukan.

Jika kita menyedot meja terlalu sering, server akan terus melakukan pemeliharaan daripada pekerjaan yang bermanfaat - dan ini juga tidak baik.

Perhatikan bahwa meluncurkan VACUUM sesuai jadwal tidak berarti menyelesaikan masalah karena beban kerja dapat berubah seiring waktu. Jika tabel mulai berubah lebih intensif, itu harus disedot lebih sering.

Autovacuum adalah teknik tepat yang memungkinkan kami meluncurkan penyedotan tergantung pada seberapa intensif perubahan tabelnya.

Ketika autovacuum dihidupkan (set parameter konfigurasi autovacuum diatur), proses daemon peluncur autovacuum dimulai, yang merencanakan pekerjaan. Vakum sendiri dilakukan oleh proses pekerja autovacuum , beberapa contoh yang dapat berjalan secara paralel.

Proses peluncur autovacuum menyusun daftar basis data tempat aktivitas apa pun terjadi. Aktivitas ditentukan dari statistik, dan untuk mengumpulkannya, parameter track_counts harus ditetapkan. Jangan pernah mematikan autovacuum dan track_counts , jika tidak, fitur autovacuum tidak akan berfungsi.

Setelah setiap detik autovacuum_naptime , peluncur autovacuum dimulai (menggunakan proses postmaster ) proses pekerja untuk setiap database dalam daftar. Dengan kata lain, jika ada beberapa aktivitas dalam database, proses pekerja akan dikirim ke sana pada interval detik autovacuum_naptime . Untuk tujuan ini, jika beberapa (N) database aktif tersedia, proses pekerja diluncurkan N kali sesering setiap detik autovacuum_naptime . Tetapi jumlah total proses pekerja yang berjalan bersamaan dibatasi oleh parameter autovacuum_max_workers .

Ketika dimulai, proses pekerja terhubung ke database yang ditugaskan untuknya dan mulai dengan membuat daftar:

  • Semua tabel, tampilan terwujud dan tabel TOAST yang membutuhkan penghisap debu.
  • Semua tabel dan tampilan terwujud yang memerlukan analisis (tabel TOAST tidak dianalisis karena selalu dijangkau dengan akses indeks).

Kemudian pekerja memproses vakuum dan / atau menganalisis objek pada daftar satu per satu dan selesai saat menyedot debu selesai.

Jika proses tidak dapat melakukan semua pekerjaan yang direncanakan dalam detik autovacuum_naptime , proses peluncur autovacuum akan mengirimkan satu proses pekerja lagi ke database ini, dan mereka akan bekerja bersama. "Bersama" hanya berarti bahwa proses kedua akan membangun daftar sendiri dan bekerja melewatinya. Jadi, hanya tabel yang berbeda yang akan diproses secara paralel, tetapi tidak ada paralelisme di tingkat satu tabel - jika salah satu proses pekerja sudah menangani tabel, proses lain akan melewatkannya dan melanjutkan lebih jauh.

Sekarang mari kita perjelas lebih rinci apa yang dimaksud dengan "memerlukan penghisap debu" dan "memerlukan analisis".

Baru-baru ini patch dilakukan yang memungkinkan vakum untuk memproses indeks secara paralel dengan pekerja latar belakang.

Tabel apa yang membutuhkan penghisap debu?


Penyedotan debu dianggap diperlukan jika jumlah tupel yang mati (yaitu kedaluwarsa) melebihi ambang batas yang ditentukan. Kolektor statistik secara permanen melacak jumlah tupel mati, yang disimpan dalam tabel pg_stat_all_tables . Dan dua parameter menentukan ambang batas:

  • autovacuum_vacuum_threshold mendefinisikan nilai absolut (jumlah tupel).
  • autovacuum_vacuum_scale_factor mendefinisikan pembagian baris dalam tabel.

Singkatnya: menyedot debu diperlukan jika pg_stat_all_tables.n_dead_tup > = autovacuum_vacuum_threshold + autovacuum_vacuum_valeum_scale_factor * pg_class.reltupes .

Dengan pengaturan default, autovacuum_vacuum_threshold = 50 dan autovacuum_vacuum_scale_factor = 0.2. autovacuum_vacuum_scale_factor , tentu saja, yang paling penting di sini - parameter ini yang sangat penting untuk tabel besar (dan itu adalah mereka yang mungkin terkait dengan masalah). Nilai 20% tampaknya terlalu tinggi, dan kemungkinan besar itu harus sangat dikurangi.

Nilai optimal dari parameter dapat bervariasi untuk tabel yang berbeda dan tergantung pada ukuran tabel dan spesifikasi perubahan. Masuk akal untuk menetapkan nilai yang umumnya sesuai dan, jika perlu, lakukan penyesuaian khusus pada parameter pada tingkat tabel tertentu dengan menggunakan parameter penyimpanan:

  • autovacuum_vacuum_threshold dan bersulang.autovacuum_vacuum_threshold .
  • autovacuum_vacuum_scale_factor dan bersulang.autovacuum_vacuum_scale_factor .

Untuk menghindari kebingungan, ini masuk akal untuk dilakukan hanya untuk beberapa tabel yang dibedakan di antara yang lainnya dengan jumlah dan intensitas perubahan dan hanya ketika nilai yang ditetapkan secara global gagal berfungsi dengan baik.

Selain itu, Anda dapat mematikan autovacuum di tingkat meja (meskipun kami hampir tidak dapat memikirkan alasan mengapa itu bisa diperlukan):

  • autovacuum_enabled dan bersulang.autovacuum_enabled .

Misalnya, terakhir kali kami membuat tabel vac dengan autovacuum dimatikan untuk mengontrol penyedotan debu secara manual untuk tujuan demo. Parameter penyimpanan dapat diubah sebagai berikut:

 => ALTER TABLE vac SET (autovacuum_enabled = off); 

Untuk memformalkan semua hal di atas, mari kita buat tampilan yang menunjukkan tabel mana yang perlu disedot pada saat ini. Ini akan menggunakan fungsi yang mengembalikan nilai parameter saat ini dan memperhitungkan bahwa nilai tersebut dapat didefinisikan ulang di tingkat tabel:

 => CREATE FUNCTION get_value(param text, reloptions text[], relkind "char") RETURNS float AS $$ SELECT coalesce( -- if the storage parameter is set, we take its value (SELECT option_value FROM pg_options_to_table(reloptions) WHERE option_name = CASE -- for TOAST tables, the parameter name differs WHEN relkind = 't' THEN 'toast.' ELSE '' END || param ), -- otherwise, we take the value of the configuration parameter current_setting(param) )::float; $$ LANGUAGE sql; 

Dan inilah pandangannya:

 => CREATE VIEW need_vacuum AS SELECT st.schemaname || '.' || st.relname tablename, st.n_dead_tup dead_tup, get_value('autovacuum_vacuum_threshold', c.reloptions, c.relkind) + get_value('autovacuum_vacuum_scale_factor', c.reloptions, c.relkind) * c.reltuples max_dead_tup, st.last_autovacuum FROM pg_stat_all_tables st, pg_class c WHERE c.oid = st.relid AND c.relkind IN ('r','m','t'); 

Tabel apa yang membutuhkan analisis?


Situasi dengan analisis otomatis serupa. Tabel-tabel tersebut dianggap memerlukan analisis yang jumlah tupel yang diperbarui (sejak analisis terakhir) melebihi ambang yang ditentukan oleh dua parameter yang serupa: pg_stat_all_tables.n_mod_since_analyze > = autovacuum_analyze_threshold + autovacuum_analyze_scale_factor * pg_class.reltupes .

Pengaturan default analisis otomatis agak berbeda: autovacuum_analyze_threshold = 50 dan autovacuum_analyze_scale_factor = 0,1. Mereka juga dapat didefinisikan pada tingkat parameter penyimpanan tabel terpisah:

  • autovacuum_analyze_threshold
  • autovacuum_analyze_scale_factor

Karena tabel TOAST tidak dianalisis, mereka tidak memiliki parameter seperti itu.

Mari kita juga membuat tampilan untuk analisis:

 => CREATE VIEW need_analyze AS SELECT st.schemaname || '.' || st.relname tablename, st.n_mod_since_analyze mod_tup, get_value('autovacuum_analyze_threshold', c.reloptions, c.relkind) + get_value('autovacuum_analyze_scale_factor', c.reloptions, c.relkind) * c.reltuples max_mod_tup, st.last_autoanalyze FROM pg_stat_all_tables st, pg_class c WHERE c.oid = st.relid AND c.relkind IN ('r','m'); 

Contoh


Mari tetapkan nilai parameter berikut untuk percobaan:

 => ALTER SYSTEM SET autovacuum_naptime = '1s'; -- to aviod waiting long => ALTER SYSTEM SET autovacuum_vacuum_scale_factor = 0.03; -- 3% => ALTER SYSTEM SET autovacuum_vacuum_threshold = 0; => ALTER SYSTEM SET autovacuum_analyze_scale_factor = 0.02; -- 2% => ALTER SYSTEM SET autovacuum_analyze_threshold = 0; 
 => SELECT pg_reload_conf(); 
  pg_reload_conf ---------------- t (1 row) 

Sekarang mari kita buat tabel yang mirip dengan yang digunakan terakhir kali dan masukkan seribu baris ke dalamnya. Autovacuum dimatikan di tingkat meja, dan kami akan menyalakannya sendiri. Tanpa ini, contoh-contoh tidak akan dapat direproduksi karena autovacuuming dapat dipicu pada waktu yang buruk.

 => CREATE TABLE autovac( id serial, s char(100) ) WITH (autovacuum_enabled = off); => INSERT INTO autovac SELECT g.id,'A' FROM generate_series(1,1000) g(id); 

Inilah yang akan ditampilkan oleh pandangan kami untuk menyedot debu:

 => SELECT * FROM need_vacuum WHERE tablename = 'public.autovac'; 
  tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+----------------- public.autovac | 0 | 0 | (1 row) 

Perhatian di sini harus diberikan pada dua hal. Pertama, max_dead_tup = 0 walaupun 3% dari 1000 baris menghasilkan 30 baris. Masalahnya adalah kita belum memiliki statistik di atas meja karena INSERT tidak memperbaruinya sendiri. Sampai tabel dianalisis, nol akan tetap ada sejak pg_class.reltuples = 0. Tapi mari kita lihat tampilan kedua untuk analisis:

 => SELECT * FROM need_analyze WHERE tablename = 'public.autovac'; 
  tablename | mod_tup | max_mod_tup | last_autoanalyze ----------------+---------+-------------+------------------ public.autovac | 1000 | 0 | (1 row) 

Karena 1000 baris telah diubah (ditambahkan) dalam tabel, yang lebih besar dari nol, analisis otomatis harus dipicu. Mari kita periksa ini:

 => ALTER TABLE autovac SET (autovacuum_enabled = on); 

Setelah jeda singkat, kita dapat melihat bahwa tabel telah dianalisis dan perbaiki 20 baris ditampilkan dalam max_dead_tup alih-alih nol:

 => SELECT * FROM need_analyze WHERE tablename = 'public.autovac'; 
  tablename | mod_tup | max_mod_tup | last_autoanalyze ----------------+---------+-------------+------------------------------- public.autovac | 0 | 20 | 2019-05-21 11:59:48.465987+03 (1 row) 

 => SELECT reltuples, relpages FROM pg_class WHERE relname = 'autovac'; 
  reltuples | relpages -----------+---------- 1000 | 17 (1 row) 

Mari kita kembali ke autovacuuming:

 => SELECT * FROM need_vacuum WHERE tablename = 'public.autovac'; 
  tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+----------------- public.autovac | 0 | 30 | (1 row) 

Seperti yang bisa kita lihat, max_dead_tup sudah diperbaiki. Hal lain yang perlu diperhatikan adalah dead_tup = 0. Statistik menunjukkan bahwa tabel tidak memiliki tuple mati ..., dan ini benar. Belum ada yang vakum di meja. Setiap tabel yang digunakan secara eksklusif dalam mode append-only tidak akan disedot dan oleh karena itu, peta visibilitas tidak akan diperbarui untuknya. Tetapi hal ini membuat pemindaian hanya-indeks tidak mungkin dilakukan.

(Lain kali kita akan melihat bahwa menyedot debu cepat atau lambat akan mencapai tabel hanya-append, tetapi ini akan terjadi terlalu jarang.)

Pelajaran yang dipetik: jika pemindaian hanya indeks sangat penting, mungkin diperlukan untuk memanggil proses vakum secara manual.

Sekarang mari kita matikan lagi autovacuum dan perbarui 31 baris, yang merupakan satu baris lebih besar dari ambang batas.

 => ALTER TABLE autovac SET (autovacuum_enabled = off); => UPDATE autovac SET s = 'B' WHERE id <= 31; => SELECT * FROM need_vacuum WHERE tablename = 'public.autovac'; 
  tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+----------------- public.autovac | 31 | 30 | (1 row) 

Sekarang kondisi pemicu vakum terpenuhi. Mari aktifkan autovacuum dan setelah jeda singkat kita akan melihat bahwa tabel telah diproses:

 => ALTER TABLE autovac SET (autovacuum_enabled = on); => SELECT * FROM need_vacuum WHERE tablename = 'public.autovac'; 
  tablename | dead_tup | max_dead_tup | last_autovacuum ----------------+----------+--------------+------------------------------- public.autovac | 0 | 30 | 2019-05-21 11:59:52.554571+03 (1 row) 

Muatkan pelambatan


VACUUM tidak memblokir proses lain karena ini bekerja halaman demi halaman, tetapi menghasilkan beban tambahan pada sistem dan secara signifikan dapat mempengaruhi kinerja.

Mencekik untuk vakum


Untuk dapat mengontrol intensitas vakum dan karena itu, efeknya pada sistem, proses bergantian bekerja dan menunggu. Prosesnya akan melakukan tentang vacuum_cost_limit unit kerja konvensional dan kemudian akan tidur untuk ms vacuum_cost_delay .

Pengaturan default adalah vacuum_cost_limit = 200 dan vacuum_cost_delay = 0. Nol terakhir sebenarnya berarti bahwa VACUUM tidak tidur, jadi nilai spesifik dari vacuum_cost_limit tidak masalah sama sekali. Alasan di balik ini adalah bahwa jika seorang administrator memang harus meluncurkan VACUUM secara manual, ia kemungkinan berharap penyedotan debu dilakukan secepat mungkin.

Namun demikian, jika kita mengatur waktu tidur, jumlah pekerjaan yang ditentukan dalam vacuum_cost_limit akan terdiri dari biaya pekerjaan dengan halaman dalam cache buffer. Setiap akses halaman diperkirakan sebagai berikut:

  • Jika halaman ditemukan dalam cache buffer, vacuum_cost_page_hit = 1.
  • Jika tidak ditemukan, vacuum_cost_page_miss = 10.
  • Jika tidak ditemukan dan halaman yang kotor harus digusur dari cache buffer, vacuum_cost_page_dirty = 20.

Artinya, dengan pengaturan default vacuum_cost_limit , 200 halaman cache atau 20 halaman disk atau 10 halaman dengan penggusuran dapat diproses dalam sekali jalan. Jelas bahwa angka-angka ini cukup sementara, tetapi tidak masuk akal untuk memilih yang lebih akurat.

Pembatasan untuk autovacuuming


Untuk proses vakum, pelambatan beban bekerja dengan cara yang sama seperti untuk VACUUM. Tetapi untuk proses autovacuum dan secara manual meluncurkan VACUUM untuk bekerja dengan intensitas yang berbeda, autovacuum memiliki parameter sendiri: autovacuum_vacuum_cost_limit dan autovacuum_vacuum_cost_delay . Jika parameter ini memiliki nilai -1, nilai vacuum_cost_limit dan / atau vacuum_cost_delay digunakan.

Secara default autovacuum_vacuum_cost_limit = -1 ( mis . Nilai vacuum_cost_limit = 200 digunakan) dan autovacuum_vacuum_cost_delay = 20 ms. Pada perangkat keras modern, autovacuum akan sangat lambat.

Dalam versi 12, nilai autovacuum_vacuum_cost_delay dikurangi menjadi 2 ms, yang dapat diambil untuk perkiraan pertama yang lebih tepat.

Selain itu, kita harus mencatat bahwa batas yang ditentukan oleh pengaturan ini adalah umum untuk semua proses pekerja. Dengan kata lain, ketika jumlah proses pekerja simultan berubah, beban keseluruhan tetap tidak berubah. Jadi, untuk meningkatkan kinerja autovacuum, ketika menambahkan proses pekerja, masuk akal juga untuk meningkatkan autovacuum_vacuum_cost_limit .

Penggunaan memori dan pemantauan


Terakhir kali kami mengamati bagaimana VACUUM menggunakan RAM dengan ukuran maintenance_work_mem untuk menyimpan data yang akan disedot.

Autovacuum benar-benar sama. Tetapi bisa ada banyak proses pekerja simultan jika autovacuum_max_workers diatur ke nilai yang besar. Selain itu, semua memori dialokasikan sekaligus daripada saat diperlukan. Oleh karena itu, untuk proses pekerja, batasannya sendiri dapat ditetapkan dengan menggunakan parameter autovacuum_work_mem . Nilai default dari parameter ini adalah -1, yaitu tidak digunakan.

Seperti yang telah disebutkan, VACUUM juga dapat bekerja dengan ukuran memori minimum. Tetapi jika indeks dibuat di atas meja, nilai kecil maintenance_work_mem dapat memerlukan pemindaian indeks berulang. Hal yang sama berlaku untuk autovacuum. Idealnya, autovacuum_work_mem harus memiliki nilai minimum sehingga tidak terjadi pemindaian berulang.

Kami telah melihat bahwa untuk memantau VACUUM, opsi VERBOSE dapat digunakan (yang tidak dapat ditentukan untuk autovacuum) atau tampilan pg_stat_progress_vacuum (yang, bagaimanapun, hanya menampilkan informasi terkini). Oleh karena itu, sarana utama untuk memantau autovacuuming adalah dengan menggunakan parameter log_autovacuum_min_duration , yang menampilkan informasi ke log pesan server. Secara default dimatikan (diatur ke -1). Masuk akal untuk mengaktifkan parameter ini (dengan nilai 0, informasi tentang semua proses autovacuum akan menjadi output) dan perhatikan angka-angkanya.

Seperti inilah informasi output:

 => ALTER SYSTEM SET log_autovacuum_min_duration = 0; => SELECT pg_reload_conf(); 
  pg_reload_conf ---------------- t (1 row) 

 => UPDATE autovac SET s = 'C' WHERE id <= 31; 

 student$ tail -n 7 /var/log/postgresql/postgresql-11-main.log 
 2019-05-21 11:59:55.675 MSK [9737] LOG: automatic vacuum of table "test.public.autovac": index scans: 0 pages: 0 removed, 18 remain, 0 skipped due to pins, 0 skipped frozen tuples: 31 removed, 1000 remain, 0 are dead but not yet removable, oldest xmin: 4040 buffer usage: 78 hits, 0 misses, 0 dirtied avg read rate: 0.000 MB/s, avg write rate: 0.000 MB/s system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s 2019-05-21 11:59:55.676 MSK [9737] LOG: automatic analyze of table "test.public.autovac" system usage: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s 

Semua informasi yang diperlukan tersedia di sini.

Untuk mengingatkan Anda, sering kali masuk akal untuk menurunkan ambang batas untuk pemicu vakum untuk memproses lebih sedikit data pada satu waktu daripada meningkatkan ukuran memori.

Mungkin juga masuk akal untuk menggunakan tampilan di atas untuk memonitor panjang daftar tabel yang membutuhkan penyedotan. Peningkatan panjang daftar akan menunjukkan bahwa proses autovacuum tidak memiliki waktu untuk melakukan pekerjaan mereka dan pengaturan perlu diubah.

Untuk dilanjutkan.

Source: https://habr.com/ru/post/id486104/


All Articles