MVCC di PostgreSQL-6. Vakum

Kami mulai dengan masalah yang berkaitan dengan isolasi , membuat penyimpangan tentang struktur data tingkat rendah , kemudian membahas versi baris dan mengamati bagaimana snapshot data diperoleh dari versi baris.

Terakhir kali kami berbicara tentang pembaruan HOT dan penyedotan halaman, dan hari ini kami akan melanjutkan ke vacuum vulgaris yang terkenal. Sungguh, sudah banyak yang telah ditulis tentang hal itu sehingga saya hampir tidak dapat menambahkan sesuatu yang baru, tetapi keindahan dari gambar penuh membutuhkan pengorbanan. Jadi, bersabarlah.

Vakum


Apa yang dilakukan dengan vakum?


Vakum dalam halaman berfungsi dengan cepat, tetapi hanya membebaskan sebagian ruang. Ini berfungsi dalam satu halaman tabel dan tidak menyentuh indeks.

Dasar, vakum "normal" dilakukan dengan menggunakan perintah VACUUM, dan kami akan menyebutnya hanya "vakum" (meninggalkan "autovacuum" untuk diskusi terpisah).

Jadi, proses vakum seluruh tabel. Ini mengosongkan tidak hanya tupel mati, tetapi juga referensi untuk mereka dari semua indeks.

Penghisapan debu bersamaan dengan kegiatan lain dalam sistem. Tabel dan indeks dapat digunakan secara teratur baik untuk membaca dan memperbarui (namun, eksekusi perintah bersamaan seperti CREATE INDEX, ALTER TABLE dan beberapa lainnya tidak mungkin).

Hanya halaman-halaman tabel yang dilihat melalui tempat beberapa kegiatan berlangsung. Untuk mendeteksinya, peta visibilitas digunakan (untuk mengingatkan Anda, peta melacak halaman-halaman yang berisi tupel yang cukup tua, yang terlihat jelas di semua snapshot data). Hanya halaman tersebut yang diproses yang tidak dilacak oleh peta visibilitas, dan peta itu sendiri akan diperbarui.

Peta ruang bebas juga akan diperbarui dalam proses untuk mencerminkan ruang bebas tambahan di halaman.

Seperti biasa, mari kita buat tabel:

=> CREATE TABLE vac( id serial, s char(100) ) WITH (autovacuum_enabled = off); => CREATE INDEX vac_s ON vac(s); => INSERT INTO vac(s) VALUES ('A'); => UPDATE vac SET s = 'B'; => UPDATE vac SET s = 'C'; 

Kami menggunakan parameter autovacuum_enabled untuk mematikan proses autovacuum. Kami akan membahasnya lain kali, dan sekarang sangat penting bagi percobaan kami bahwa kami mengontrol penyedotan debu secara manual.

Tabel sekarang memiliki tiga tupel, yang masing-masing dirujuk dari indeks:

 => SELECT * FROM heap_page('vac',0); 
  ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+----------+-----+-----+-------- (0,1) | normal | 4000 (c) | 4001 (c) | | | (0,2) (0,2) | normal | 4001 (c) | 4002 | | | (0,3) (0,3) | normal | 4002 | 0 (a) | | | (0,3) (3 rows) 

 => SELECT * FROM index_page('vac_s',1); 
  itemoffset | ctid ------------+------- 1 | (0,1) 2 | (0,2) 3 | (0,3) (3 rows) 

Setelah menyedot debu, tuple mati disedot, dan hanya satu, hidup, tuple tetap. Dan hanya satu referensi yang tersisa dalam indeks:

 => VACUUM vac; => SELECT * FROM heap_page('vac',0); 
  ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+-------+-----+-----+-------- (0,1) | unused | | | | | (0,2) | unused | | | | | (0,3) | normal | 4002 (c) | 0 (a) | | | (0,3) (3 rows) 
 => SELECT * FROM index_page('vac_s',1); 
  itemoffset | ctid ------------+------- 1 | (0,3) (1 row) 

