MVCC-7. Pembersihan otomatis

Biarkan saya mengingatkan Anda bahwa kami mulai dengan masalah yang berkaitan dengan isolasi , melakukan penyimpangan tentang pengorganisasian data pada tingkat rendah , berbicara secara rinci tentang versi baris dan bagaimana snapshot diperoleh dari versi.

Kemudian kami melihat pembersihan dalam halaman (dan pembaruan HOT), pembersihan rutin , tetapi hari ini kami melihat pembersihan otomatis.

Pembersihan otomatis (autovacuum)


Kami telah mengatakan bahwa pembersihan biasa dalam kondisi normal (ketika tidak ada yang memegang cakrawala transaksi untuk waktu yang lama) harus mengatasi pekerjaannya. Pertanyaannya adalah seberapa sering menyebutnya.

Jika Anda membersihkan meja ganti terlalu jarang, itu akan tumbuh lebih besar dari yang Anda inginkan. Selain itu, untuk pembersihan berikutnya, mungkin diperlukan beberapa kali melewati indeks jika terlalu banyak perubahan telah terakumulasi.

Jika Anda membersihkan tabel terlalu sering, maka alih-alih pekerjaan yang bermanfaat, server akan terus terlibat dalam pemeliharaan - juga tidak baik.

Perhatikan bahwa memulai pembersihan terjadwal secara teratur tidak menyelesaikan masalah, karena beban dapat berubah seiring waktu. Jika meja mulai diperbarui lebih aktif, maka itu harus dibersihkan lebih sering.

Pembersihan otomatis hanyalah mekanisme yang memungkinkan Anda untuk mulai membersihkan, tergantung pada aktivitas perubahan dalam tabel.

Ketika pembersihan otomatis diaktifkan (parameter konfigurasi autovacuum ), proses peluncur autovacuum selalu ada dalam sistem, yang berencana untuk bekerja, dan proses kerja pekerja autovacuum terlibat dalam pembersihan nyata, beberapa contoh yang dapat bekerja secara paralel.

Proses peluncur autovacuum menyusun daftar database di mana ada aktivitas apa pun. Aktivitas ditentukan oleh statistik, dan untuk itu dikumpulkan, parameter track_counts harus ditetapkan. Jangan pernah mematikan autovacuum dan track_counts , jika tidak pembersihan otomatis tidak akan berfungsi.

Setelah di autovacuum_naptime, proses peluncur autovacuum dimulai (menggunakan proses postmaster) alur kerja untuk setiap database dalam daftar. Dengan kata lain, jika ada aktivitas dalam database, maka alur kerja akan masuk ke dalamnya dengan interval autovacuum_naptime . Untuk melakukan ini, jika ada beberapa database aktif (potongan N), maka proses kerja diluncurkan N kali lebih sering daripada autovacuum_naptime . Tetapi pada saat yang sama, jumlah total alur kerja yang bekerja secara bersamaan dibatasi oleh parameter autovacuum_max_workers .

Setelah dimulai, alur kerja terhubung ke database yang ditentukan olehnya dan mulai dengan membangun daftar:

  • semua tabel, tampilan terwujud, dan tabel roti panggang yang perlu dibersihkan,
  • semua tabel dan representasi terwujud yang memerlukan analisis (tabel roti tidak dianalisis, karena mereka selalu diakses oleh indeks).

Selanjutnya, alur kerja pada gilirannya membersihkan dan / atau menganalisis objek yang dipilih dan berakhir ketika pembersihan selesai.

Jika proses belum menyelesaikan semua pekerjaan yang dimaksudkan untuk autovacuum_naptime , proses peluncur autovacuum akan mengirim alur kerja lain ke database yang sama dan mereka akan bekerja bersama. "Bersama" berarti bahwa proses kedua akan membangun daftar tabel dan mengikutinya. Dengan demikian, tabel yang berbeda akan diproses secara paralel, tetapi pada tingkat satu tabel tidak ada paralelisme - jika salah satu proses kerja sudah bekerja di atas meja, yang lain akan melewatkannya dan melanjutkan.

