Postgres: bloat, pg_repack sareng konstrain nunggak

Postgres: bloat, pg_repack sareng konstrain nunggak

Pangaruh bloat on tabel sarta indexes geus dipikawanoh lega tur hadir teu ukur dina Postgres. Aya sababaraha cara pikeun nganyahokeunana kaluar tina kotak, sapertos VACUUM FULL atanapi CLUSTER, tapi aranjeunna ngonci tabel nalika operasi sahingga henteu tiasa dianggo.

Artikel bakal ngandung téori saeutik ngeunaan kumaha bloat lumangsung, kumaha anjeun tiasa ngalawan eta, ngeunaan konstrain nunggak jeung masalah aranjeunna mawa kana pamakéan extension pg_repack.

Artikel ieu ditulis dumasar kana ucapan kuring di PgConf.Russia 2020.

Naha bloat lumangsung?

Postgres dumasar kana model multi-versi (MVCC). Intina nyaéta yén unggal baris dina tabél tiasa gaduh sababaraha versi, sedengkeun transaksi ningali henteu langkung ti hiji versi ieu, tapi henteu kedah sami. Hal ieu ngamungkinkeun sababaraha transaksi tiasa dianggo sakaligus sareng ampir teu aya pangaruhna.

Jelas, sadaya vérsi ieu kedah disimpen. Postgres dianggo sareng halaman mémori ku halaman sareng halaman mangrupikeun jumlah data minimum anu tiasa dibaca tina disk atanapi tulisan. Hayu urang tingali conto leutik pikeun ngartos kumaha ieu kajadian.

Anggap urang gaduh tabel dimana kami parantos nambihan sababaraha rékaman. Data anyar parantos muncul dina halaman munggaran file dimana méja disimpen. Ieu mangrupikeun versi live baris anu sayogi pikeun transaksi sanés saatos komitmen (pikeun kesederhanaan, urang bakal nganggap yén tingkat isolasi nyaéta Baca Komitmen).

Postgres: bloat, pg_repack sareng konstrain nunggak

Urang teras ngapdet salah sahiji éntri, ku kituna nyirian versi anu lami janten henteu relevan deui.

Postgres: bloat, pg_repack sareng konstrain nunggak

Léngkah-léngkah, ngamutahirkeun sareng ngahapus vérsi barisan, kami réngsé sareng halaman dimana kira-kira satengahna datana "sampah". Data ieu henteu katingali ku transaksi naon waé.

Postgres: bloat, pg_repack sareng konstrain nunggak

Postgres gaduh mékanisme vakum, nu cleans kaluar versi luntur jeung ngajadikeun rohangan pikeun data anyar. Tapi lamun teu ngonpigurasi cukup aggressively atanapi sibuk gawe dina tabel séjén, "data sampah" tetep, sarta kami kudu make kaca tambahan pikeun data anyar.

Ku kituna dina conto urang, di sawatara titik dina jangka waktu tabel bakal diwangun ku opat kaca, tapi ngan satengahna bakal ngandung data hirup. Hasilna, nalika ngaksés méja, urang bakal maca langkung seueur data ti anu diperyogikeun.

Postgres: bloat, pg_repack sareng konstrain nunggak

Sanaos VACUUM ayeuna ngahapus sadaya vérsi baris anu teu relevan, kaayaan éta moal ningkat sacara dramatis. Urang bakal ngagaduhan rohangan bébas dina halaman atanapi malah sadayana halaman pikeun baris anyar, tapi urang bakal tetep maca langkung seueur data ti anu diperyogikeun.
Ku jalan kitu, upami halaman kosong lengkep (anu kadua dina conto urang) aya di tungtung file, maka VACUUM tiasa motong éta. Tapi ayeuna anjeunna aya di tengah, janten teu aya anu tiasa dilakukeun sareng anjeunna.

Postgres: bloat, pg_repack sareng konstrain nunggak

Lamun jumlah kaca kosong atawa kacida sparse misalna jadi badag, nu disebut bloat, éta mimiti mangaruhan kinerja.

Sagalana ditétélakeun di luhur nyaéta mékanika lumangsungna bloat dina tabel. Dina indexes ieu kajadian dina loba cara nu sarua.

Dupi abdi gaduh kembung?