Perhatikan bahwa dua petunjuk pertama memperoleh status "tidak digunakan" alih-alih "mati", yang akan mereka peroleh dengan kekosongan halaman.

Tentang cakrawala transaksi sekali lagi


Bagaimana PostgreSQL mengetahui tupel mana yang dapat dianggap mati? Kami sudah menyentuh konsep horizon transaksi ketika membahas snapshot data , tetapi tidak ada salahnya untuk mengulangi masalah penting tersebut.

Mari kita mulai eksperimen sebelumnya lagi.

 => TRUNCATE vac; => INSERT INTO vac(s) VALUES ('A'); => UPDATE vac SET s = 'B'; 

Tetapi sebelum memperbarui baris sekali lagi, biarkan satu lagi transaksi dimulai (tetapi tidak berakhir). Dalam contoh ini, ia akan menggunakan tingkat Komitmen Baca, tetapi harus mendapatkan nomor transaksi yang benar (bukan virtual). Sebagai contoh, transaksi dapat mengubah dan bahkan mengunci baris tertentu di tabel apa pun, bukan vac wajib:

 | => BEGIN; | => SELECT s FROM t FOR UPDATE; 
 | s | ----- | FOO | BAR | (2 rows) 

 => UPDATE vac SET s = 'C'; 

Ada tiga baris dalam tabel dan tiga referensi dalam indeks sekarang. Apa yang akan terjadi setelah menyedot debu?

 => VACUUM vac; => SELECT * FROM heap_page('vac',0); 
  ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+----------+-----+-----+-------- (0,1) | unused | | | | | (0,2) | normal | 4005 (c) | 4007 (c) | | | (0,3) (0,3) | normal | 4007 (c) | 0 (a) | | | (0,3) (3 rows) 
 => SELECT * FROM index_page('vac_s',1); 
  itemoffset | ctid ------------+------- 1 | (0,2) 2 | (0,3) (2 rows) 

Dua tuple tetap ada dalam tabel: VACUUM memutuskan bahwa tuple (0,2) belum dapat disedot. Alasannya tentu saja dalam cakrawala transaksi dari basis data, yang dalam contoh ini ditentukan oleh transaksi yang tidak selesai:

 | => SELECT backend_xmin FROM pg_stat_activity WHERE pid = pg_backend_pid(); 
 | backend_xmin | -------------- | 4006 | (1 row) 

Kami dapat meminta VACUUM untuk melaporkan apa yang terjadi:

 => VACUUM VERBOSE vac; 
 INFO: vacuuming "public.vac" INFO: index "vac_s" now contains 2 row versions in 2 pages DETAIL: 0 index row versions were removed. 0 index pages have been deleted, 0 are currently reusable. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. INFO: "vac": found 0 removable, 2 nonremovable row versions in 1 out of 1 pages DETAIL: 1 dead row versions cannot be removed yet, oldest xmin: 4006 There were 1 unused item pointers. Skipped 0 pages due to buffer pins, 0 frozen pages. 0 pages are entirely empty. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM 

Perhatikan bahwa:

  • 2 nonremovable row versions yang tidak dapat dihapus - dua tupel yang tidak dapat dihapus ditemukan dalam tabel.
  • 1 dead row versions cannot be removed yet - salah satunya mati.
  • oldest xmin menunjukkan cakrawala saat ini.

Mari kita ulangi kesimpulannya: jika sebuah database memiliki transaksi yang berumur panjang (tidak selesai atau dilakukan sangat lama), ini dapat menyebabkan penggembungan tabel terlepas dari seberapa sering penyedotan terjadi. Oleh karena itu, beban kerja tipe OLTP dan OLAP buruk hidup berdampingan dalam satu database PostgreSQL: laporan yang berjalan berjam-jam tidak akan membiarkan tabel yang diperbarui disedot debu secara tepat. Pembuatan replika terpisah untuk tujuan pelaporan dapat menjadi solusi yang memungkinkan untuk ini.