Telah ada diskusi tentang perlunya pemrosesan paralel untuk waktu yang lama, tetapi tambalan belum diadopsi.

Sekarang mari kita melihat lebih dekat apa yang “perlu dibersihkan” dan “membutuhkan analisis”.

Meja mana yang perlu dibersihkan


Dipercayai bahwa pembersihan diperlukan jika jumlah versi "mati", yaitu, tidak relevan, melebihi nilai ambang batas yang ditetapkan. Jumlah versi mati secara konstan dikumpulkan oleh pengumpul statistik dan disimpan di tabel pg_stat_all_tables. Dan ambangnya diatur oleh dua parameter:

  • autovacuum_vacuum_threshold mendefinisikan nilai absolut (dalam potongan),
  • autovacuum_vacuum_scale_factor menentukan proporsi baris dalam tabel.

Rumus terakhir adalah: pembersihan diperlukan jika pg_stat_all_tables.n_dead_tup> = autovacuum_vacuum_threshold + autovacuum_vacuum_valeum_scale_factor * pg_class.reltupes.

Pengaturan default ditetapkan autovacuum_vacuum_threshold = 50 dan
autovacuum_vacuum_scale_factor = 0.2. Parameter utama di sini, tentu saja, adalah autovacuum_vacuum_scale_factor - itu yang penting untuk tabel besar (yaitu, kemungkinan masalah terkait dengan mereka). Nilai 20% tampaknya sangat berlebihan, kemungkinan besar perlu dikurangi secara signifikan.

Nilai parameter optimal dapat bervariasi untuk tabel yang berbeda tergantung pada ukuran dan sifat perubahannya. Masuk akal untuk menetapkan, secara keseluruhan, nilai-nilai yang memadai, dan - jika perlu - untuk secara khusus mengkonfigurasi parameter di tingkat beberapa tabel menggunakan parameter penyimpanan:

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

Agar tidak bingung, ini harus dilakukan hanya untuk sejumlah kecil tabel yang menonjol antara lain oleh volume atau intensitas perubahan, dan hanya jika nilai yang ditetapkan secara global tidak cocok.

Selain itu, pembersihan otomatis dapat dinonaktifkan di tingkat meja (meskipun sulit untuk memikirkan alasan mengapa ini diperlukan):

  • autovacuum_enabled dan bersulang.autovacuum_enabled .

Misalnya, terakhir kali kami membuat tabel vakum dengan pembersihan otomatis dinonaktifkan, untuk - untuk tujuan demonstrasi - untuk mengelola pembersihan manual. Parameter penyimpanan dapat diubah sebagai berikut:

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

Untuk memformalkan semua hal di atas, kami akan membuat tampilan yang menunjukkan tabel mana yang saat ini perlu dibersihkan. Ini akan menggunakan fungsi yang mengembalikan nilai parameter saat ini, mengingat bahwa itu dapat diganti pada level tabel:

 => CREATE FUNCTION get_value(param text, reloptions text[], relkind "char") RETURNS float AS $$ SELECT coalesce( --    ,    (SELECT option_value FROM pg_options_to_table(reloptions) WHERE option_name = CASE --  toast-    WHEN relkind = 't' THEN 'toast.' ELSE '' END || param ), --      current_setting(param) )::float; $$ LANGUAGE sql; 

Dan inilah tampilan:

 => 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 perlu analisis


Dengan analisis otomatis, situasinya hampir sama. Dipercaya bahwa analisis diperlukan untuk tabel-tabel yang jumlah baris (sejak analisis terakhir) yang diubah melebihi nilai ambang yang ditentukan oleh dua parameter yang serupa: pg_stat_all_tables.n_mod_since_analyze> = autovacuum_analyze_threshold + autovacuum_analyze_scale_faktor.faktor .

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

  • autovacuum_analyze_threshold ,
  • autovacuum_analyze_scale_factor

Karena tabel roti bakar tidak dianalisis, tidak ada parameter yang sesuai untuk mereka.

Mari kita buat 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