Aya sababaraha cara pikeun nangtoskeun upami anjeun ngagaduhan kembung. Gagasan anu munggaran nyaéta ngagunakeun statistik Postgres internal, anu ngandung inpormasi perkiraan ngeunaan jumlah baris dina tabel, jumlah barisan "live", jsb. Anjeun tiasa mendakan seueur variasi naskah anu siap didamel dina Internét. Urang nyandak salaku dadasar naskah ti Ahli PostgreSQL, nu bisa evaluate tabel bloat sapanjang kalawan roti bakar jeung bloat indéks btree. Dina pangalaman urang, kasalahan nyaeta 10-20%.

Cara séjén nyaéta ngagunakeun extension pgstattuple, nu ngidinan Anjeun pikeun nempo jero kaca tur meunangkeun duanana hiji estimasi jeung hiji nilai bloat pasti. Tapi dina kasus anu kadua, anjeun kedah nyeken sadaya méja.

Urang nganggap nilai bloat leutik, nepi ka 20%, bisa ditarima. Ieu bisa dianggap salaku analog tina fillfactor pikeun tabél и indéks. Dina 50% ka luhur, masalah kinerja bisa dimimitian.

Cara pikeun merangan kembung

Postgres boga sababaraha cara pikeun nungkulan bloat out of the box, tapi aranjeunna henteu salawasna cocog pikeun sadayana.

Konpigurasikeun AUTOVACUUM ambéh kembung teu lumangsung. Atawa leuwih tepatna, tetep dina tingkat ditarima ku anjeun. Ieu sigana nasehat "kaptén", tapi kanyataanana ieu teu salawasna gampang pikeun ngahontal. Salaku conto, anjeun gaduh pamekaran aktip kalayan parobihan rutin kana skéma data, atanapi sababaraha jinis migrasi data nuju lumangsung. Hasilna, profil beban anjeun tiasa sering robih sareng biasana bakal béda-béda ti méja ka méja. Ieu ngandung harti anjeun kudu terus-terusan gawé saeutik payun tur saluyukeun AUTOVACUUM kana profil ngarobah unggal tabel. Tapi écés ieu teu gampang pikeun ngalakukeun.

Alesan umum anu sanés naha AUTOVACUUM henteu tiasa ngiringan tabel nyaéta kusabab aya transaksi anu lami-lami anu nyegah ngabersihkeun data anu sayogi pikeun transaksi éta. Rekomendasi di dieu ogé écés - nyingkirkeun transaksi "dangling" sareng ngaleutikan waktos transaksi aktip. Tapi upami beban dina aplikasi anjeun mangrupikeun hibrida OLAP sareng OLTP, maka anjeun tiasa sakaligus gaduh seueur apdet sering sareng patarosan pondok, kitu ogé operasi jangka panjang - contona, ngawangun laporan. Dina kaayaan kitu, sia mikir ngeunaan nyebarkeun beban dina basa béda, nu bakal ngidinan pikeun leuwih fine-tuning unggal sahijina.

Conto anu sanés - sanaos profilna homogen, tapi pangkalan data sahandapeun beban anu luhur pisan, teras AUTOVACUUM anu paling agrésif henteu tiasa diatasi, sareng bloat bakal lumangsung. Skala (vertikal atanapi horizontal) mangrupikeun hiji-hijina solusi.

Naon anu kudu dipigawé dina kaayaan dimana anjeun geus nyetél AUTOVACUUM, tapi bloat terus tumuwuh.

regu VACUUM pinuh ngawangun deui eusi tabel sareng indéks sareng ngan ukur data anu relevan dina éta. Pikeun ngaleungitkeun bloat, gawéna sampurna, tapi salila palaksanaan na hiji konci ekslusif dina tabel direbut (AccessExclusiveLock), nu moal ngidinan executing queries dina tabel ieu, malah milih. Upami anjeun mampuh ngeureunkeun jasa anjeun atanapi bagian tina éta pikeun sababaraha waktos (tina puluhan menit dugi ka sababaraha jam gumantung kana ukuran database sareng hardware anjeun), maka pilihan ieu mangrupikeun anu pangsaéna. Hanjakal, urang teu boga waktu pikeun ngajalankeun VACUUM FULL salila pangropéa dijadwalkeun, jadi metoda ieu teu cocog pikeun urang.