Setelah menyelesaikan transaksi terbuka, cakrawala bergerak, dan situasinya diperbaiki:

 | => COMMIT; 

 => VACUUM VERBOSE vac; 
 INFO: vacuuming "public.vac" INFO: scanned index "vac_s" to remove 1 row versions DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s INFO: "vac": removed 1 row versions in 1 pages DETAIL: CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s INFO: index "vac_s" now contains 1 row versions in 2 pages DETAIL: 1 index row versions were removed. 0 index pages have been deleted, 0 are currently reusable. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. INFO: "vac": found 1 removable, 1 nonremovable row versions in 1 out of 1 pages DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 4008 There were 1 unused item pointers. Skipped 0 pages due to buffer pins, 0 frozen pages. 0 pages are entirely empty. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. VACUUM 

Sekarang hanya versi terbaru, langsung, baris yang tersisa di halaman:

 => SELECT * FROM heap_page('vac',0); 
  ctid | state | xmin | xmax | hhu | hot | t_ctid -------+--------+----------+-------+-----+-----+-------- (0,1) | unused | | | | | (0,2) | unused | | | | | (0,3) | normal | 4007 (c) | 0 (a) | | | (0,3) (3 rows) 

Indeks ini juga hanya memiliki satu baris:

 => SELECT * FROM index_page('vac_s',1); 
  itemoffset | ctid ------------+------- 1 | (0,3) (1 row) 

Apa yang terjadi di dalam?


Penyedot debu harus memproses tabel dan indeks secara bersamaan dan melakukan ini agar tidak mengunci proses lainnya. Bagaimana bisa begitu?

Semua dimulai dengan fase tumpukan pemindaian (peta visibilitas diperhitungkan, sebagaimana telah disebutkan). Di halaman yang dibaca, tupel mati terdeteksi, dan tid mereka dituliskan ke array khusus. Array disimpan dalam memori lokal dari proses vakum, di mana memori maintenance_work_mem dialokasikan untuk itu. Nilai default dari parameter ini adalah 64 MB. Perhatikan bahwa jumlah penuh memori dialokasikan sekaligus, daripada saat diperlukan. Namun, jika tabelnya tidak besar, jumlah memori yang lebih kecil dialokasikan.

Kemudian kita mencapai akhir tabel atau memori yang dialokasikan untuk array berakhir. Dalam kedua kasus, fase indeks penyedotan dimulai. Untuk tujuan ini, setiap indeks yang dibuat di atas tabel dipindai sepenuhnya untuk mencari baris yang merujuk pada tuple yang diingat. Baris yang ditemukan dihilangkan dari halaman indeks.

Di sini kita menghadapi yang berikut: indeks belum memiliki referensi untuk tupel mati, sementara tabel masih memilikinya. Dan ini tidak bertentangan dengan apa pun: saat menjalankan kueri, kami tidak menekan tupel mati (dengan akses indeks) atau menolaknya pada pemeriksaan visibilitas (saat memindai tabel).

Setelah itu, fase tumpukan debu dimulai. Tabel dipindai kembali untuk membaca halaman yang sesuai, menyedotnya dari tupel yang diingat dan melepaskan pointer. Kita dapat melakukan ini karena tidak ada referensi dari indeks lagi.

Jika tabel tidak sepenuhnya dibaca selama siklus pertama, array dihapus dan semuanya diulang dari tempat kami mencapai.

Singkatnya:

  • Tabel selalu dipindai dua kali.
  • Jika menyedot debu menghapus begitu banyak tupel sehingga semuanya tidak sesuai dengan memori maintenance_work_mem ukuran, semua indeks akan dipindai sebanyak yang diperlukan.

Untuk tabel besar, ini membutuhkan banyak waktu dan menambah beban kerja sistem yang signifikan. Tentu saja, pertanyaan tidak akan dikunci, tetapi input / output tambahan jelas tidak diinginkan.