Untuk percobaan, kami menetapkan nilai parameter berikut:

 => ALTER SYSTEM SET autovacuum_naptime = '1s'; --     => 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 buat tabel yang mirip dengan yang kita gunakan terakhir kali dan masukkan seribu baris ke dalamnya. Pembersihan otomatis dinonaktifkan di tingkat meja, dan kami akan menyalakannya sendiri. Jika ini tidak dilakukan, maka contohnya tidak akan dapat direproduksi, karena pembersihan otomatis dapat bekerja pada waktu yang salah.

 => 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); 

Berikut tampilan yang akan kami tampilkan:

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

Ada dua hal yang harus Anda perhatikan. Pertama, max_dead_tup = 0, meskipun 3% dari 1000 baris adalah 30 baris. Faktanya adalah kita belum memiliki statistik di atas meja, karena INSERT sendiri tidak memperbaruinya. Sampai tabel kita dianalisis, nol akan tetap ada, karena pg_class.reltuples = 0. Namun, 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 tabel telah berubah (ditambahkan) 1000 baris, dan ini lebih dari nol, analisis otomatis akan berfungsi. Lihat ini:

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

Setelah jeda singkat, kita melihat bahwa tabel dianalisis dan bukannya nol di max_mod_tup kita melihat 20 baris yang benar:

 => 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 pembersihan otomatis:

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

Max_dead_tup, seperti yang kita lihat, sudah diperbaiki. Poin kedua yang perlu diperhatikan adalah dead_tup = 0. Statistik menunjukkan bahwa tidak ada versi mati baris dalam tabel ... dan ini benar. Belum ada yang dibersihkan di meja kami. Jadi setiap tabel yang digunakan hanya dalam mode append-only tidak akan dihapus dan, oleh karena itu, peta visibilitas tidak akan diperbarui untuk itu. Dan ini membuat tidak mungkin untuk menggunakan pemindaian indeks secara eksklusif (hanya indeks scan).

(Lain kali kita akan melihat bahwa membersihkan cepat atau lambat akan datang ke meja append-only, tetapi ini akan sangat jarang terjadi.)

Kesimpulan praktis: jika penting hanya menggunakan pemindaian indeks, Anda mungkin perlu meminta pembersihan manual.

Sekarang matikan pembersihan-otomatis lagi dan perbarui 31 baris - satu lebih dari nilai 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 untuk memicu pembersihan otomatis terpenuhi. Nyalakan pembersihan otomatis 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) 

Regulasi beban


Pembersihan tidak menghalangi proses lain, karena ia bekerja berdasarkan halaman demi halaman, tetapi tetap menciptakan beban pada sistem dan dapat memiliki efek yang nyata pada kinerja.

Regulasi untuk pembersihan rutin


Agar dapat mengontrol intensitas pembersihan dan, akibatnya, pengaruhnya pada sistem, proses bergantian antara pekerjaan dan harapan. Pembersihan melakukan sekitar vacuum_cost_limit unit kerja konvensional, dan kemudian tertidur pada ms vacuum_cost_delay .

Pengaturan default yang ditetapkan vacuum_cost_limit = 200, vacuum_cost_delay = 0. Nol terakhir sebenarnya berarti bahwa pembersihan (normal) tidak tertidur, sehingga nilai spesifik dari vacuum_cost_limit tidak memainkan peran apa pun. Ini dilakukan dengan alasan bahwa jika administrator harus memulai VACUUM secara manual, maka ia mungkin ingin melakukan pembersihan secepat mungkin.

Namun demikian, jika Anda masih mengatur waktu tidur, maka jumlah pekerjaan yang ditentukan dalam vacuum_cost_limit akan terdiri dari biaya bekerja dengan halaman dalam cache buffer. Setiap akses halaman dievaluasi sebagai berikut:

  • jika halaman ditemukan dalam cache buffer, maka vacuum_cost_page_hit = 1;
  • jika tidak ditemukan, maka vacuum_cost_page_miss = 10;
  • jika Anda tidak dapat menemukannya, dan Anda harus mendorong halaman kotor keluar dari buffer, kemudian vacuum_cost_page_dirty = 20.