regu KLUSTER Rebuilds eusi tabel dina cara nu sarua salaku VACUUM FULL, tapi ngidinan Anjeun pikeun nangtukeun hiji indéks nurutkeun nu data bakal maréntahkeun fisik dina disk (tapi dina mangsa nu bakal datang urutan teu dijamin pikeun baris anyar). Dina kaayaan nu tangtu, ieu téh optimasi alus keur sababaraha queries - kalawan maca sababaraha rékaman ku indéks. Kakurangan paréntahna sami sareng VACUUM FULL - éta ngonci méja nalika operasi.

regu REINDEX sarupa jeung dua saméméhna, tapi rebuilds hiji indéks husus atawa sakabéh indéks dina tabél. Konci rada lemah: ShareLock dina méja (nyegah modifikasi, tapi ngamungkinkeun pilih) sareng AccessExclusiveLock dina indéks anu diwangun deui (meungpeuk patarosan nganggo indéks ieu). Sanajan kitu, dina versi 12 of Postgres parameter mucunghul SARENG, anu ngamungkinkeun anjeun ngawangun deui indéks tanpa ngahalangan tambihan sakaligus, modifikasi, atanapi ngahapus rékaman.

Dina versi Postgres samemehna, anjeun tiasa ngahontal hasil anu sami sareng REINDEX SAMPANG ngagunakeun Jieun indéks sakaligus. Eta ngidinan Anjeun pikeun nyieun hiji indéks tanpa ngonci ketat (ShareUpdateExclusiveLock, nu teu ngaganggu queries paralel), lajeng ngaganti indéks heubeul ku nu anyar tur ngahapus indéks heubeul. Ieu ngamungkinkeun anjeun ngaleungitkeun indéks bloat tanpa ngaganggu aplikasi anjeun. Penting pikeun nganggap yén nalika ngawangun deui indéks bakal aya beban tambahan dina subsistem disk.

Ku kituna, lamun keur indexes aya cara pikeun ngaleungitkeun bloat "dina laleur," lajeng teu aya pikeun tabel. Ieu dimana rupa-rupa ekstensi éksternal dimaénkeun: pg_repack (baheula pg_reorg), pgcompact, pgcompacttable jeung sajabana. Dina artikel ieu, kuring moal ngabandingkeun aranjeunna sarta ngan bakal ngobrol ngeunaan pg_repack, nu, sanggeus sababaraha modifikasi, kami nganggo sorangan.

Kumaha pg_repack jalan

Postgres: bloat, pg_repack sareng konstrain nunggak
Anggap urang gaduh tabel anu biasa - kalayan indéks, larangan sareng, hanjakalna, kalayan kembung. Léngkah munggaran dina pg_repack nyaéta nyieun tabel log pikeun nyimpen data ngeunaan sagala parobahan nalika ngajalankeun. Pemicu bakal ngayakeun réplikasi parobahan ieu pikeun unggal sisipan, ngapdet sareng ngahapus. Lajeng tabel dijieun, sarupa jeung aslina dina struktur, tapi tanpa indéks jeung larangan, ku kituna teu ngalambatkeun turun prosés inserting data.

Salajengna, pg_repack nransper data tina tabel heubeul ka tabel anyar, otomatis nyaring kaluar kabeh baris teu relevan, lajeng nyieun indéks pikeun tabel anyar. Salila palaksanaan sadaya operasi ieu, parobahan ngumpulkeun dina tabel log.

Lengkah saterusna nyaéta nransper parobahan kana tabel anyar. Migrasi dipigawé ngaliwatan sababaraha iterasi, sarta lamun aya kirang ti 20 éntri ditinggalkeun dina tabel log, pg_repack acquires konci kuat, migrasi data panganyarna, tur ngaganti tabel heubeul jeung nu anyar dina tabel Sistim Postgres. Ieu hiji-hijina sarta waktu anu pohara pondok mun anjeun moal bisa digawekeun ku tabél. Saatos ieu, méja lami sareng méja kalayan log dihapus sareng rohangan dibébaskeun dina sistem file. Prosésna réngsé.

Sagalana Sigana hébat dina teori, tapi naon anu lumangsung dina praktekna? Kami nguji pg_repack tanpa beban sareng handapeun beban, sareng pariksa operasina upami eureun prématur (dina basa sanés, nganggo Ctrl + C). Sadaya tés positip.

Kami angkat ka toko tuangeun - teras sadayana henteu jalan sapertos anu kami ngarepkeun.

Pancake munggaran diobral

Dina klaster kahiji kami nampi kasalahan ngeunaan palanggaran konstrain unik:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