Untuk mempercepat proses, masuk akal untuk memanggil VACUUM lebih sering (sehingga tidak terlalu banyak tuple yang disedot setiap kali) atau mengalokasikan lebih banyak memori.

Untuk dicatat dalam tanda kurung, dimulai dengan versi 11, PostgreSQL dapat melewati pemindaian indeks kecuali jika ada kebutuhan yang mendesak. Ini harus membuat hidup lebih mudah bagi pemilik tabel besar di mana baris hanya ditambahkan (tetapi tidak diubah).

Pemantauan


Bagaimana kita mengetahui bahwa VACUUM tidak dapat melakukan tugasnya dalam satu siklus?

Kami telah melihat cara pertama: untuk memanggil perintah VACUUM dengan opsi VERBOSE. Dalam hal ini, informasi tentang fase proses akan dikeluarkan ke konsol.

Kedua, dimulai dengan versi 9.6, tampilan pg_stat_progress_vacuum tersedia, yang juga menyediakan semua informasi yang diperlukan.

(Cara ketiga juga tersedia: untuk menampilkan informasi ke log pesan, tetapi ini hanya berfungsi untuk autovacuum, yang akan dibahas lain kali.)

Mari kita sisipkan beberapa baris dalam tabel, untuk proses vakum berlangsung cukup lama, dan mari kita perbarui semuanya, agar VACUUM dapat melakukan sesuatu.

 => TRUNCATE vac; => INSERT INTO vac(s) SELECT 'A' FROM generate_series(1,500000); => UPDATE vac SET s = 'B'; 

Mari kita kurangi ukuran memori yang dialokasikan untuk array pengidentifikasi:

 => ALTER SYSTEM SET maintenance_work_mem = '1MB'; => SELECT pg_reload_conf(); 

Mari kita mulai VACUUM dan ketika sedang bekerja, mari kita akses tampilan pg_stat_progress_vacuum beberapa kali:

 => VACUUM VERBOSE vac; 

 | => SELECT * FROM pg_stat_progress_vacuum \gx 
 | -[ RECORD 1 ]------+------------------ | pid | 6715 | datid | 41493 | datname | test | relid | 57383 | phase | vacuuming indexes | heap_blks_total | 16667 | heap_blks_scanned | 2908 | heap_blks_vacuumed | 0 | index_vacuum_count | 0 | max_dead_tuples | 174762 | num_dead_tuples | 174480 

 | => SELECT * FROM pg_stat_progress_vacuum \gx 
 | -[ RECORD 1 ]------+------------------ | pid | 6715 | datid | 41493 | datname | test | relid | 57383 | phase | vacuuming indexes | heap_blks_total | 16667 | heap_blks_scanned | 5816 | heap_blks_vacuumed | 2907 | index_vacuum_count | 1 | max_dead_tuples | 174762 | num_dead_tuples | 174480 

Di sini kita dapat melihat, khususnya:

  • Nama fase saat ini - kami membahas tiga fase utama, tetapi ada lebih banyak dari mereka secara umum.
  • Jumlah total halaman tabel ( heap_blks_total ).
  • Jumlah halaman yang dipindai ( heap_blks_scanned ).
  • Jumlah halaman yang sudah dihisap debu ( heap_blks_vacuumed ).
  • Jumlah siklus vakum indeks ( index_vacuum_count ).

Kemajuan umum ditentukan oleh rasio heap_blks_vacuumed ke heap_blks_total , tetapi kita harus memperhitungkan bahwa nilai ini berubah dalam peningkatan besar daripada lancar karena pemindaian indeks. Perhatian utama, bagaimanapun, harus diberikan pada jumlah siklus vakum: jumlah yang lebih besar dari 1 berarti bahwa memori yang dialokasikan tidak cukup untuk menyelesaikan proses vakum dalam satu siklus.