Yaitu, dengan pengaturan vacuum_cost_limit secara default, 200 halaman dari cache, atau 20 halaman dari disk, atau 10 halaman dengan ekstrusi dapat diproses dalam satu duduk. Jelas bahwa angka-angka ini agak sewenang-wenang, tetapi tidak masuk akal untuk memilihnya lebih tepat.

Regulasi untuk pembersihan otomatis


Kontrol beban selama pembersihan otomatis berfungsi sama seperti untuk pembersihan biasa. Tetapi agar pembersihan manual dan pembersihan otomatis dapat bekerja dengan intensitas berbeda, autoproses memiliki parameter sendiri: autovacuum_vacuum_cost_limit dan autovacuum_vacuum_cost_delay . Jika parameter ini mengambil nilai -1, maka nilai dari vacuum_cost_limit dan / atau vacuum_cost_delay digunakan .

Secara default, autovacuum_vacuum_cost_limit = -1 (yaitu, nilai vacuum_cost_limit = 200 digunakan) dan autovacuum_vacuum_cost_delay = 20ms. Pada peralatan modern dengan angka-angka ini, pembersihan otomatis akan bekerja sangat, sangat lambat.

Dalam versi 12, nilai autovacuum_vacuum_cost_delay akan dikurangi menjadi 2ms, yang dapat dianggap sebagai perkiraan pertama yang lebih tepat.

Selain itu, perlu dicatat bahwa batas yang ditetapkan oleh parameter ini adalah umum untuk semua proses kerja. Dengan kata lain, ketika jumlah proses kerja bersamaan berubah, total beban akan tetap konstan. Karena itu, jika tugasnya adalah meningkatkan kinerja pembersihan otomatis , maka saat menambahkan alur kerja, ada baiknya meningkatkan autovacuum_vacuum_cost_limit .

Penggunaan dan pemantauan memori


Terakhir kali, kami melihat bagaimana pembersihan menggunakan memori RAM dengan ukuran maintenance_work_mem untuk menyimpan pengenal versi dari baris yang akan dibersihkan.

Pembersihan otomatis tidak persis sama. Tetapi ada banyak proses bersamaan jika Anda mengatur autovacuum_max_workers ke nilai yang besar. Selain itu, semua memori dialokasikan segera dan sepenuhnya, dan bukan karena kebutuhan. Oleh karena itu, untuk alur kerja pembersihan-otomatis , Anda dapat mengatur batasan Anda sendiri menggunakan parameter autovacuum_work_mem . Secara default, parameter ini -1, yaitu tidak digunakan.

Seperti yang telah disebutkan, pembersihan dapat bekerja dengan jumlah memori minimum. Tetapi jika indeks dibuat di atas meja, maka nilai maintenance_work_mem kecil dapat menyebabkan pemindaian indeks berulang. Hal yang sama berlaku untuk pembersihan otomatis. Idealnya, Anda harus memilih nilai minimum autovacuum_work_mem di mana pemindaian berulang tidak terjadi.

Kami melihat bahwa untuk memantau pembersihan Anda dapat menggunakan parameter VERBOSE (tetapi tidak dapat ditentukan untuk pembersihan otomatis) atau tampilan pg_stat_progress_vacuum (tetapi hanya menampilkan informasi saat ini). Oleh karena itu, cara utama untuk memantau pembersihan otomatis adalah parameter log_autovacuum_min_duration , yang menampilkan informasi dalam log pesan server. Secara default tidak aktif (diatur ke -1). Ada alasan untuk mengaktifkan parameter ini (pada nilai 0, informasi tentang semua awal pembersihan otomatis akan ditampilkan) dan amati angkanya.

Seperti inilah hasilnya:

 => 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 ada di sini.

Ingatlah bahwa seringkali Anda seharusnya tidak menambah ukuran memori, tetapi kurangi ambang batas untuk pembersihan sehingga lebih sedikit data yang diproses pada satu waktu.

Mungkin juga masuk akal untuk memantau panjang daftar tabel yang perlu dibersihkan menggunakan tampilan di atas. Peningkatan panjang daftar akan menunjukkan bahwa pembersihan otomatis tidak punya waktu untuk melakukan tugasnya dan bahwa pengaturan perlu diubah.

Untuk dilanjutkan .

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


All Articles