Watesan ieu ngagaduhan nami index_16508 otomatis - éta diciptakeun ku pg_repack. Dumasar kana atribut anu kalebet dina komposisina, urang nangtukeun konstrain "urang" anu pakait sareng éta. Masalahna tétéla yén ieu sanés watesan anu biasa, tapi anu ditunda (konstrain ditunda), nyaéta. verifikasi na dipigawé engké ti paréntah sql, nu ngakibatkeun konsékuansi teu kaduga.

Konstrain nunggak: naha maranéhna diperlukeun tur kumaha aranjeunna jalan

A téori saeutik ngeunaan larangan nunggak.
Hayu urang nganggap conto basajan: urang boga buku tabel-rujukan mobil kalawan dua atribut - ngaran jeung urutan mobil dina diréktori.
Postgres: bloat, pg_repack sareng konstrain nunggak

create table cars
(
  name text constraint pk_cars primary key,
  ord integer not null constraint uk_cars unique
);



Anggap urang kedah ngagentos mobil kahiji sareng kadua. Solusi lugas nyaéta ngapdet nilai kahiji ka anu kadua, sareng anu kadua ka anu kahiji:

begin;
  update cars set ord = 2 where name = 'audi';
  update cars set ord = 1 where name = 'bmw';
commit;

Tapi nalika urang ngajalankeun kode ieu, urang ngaharepkeun palanggaran konstrain sabab urutan nilai dina tabél téh unik:

[23305] ERROR: duplicate key value violates unique constraint “uk_cars”
Detail: Key (ord)=(2) already exists.

Kumaha carana abdi tiasa ngalakukeun eta béda? Pilihan hiji: tambahkeun hiji ngagantian nilai tambahan kana urutan nu dijamin moal aya dina tabél, contona "-1". Dina programming, ieu disebut "nukar nilai dua variabel ngaliwatan katilu". Hiji-hijina aral tina metode ieu nyaéta pembaruan tambahan.

Pilihan dua: Ngadesain ulang tabel pikeun ngagunakeun tipe data floating point pikeun nilai urutan tinimbang integer. Teras, nalika ngamutahirkeun nilai tina 1, contona, ka 2.5, éntri kahiji bakal otomatis "nangtung" antara kadua sareng katilu. Solusi ieu jalan, tapi aya dua watesan. Mimiti, éta moal tiasa dianggo pikeun anjeun upami nilaina dianggo dimana waé dina antarmuka. Kadua, gumantung kana katepatan jinis data, anjeun bakal gaduh sajumlah kawates kamungkinan sisipan sateuacan ngitung deui nilai sadaya rékaman.

Pilihan tilu: ngadamel konstrain ditunda supados dipariksa ngan ukur dina waktos komitmen:

create table cars
(
  name text constraint pk_cars primary key,
  ord integer not null constraint uk_cars unique deferrable initially deferred
);

Kusabab logika pamundut awal urang mastikeun yén sadaya nilai unik dina waktos komitmen, éta bakal suksés.

Conto anu dibahas di luhur, tangtosna, sintétis pisan, tapi ngungkabkeun ideu. Dina aplikasi kami, kami nganggo konstrain nunggak pikeun nerapkeun logika anu tanggung jawab pikeun ngarengsekeun konflik nalika pamaké sakaligus dianggo kalayan objék widget dibagikeun dina dewan. Nganggo larangan sapertos kitu ngamungkinkeun urang ngajantenkeun kode aplikasi langkung saderhana.

Sacara umum, gumantung kana jinis konstrain, Postgres ngagaduhan tilu tingkatan granularitas pikeun mariksa aranjeunna: tingkat baris, transaksi, sareng ekspresi.
Postgres: bloat, pg_repack sareng konstrain nunggak
sumber: begriffs

CHECK sareng NOT NULL sok dipariksa di tingkat baris; pikeun larangan anu sanés, sakumaha anu katingali tina tabél, aya pilihan anu béda. Anjeun tiasa maca deui di dieu.

Pikeun nyimpulkeun sakeudeung, konstrain nunggak dina sababaraha situasi nyadiakeun kode nu leuwih bisa dibaca jeung paréntah pangsaeutikna. Najan kitu, anjeun kudu mayar ieu ku complicating prosés debugging, saprak momen kasalahan lumangsung sarta momen anjeun manggihan ngeunaan eta dipisahkeun dina jangka waktu nu. masalah sejen mungkin nyaeta scheduler nu bisa jadi teu salawasna bisa nyusunna rencana optimal lamun pamundut ngalibatkeun konstrain nunggak.