Output dari perintah VACUUM VERBOSE, yang sudah selesai pada saat itu, akan menampilkan gambaran umum:

 INFO: vacuuming "public.vac" 
 INFO: scanned index "vac_s" to remove 174480 row versions DETAIL: CPU: user: 0.50 s, system: 0.07 s, elapsed: 1.36 s INFO: "vac": removed 174480 row versions in 2908 pages DETAIL: CPU: user: 0.02 s, system: 0.02 s, elapsed: 0.13 s 
 INFO: scanned index "vac_s" to remove 174480 row versions DETAIL: CPU: user: 0.26 s, system: 0.07 s, elapsed: 0.81 s INFO: "vac": removed 174480 row versions in 2908 pages DETAIL: CPU: user: 0.01 s, system: 0.02 s, elapsed: 0.10 s 
 INFO: scanned index "vac_s" to remove 151040 row versions DETAIL: CPU: user: 0.13 s, system: 0.04 s, elapsed: 0.47 s INFO: "vac": removed 151040 row versions in 2518 pages DETAIL: CPU: user: 0.01 s, system: 0.02 s, elapsed: 0.08 s 
 INFO: index "vac_s" now contains 500000 row versions in 17821 pages DETAIL: 500000 index row versions were removed. 8778 index pages have been deleted, 0 are currently reusable. CPU: user: 0.00 s, system: 0.00 s, elapsed: 0.00 s. INFO: "vac": found 500000 removable, 500000 nonremovable row versions in 16667 out of 16667 pages DETAIL: 0 dead row versions cannot be removed yet, oldest xmin: 4011 There were 0 unused item pointers. 0 pages are entirely empty. CPU: user: 1.10 s, system: 0.37 s, elapsed: 3.71 s. VACUUM 

Kita dapat melihat di sini bahwa tiga siklus atas indeks telah dilakukan, dan dalam setiap siklus, 174480 pointer ke tuple mati dihilangkan. Kenapa tepatnya nomor ini? Satu tid menempati 6 byte, dan 1024 * 1024/6 = 174762, yang merupakan angka yang kita lihat di pg_stat_progress_vacuum.max_dead_tuples . Pada kenyataannya, sedikit lebih sedikit dapat digunakan: ini memastikan bahwa ketika halaman berikutnya dibaca, semua pointer ke tuple yang mati pasti sesuai dengan memori.

Analisis


Analisis, atau, dengan kata lain, mengumpulkan statistik untuk perencana kueri, secara formal tidak terkait dengan menyedot debu sama sekali. Namun demikian, kita dapat melakukan analisis tidak hanya menggunakan perintah ANALYZE, tetapi menggabungkan penyedotan dan analisis dalam VACUUM ANALYZE. Di sini kekosongan dilakukan pertama dan kemudian analisis, jadi ini tidak memberikan keuntungan.

Tetapi seperti yang akan kita lihat nanti, autovacuum dan analisis otomatis dilakukan dalam satu proses dan dikendalikan dengan cara yang sama.

VACUUM FULL


Seperti disebutkan di atas, vakum membebaskan lebih banyak ruang daripada vakum dalam-halaman, tetapi tetap saja itu tidak sepenuhnya menyelesaikan masalah.

Jika karena alasan tertentu ukuran tabel atau indeks telah meningkat banyak, VACUUM akan mengosongkan ruang di dalam halaman yang ada: "lubang" akan terjadi di sana, yang kemudian akan digunakan untuk memasukkan tupel baru. Tetapi jumlah halaman tidak akan berubah, dan karena itu, dari sudut pandang sistem operasi, file akan menempati ruang yang sama persis seperti sebelum vakum. Dan ini tidak baik karena:

  • Pemindaian penuh tabel (atau indeks) melambat.
  • Cache penyangga yang lebih besar mungkin diperlukan (karena itu adalah halaman yang disimpan di sana dan kepadatan informasi yang berguna berkurang).
  • Di pohon indeks, level tambahan dapat terjadi, yang akan memperlambat akses indeks.
  • File menempati ruang ekstra pada disk dan dalam salinan cadangan.