Perbaikan pg_repack

Kami parantos nutupan konstrain anu ditunda, tapi kumaha hubunganana sareng masalah urang? Hayu urang émut kasalahan anu katampi sateuacana:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

Éta lumangsung nalika data disalin tina méja log ka méja énggal. Ieu sigana aneh sabab ... data dina tabel log ieu komitmen babarengan jeung data dina tabel sumber. Mun aranjeunna nyugemakeun konstrain tina tabel aslina, kumaha maranéhna bisa ngalanggar konstrain sarua dina anyar?

Sakumaha nu kabukti ayeuna kaluar, akar masalah perenahna di hambalan saméméhna tina pg_repack, nu nyiptakeun ukur indéks, tapi teu konstrain: tabel heubeul miboga konstrain unik, sarta nu anyar dijieun indéks unik gantina.

Postgres: bloat, pg_repack sareng konstrain nunggak

Kadé dicatet di dieu yén lamun konstrain téh normal jeung teu ditunda, mangka indéks unik dijieun gantina sarua jeung konstrain ieu, sabab Konstrain unik dina Postgres dilaksanakeun ku nyieun indéks unik. Tapi dina kasus konstrain nunggak, paripolahna henteu sami, sabab indéksna teu tiasa ditunda sareng sok dipariksa nalika paréntah sql dieksekusi.

Ku kituna, hakekat masalah perenahna di "reureuh" tina dipariksa: dina tabel aslina eta lumangsung dina waktu komitmen, sarta dina tabel anyar dina waktu paréntah SQL dieksekusi. Ieu hartosna urang kedah mastikeun yén pamariksaan dilaksanakeun sami dina dua kasus: boh sok ditunda, atanapi sok langsung.

Janten ide naon anu urang gaduh?

Jieun indéks sarupa nunggak

Gagasan anu kahiji nyaéta ngalaksanakeun duanana pamariksaan dina modeu langsung. Ieu bisa ngahasilkeun sababaraha larangan positif palsu, tapi lamun aya sababaraha di antarana, ieu teu kudu mangaruhan karya pamaké, sabab konflik misalna mangrupakeun kaayaan normal pikeun aranjeunna. Éta lumangsung, contona, nalika dua pamaké mimiti ngédit widget sarua dina waktos anu sareng, sarta klien tina pamaké kadua teu boga waktu pikeun nampa informasi yén widget geus dipeungpeuk pikeun ngédit ku pamaké munggaran. Dina kaayaan kitu, server nolak pamaké kadua, sarta klien na gulung deui parobahanana sarta blok widget. Sakedap deui, nalika pangguna munggaran réngsé ngédit, anu kadua bakal nampi inpormasi yén widget henteu deui diblokir sareng bakal tiasa ngulang tindakanna.

Postgres: bloat, pg_repack sareng konstrain nunggak

Pikeun mastikeun yén cék salawasna dina modeu teu ditunda, kami nyiptakeun indéks énggal anu sami sareng konstrain anu ditunda aslina:

CREATE UNIQUE INDEX CONCURRENTLY uk_tablename__immediate ON tablename (id, index);
-- run pg_repack
DROP INDEX CONCURRENTLY uk_tablename__immediate;

Dina lingkungan tés, kami nampi ngan ukur sababaraha kasalahan anu dipiharep. Sukses! Urang lumpat pg_repack deui dina produksi jeung meunang 5 kasalahan dina klaster munggaran dina jam gawé. Ieu hasil nu bisa ditarima. Nanging, dina klaster kadua jumlah kasalahan ningkat sacara signifikan sareng urang kedah ngeureunkeun pg_repack.

Naha éta kajadian? Kamungkinan kasalahan lumangsung gumantung kana sabaraha pangguna anu damel sareng widget anu sami dina waktos anu sami. Tétéla, dina momen éta aya loba pangsaeutikna parobahan kalapa kalawan data nu disimpen dina klaster munggaran ti on batur, i.e. kami ngan "untung".

Ide teu jalan. Dina titik éta, urang nempo dua solusi séjén: nulis ulang kode aplikasi urang dispense kalawan konstrain nunggak, atawa "ngajarkeun" pg_repack digawekeun ku aranjeunna. Urang milih nu kadua.

Ngaganti indexes dina tabel anyar kalawan konstrain nunggak tina tabel aslina

Tujuan révisi éta écés - upami tabel aslina ngagaduhan konstrain anu ditunda, maka pikeun anu énggal anjeun kedah nyiptakeun konstrain sapertos kitu, sanés indéks.

Pikeun nguji parobahan kami, kami nulis tés basajan:

  • méja kalayan konstrain nunggak sareng hiji catetan;
  • nyelapkeun data dina loop anu bentrok jeung rékaman aya;
  • ngalakukeun apdet - data henteu deui konflik;
  • ngalakukeun parobahan.

create table test_table
(
  id serial,
  val int,
  constraint uk_test_table__val unique (val) deferrable initially deferred 
);

INSERT INTO test_table (val) VALUES (0);
FOR i IN 1..10000 LOOP
  BEGIN
    INSERT INTO test_table VALUES (0) RETURNING id INTO v_id;
    UPDATE test_table set val = i where id = v_id;
    COMMIT;
  END;
END LOOP;

Versi aslina pg_repack salawasna nabrak dina sisipan munggaran, versi dirobah digawé tanpa kasalahan. Hebat.

Urang balik ka produksi jeung deui meunang kasalahan dina fase sarua nyalin data tina tabel log ka nu anyar:

$ ./pg_repack -t tablename -o id
INFO: repacking table "tablename"
ERROR: query failed: 
    ERROR: duplicate key value violates unique constraint "index_16508"
DETAIL:  Key (id, index)=(100500, 42) already exists.

Kaayaan klasik: sadayana tiasa dianggo dina lingkungan uji, tapi henteu dina produksi?!

APPLY_COUNT jeung simpang dua bets

Urang mimiti nganalisis kode sacara harfiah baris demi baris sarta manggihan hiji titik penting: data ditransferkeun tina tabel log ka nu anyar dina bets, konstanta APPLY_COUNT nunjukkeun ukuran bets:

for (;;)
{
num = apply_log(connection, table, APPLY_COUNT);

if (num > MIN_TUPLES_BEFORE_SWITCH)
     continue;  /* there might be still some tuples, repeat. */
...
}

Masalahna nyaéta yén data tina transaksi asli, dimana sababaraha operasi berpotensi ngalanggar konstrain, nalika ditransfer, tiasa mungkas di simpang dua bets - satengah paréntah bakal dilakukeun dina angkatan kahiji, sareng satengah sanésna. dina kadua. Sareng di dieu, gumantung kana nasib anjeun: upami tim henteu ngalanggar naon waé dina bets kahiji, maka sadayana henteu kunanaon, tapi upami aranjeunna ngalakukeun, aya kasalahan.

APPLY_COUNT sami sareng 1000 rékaman, anu ngajelaskeun naha tés kami suksés - aranjeunna henteu nutupan kasus "simpang angkatan". Kami nganggo dua paréntah - nyelapkeun sareng ngapdet, janten persis 500 transaksi tina dua paréntah sok disimpen dina hiji angkatan sareng kami henteu ngalaman masalah. Saatos nambihan apdet kadua, éditan urang lirén damel:

FOR i IN 1..10000 LOOP
  BEGIN
    INSERT INTO test_table VALUES (1) RETURNING id INTO v_id;
    UPDATE test_table set val = i where id = v_id;
    UPDATE test_table set val = i where id = v_id; -- one more update
    COMMIT;
  END;
END LOOP;

Janten, tugas salajengna nyaéta mastikeun yén data tina tabel asli, anu dirobih dina hiji transaksi, ditungtungan dina tabel énggal ogé dina hiji transaksi.

Panolakan tina batching

Sareng deui kami ngagaduhan dua solusi. Kahiji: hayu urang sagemblengna abandon partisi kana bets sarta mindahkeun data dina hiji transaksi. Kauntungannana leyuran ieu kesederhanaan - parobahan kode diperlukeun éta minimal (ku jalan kitu, dina versi heubeul pg_reorg digawé persis kawas éta). Tapi aya masalah - urang nyieun urus lila-ngajalankeun, sarta ieu, sakumaha ceuk saméméhna, anceman kana mecenghulna bloat anyar.