(Satu-satunya pengecualian adalah halaman yang benar-benar menyedot debu, terletak di akhir file. Halaman ini dipangkas dari file dan dikembalikan ke sistem operasi.)

Jika pembagian informasi yang berguna dalam file berada di bawah batas yang wajar, administrator dapat melakukan VACUUM FULL dari tabel. Dalam hal ini, tabel dan semua indeksnya dibangun kembali dari awal dan datanya dikemas dengan cara yang sebagian besar ringkas (tentu saja, parameter fillfactor dipertimbangkan). Selama proses pembangunan kembali, PostgreSQL pertama-tama membangun kembali tabel dan kemudian masing-masing indeks satu-per-satu. Untuk setiap objek, file baru dibuat, dan file lama dihapus pada akhir pembangunan kembali. Kita harus memperhitungkan bahwa ruang disk tambahan akan dibutuhkan dalam proses.

Untuk mengilustrasikan ini, mari kita kembali memasukkan sejumlah baris ke dalam tabel:

 => TRUNCATE vac; => INSERT INTO vac(s) SELECT 'A' FROM generate_series(1,500000); 

Bagaimana kita bisa memperkirakan kepadatan informasi? Untuk melakukan ini, lebih mudah menggunakan ekstensi khusus:

 => CREATE EXTENSION pgstattuple; => SELECT * FROM pgstattuple('vac') \gx 
 -[ RECORD 1 ]------+--------- table_len | 68272128 tuple_count | 500000 tuple_len | 64500000 tuple_percent | 94.47 dead_tuple_count | 0 dead_tuple_len | 0 dead_tuple_percent | 0 free_space | 38776 free_percent | 0.06 

Fungsi membaca seluruh tabel dan menunjukkan statistik: data mana yang menempati berapa banyak ruang dalam file. Informasi utama yang kami minati sekarang adalah bidang tuple_percent : persentase data yang berguna. Kurang dari 100 karena overhead informasi yang tak terhindarkan di dalam halaman, tetapi masih cukup tinggi.

Untuk indeks, informasi yang berbeda adalah output, tetapi bidang avg_leaf_density memiliki arti yang sama: persentase informasi yang berguna (di halaman daun).

 => SELECT * FROM pgstatindex('vac_s') \gx 
 -[ RECORD 1 ]------+--------- version | 3 tree_level | 3 index_size | 72802304 root_block_no | 2722 internal_pages | 241 leaf_pages | 8645 empty_pages | 0 deleted_pages | 0 avg_leaf_density | 83.77 leaf_fragmentation | 64.25 

Dan ini adalah ukuran tabel dan indeks:

 => SELECT pg_size_pretty(pg_table_size('vac')) table_size, pg_size_pretty(pg_indexes_size('vac')) index_size; 
  table_size | index_size ------------+------------ 65 MB | 69 MB (1 row) 

Sekarang mari kita hapus 90% dari semua baris. Kami melakukan pilihan acak baris untuk dihapus, sehingga setidaknya satu baris sangat mungkin untuk tetap di setiap halaman:

 => DELETE FROM vac WHERE random() < 0.9; 
 DELETE 450189 

Berapa ukuran benda setelah VACUUM?

 => VACUUM vac; => SELECT pg_size_pretty(pg_table_size('vac')) table_size, pg_size_pretty(pg_indexes_size('vac')) index_size; 
  table_size | index_size ------------+------------ 65 MB | 69 MB (1 row) 

Kita dapat melihat bahwa ukurannya tidak berubah: VACUUM tidak mungkin mengurangi ukuran file. Dan ini meskipun kepadatan informasi menurun sekitar 10 kali:

 => SELECT vac.tuple_percent, vac_s.avg_leaf_density FROM pgstattuple('vac') vac, pgstatindex('vac_s') vac_s; 
  tuple_percent | avg_leaf_density ---------------+------------------ 9.41 | 9.73 (1 row) 

Sekarang mari kita periksa apa yang kita dapatkan setelah VACUUM FULL. Sekarang tabel dan indeks menggunakan file-file berikut:

 => SELECT pg_relation_filepath('vac'), pg_relation_filepath('vac_s'); 
  pg_relation_filepath | pg_relation_filepath ----------------------+---------------------- base/41493/57392 | base/41493/57393 (1 row) 

 => VACUUM FULL vac; => SELECT pg_relation_filepath('vac'), pg_relation_filepath('vac_s'); 
  pg_relation_filepath | pg_relation_filepath ----------------------+---------------------- base/41493/57404 | base/41493/57407 (1 row) 

File diganti dengan yang baru sekarang. Ukuran tabel dan indeks menurun secara signifikan, sementara kepadatan informasi meningkat sesuai:

 => SELECT pg_size_pretty(pg_table_size('vac')) table_size, pg_size_pretty(pg_indexes_size('vac')) index_size; 
  table_size | index_size ------------+------------ 6648 kB | 6480 kB (1 row) 
 => SELECT vac.tuple_percent, vac_s.avg_leaf_density FROM pgstattuple('vac') vac, pgstatindex('vac_s') vac_s; 
  tuple_percent | avg_leaf_density ---------------+------------------ 94.39 | 91.08 (1 row) 

Perhatikan bahwa kepadatan informasi dalam indeks bahkan lebih besar daripada yang asli. Lebih menguntungkan untuk membangun kembali indeks (B-tree) dari data yang tersedia daripada memasukkan data dalam indeks baris demi baris yang ada.

Fungsi ekstensi pgstattuple yang kami gunakan membaca seluruh tabel. Tapi ini merepotkan jika tabelnya besar, sehingga ekstensi memiliki fungsi pgstattuple_approx , yang melompati halaman yang ditandai di peta visibilitas dan menunjukkan angka perkiraan.

Satu lagi cara, tetapi bahkan kurang akurat, adalah dengan menggunakan katalog sistem untuk memperkirakan secara kasar rasio ukuran data dengan ukuran file. Anda dapat menemukan contoh pertanyaan seperti itu di wiki .

VACUUM FULL tidak dimaksudkan untuk penggunaan biasa karena ia memblokir semua pekerjaan dengan tabel (termasuk kueri) untuk semua durasi proses. Sudah jelas bahwa untuk sistem yang banyak digunakan, ini mungkin tampak tidak dapat diterima. Kunci akan dibahas secara terpisah, dan sekarang kami hanya akan menyebutkan ekstensi pg_repack , yang mengunci tabel hanya untuk jangka waktu singkat di akhir pekerjaan.

Perintah serupa


Ada beberapa perintah yang juga sepenuhnya membangun kembali tabel dan indeks dan karenanya menyerupai VACUUM FULL. Semuanya sepenuhnya memblokir semua pekerjaan dengan tabel, mereka semua menghapus file data lama dan membuat yang baru.

Perintah CLUSTER semuanya mirip dengan VACUUM FULL, tetapi juga secara fisik memesan tupel menurut salah satu indeks yang tersedia. Ini memungkinkan perencana untuk menggunakan akses indeks lebih efisien dalam beberapa kasus. Tetapi kita harus ingat bahwa pengelompokan tidak dipertahankan: tatanan fisik tupel akan rusak dengan perubahan tabel selanjutnya.

Perintah REINDEX membangun kembali indeks terpisah di atas meja. VACUUM FULL dan CLUSTER sebenarnya menggunakan perintah ini untuk membangun kembali indeks.

Logika dari perintah TRUNCATE mirip dengan DELETE - ia menghapus semua baris tabel. Tapi HAPUS, seperti yang telah disebutkan, hanya menandai tupel sebagai dihapus, dan ini membutuhkan penyedotan lebih lanjut. Dan TRUNCATE hanya membuat file baru yang bersih. Sebagai aturan, ini bekerja lebih cepat, tetapi kami harus memikirkan bahwa TRUNCATE akan memblokir semua pekerjaan dengan tabel hingga akhir transaksi.

Baca terus .

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


All Articles