Solusi kadua leuwih kompleks, tapi meureun leuwih bener: nyieun kolom dina tabel log jeung identifier tina urus nu ditambahkeun data kana tabél. Teras, nalika urang nyalin data, urang tiasa ngagolongkeun ku atribut ieu sareng mastikeun yén parobihan anu aya hubunganana ditransfer babarengan. bets bakal kabentuk tina sababaraha transaksi (atawa hiji badag) jeung ukuranana bakal rupa-rupa gumantung kana sabaraha data ieu robah dina transaksi ieu. Kadé dicatet yén saprak data tina transaksi béda asup kana tabel log dina urutan acak, eta moal deui mungkin maca eta sequentially, sakumaha ieu saméméhna. seqscan pikeun tiap pamundut kalawan nyaring ku tx_id mahal teuing, hiji indéks diperlukeun, tapi ogé bakal ngalambatkeun turun metoda alatan overhead ngamutahirkeun eta. Sacara umum, sakumaha salawasna, anjeun kudu kurban hal.

Janten, urang mutuskeun pikeun ngamimitian ku pilihan anu munggaran, sabab éta langkung saderhana. Kahiji, éta perlu ngartos naha urus panjang bakal jadi masalah nyata. Kusabab transfer utama data tina méja lami ka anu énggal ogé lumangsung dina hiji transaksi anu panjang, patarosan janten "sabaraha urang bakal ningkatkeun transaksi ieu?" Durasi transaksi munggaran gumantung utamana dina ukuran tabel. Durasi nu anyar gumantung kana sabaraha parobahan ngumpulkeun dina tabel salila mindahkeun data, i.e. dina inténsitas beban. The pg_repack ngajalankeun lumangsung salila waktu beban jasa minimal, sarta volume parobahan éta disproportionately leutik dibandingkeun ukuran aslina tabel. Urang mutuskeun yén urang bisa maranéh ngalalaworakeun kana waktu urus anyar (pikeun babandingan, rata-rata 1 jam 2-3 menit).

Percobaan éta positif. Ngaluncurkeun dina produksi ogé. Pikeun kajelasan, ieu gambar kalayan ukuran salah sahiji database sanggeus ngajalankeun:

Postgres: bloat, pg_repack sareng konstrain nunggak

Kusabab kami parantos puas sareng solusi ieu, kami henteu nyobian ngalaksanakeun anu kadua, tapi urang mikirkeun kamungkinan ngabahas éta sareng pamekar extension. Révisi kami ayeuna, hanjakalna, henteu acan siap pikeun publikasi, sabab kami ngan ukur ngarengsekeun masalah sareng larangan anu ditunda anu unik, sareng pikeun patch anu lengkep kedah nyayogikeun dukungan pikeun jinis anu sanés. Kami ngarepkeun tiasa ngalakukeun ieu ka hareup.

Panginten anjeun gaduh patarosan, naha urang malah aub dina carita ieu kalayan modifikasi pg_repack, sareng henteu, contona, nganggo analogna? Di sawatara titik urang ogé mikir ngeunaan ieu, tapi pangalaman positif ngagunakeun eta saméméhna, dina tabel tanpa konstrain nunggak, ngamotivasi kami pikeun nyobaan ngartos hakekat masalah na ngalereskeun eta. Salaku tambahan, ngagunakeun solusi anu sanés ogé peryogi waktos pikeun ngalaksanakeun tés, janten urang mutuskeun yén urang bakal nyobian ngalereskeun masalah di jerona, sareng upami urang sadar yén urang henteu tiasa ngalakukeun ieu dina waktos anu lumrah, maka urang bakal ngamimitian ningali analogi. .

papanggihan

Naon anu urang tiasa nyarankeun dumasar kana pangalaman urang sorangan:

  1. Ngawas bloat Anjeun. Dumasar data ngawaskeun, anjeun tiasa ngartos kumaha ogé autovacuum dikonpigurasi.
  2. Saluyukeun AUTOVACUUM pikeun ngajaga kembung dina tingkat anu tiasa ditampi.
  3. Upami bloat masih tumbuh sareng anjeun moal tiasa ngatasina nganggo alat anu luar biasa, ulah sieun nganggo ekstensi éksternal. Hal utama pikeun nguji sagalana ogé.
  4. Tong sieun ngarobih solusi éksternal pikeun nyocogkeun ka kabutuhan anjeun - sakapeung ieu tiasa langkung efektif bahkan langkung gampang tibatan ngarobih kode anjeun nyalira.

sumber: www.habr.com

Tambahkeun komentar