Postgres: kembung, pg_repack dan kekangan tertunda

Postgres: kembung, pg_repack dan kekangan tertunda

Kesan bloat pada jadual dan indeks diketahui secara meluas dan bukan sahaja terdapat dalam Postgres. Terdapat cara untuk menanganinya di luar kotak, seperti VACUUM FULL atau CLUSTER, tetapi ia mengunci jadual semasa operasi dan oleh itu tidak boleh digunakan sentiasa.

Artikel itu akan mengandungi sedikit teori tentang bagaimana kembung berlaku, bagaimana anda boleh melawannya, tentang kekangan tertunda dan masalah yang mereka bawa kepada penggunaan sambungan pg_repack.

Artikel ini ditulis berdasarkan ucapan saya di PgConf.Russia 2020.

Mengapa kembung berlaku?

Postgres adalah berdasarkan model berbilang versi (MVCC). Intipatinya ialah setiap baris dalam jadual boleh mempunyai beberapa versi, manakala urus niaga melihat tidak lebih daripada satu daripada versi ini, tetapi tidak semestinya versi yang sama. Ini membolehkan beberapa urus niaga berfungsi secara serentak dan hampir tidak mempunyai kesan antara satu sama lain.

Jelas sekali, semua versi ini perlu disimpan. Postgres berfungsi dengan halaman memori demi halaman dan halaman ialah jumlah minimum data yang boleh dibaca dari cakera atau bertulis. Mari lihat contoh kecil untuk memahami bagaimana ini berlaku.

Katakan kita mempunyai jadual yang telah kita tambahkan beberapa rekod. Data baharu telah muncul di halaman pertama fail tempat jadual disimpan. Ini ialah versi langsung baris yang tersedia untuk transaksi lain selepas komit (untuk memudahkan, kami akan menganggap bahawa tahap pengasingan ialah Read Committed).

Postgres: kembung, pg_repack dan kekangan tertunda

Kami kemudian mengemas kini salah satu entri, dengan itu menandakan versi lama sebagai tidak lagi berkaitan.

Postgres: kembung, pg_repack dan kekangan tertunda

Langkah demi langkah, mengemas kini dan memadam versi baris, kami mendapat halaman di mana kira-kira separuh daripada data adalah "sampah". Data ini tidak kelihatan kepada mana-mana transaksi.

Postgres: kembung, pg_repack dan kekangan tertunda

Postgres mempunyai mekanisme VACUUM, yang membersihkan versi usang dan memberi ruang untuk data baharu. Tetapi jika ia tidak dikonfigurasikan dengan cukup agresif atau sibuk bekerja dalam jadual lain, maka "data sampah" kekal, dan kami perlu menggunakan halaman tambahan untuk data baharu.

Jadi dalam contoh kami, pada satu ketika jadual akan terdiri daripada empat halaman, tetapi hanya separuh daripadanya akan mengandungi data langsung. Akibatnya, apabila mengakses jadual, kami akan membaca lebih banyak data daripada yang diperlukan.

Postgres: kembung, pg_repack dan kekangan tertunda

Walaupun VACUUM kini memadamkan semua versi baris yang tidak berkaitan, keadaan tidak akan bertambah baik secara mendadak. Kami akan mempunyai ruang kosong dalam halaman atau bahkan keseluruhan halaman untuk baris baharu, tetapi kami masih akan membaca lebih banyak data daripada yang diperlukan.
By the way, jika halaman kosong sepenuhnya (yang kedua dalam contoh kami) berada di penghujung fail, maka VACUUM akan dapat memangkasnya. Tetapi sekarang dia berada di tengah-tengah, jadi tiada apa yang boleh dilakukan dengannya.

Postgres: kembung, pg_repack dan kekangan tertunda

Apabila bilangan halaman kosong atau sangat jarang menjadi besar, yang dipanggil bloat, ia mula menjejaskan prestasi.

Semua yang diterangkan di atas adalah mekanisme berlakunya kembung dalam jadual. Dalam indeks ini berlaku dengan cara yang sama.

Adakah saya mempunyai perut kembung?

Terdapat beberapa cara untuk menentukan sama ada anda mengalami kembung. Idea yang pertama ialah menggunakan statistik Postgres dalaman, yang mengandungi maklumat anggaran tentang bilangan baris dalam jadual, bilangan baris "langsung", dll. Anda boleh menemui banyak variasi skrip siap sedia di Internet. Kami ambil sebagai asas skrip daripada Pakar PostgreSQL, yang boleh menilai jadual bloat bersama-sama dengan indeks roti bakar dan bloat btree. Dalam pengalaman kami, ralatnya adalah 10-20%.

Cara lain ialah menggunakan sambungan pgstattuple, yang membolehkan anda melihat ke dalam halaman dan mendapatkan kedua-dua anggaran dan nilai bloat yang tepat. Tetapi dalam kes kedua, anda perlu mengimbas keseluruhan jadual.

Kami menganggap nilai kembung yang kecil, sehingga 20%, boleh diterima. Ia boleh dianggap sebagai analogi faktor pengisi untuk jadual и Indeks. Pada 50% dan ke atas, masalah prestasi mungkin bermula.

Cara-cara untuk memerangi perut kembung

Postgres mempunyai beberapa cara untuk menangani masalah perut kembung, tetapi ia tidak selalunya sesuai untuk semua orang.

Konfigurasikan AUTOVACUUM supaya kembung tidak berlaku. Atau lebih tepat lagi, untuk mengekalkannya pada tahap yang boleh diterima oleh anda. Ini kelihatan seperti nasihat "kapten", tetapi sebenarnya ini tidak selalu mudah untuk dicapai. Contohnya, anda mempunyai pembangunan aktif dengan perubahan tetap pada skema data, atau sejenis pemindahan data sedang berlaku. Akibatnya, profil pemuatan anda mungkin kerap berubah dan biasanya berbeza dari jadual ke jadual. Ini bermakna anda perlu sentiasa berusaha sedikit ke hadapan dan melaraskan AUTOVACUUM kepada profil yang berubah bagi setiap jadual. Tetapi jelas ini tidak mudah dilakukan.

Satu lagi sebab biasa mengapa AUTOVACUUM tidak dapat bersaing dengan jadual adalah kerana terdapat transaksi jangka panjang yang menghalangnya daripada membersihkan data yang tersedia untuk transaksi tersebut. Pengesyoran di sini juga jelas - singkirkan urus niaga "terjuntai" dan kurangkan masa urus niaga aktif. Tetapi jika beban pada aplikasi anda adalah gabungan OLAP dan OLTP, maka anda boleh mempunyai banyak kemas kini yang kerap dan pertanyaan pendek pada masa yang sama, serta operasi jangka panjang - contohnya, membina laporan. Dalam keadaan sedemikian, perlu difikirkan tentang menyebarkan beban merentasi pangkalan yang berbeza, yang akan membolehkan penalaan lebih terperinci bagi setiap daripadanya.

Contoh lain - walaupun profil adalah homogen, tetapi pangkalan data berada di bawah beban yang sangat tinggi, maka AUTOVACUUM yang paling agresif mungkin tidak dapat mengatasinya, dan kembung akan berlaku. Penskalaan (menegak atau mendatar) adalah satu-satunya penyelesaian.

Apa yang perlu dilakukan dalam situasi di mana anda telah menyediakan AUTOVACUUM, tetapi kembung itu terus berkembang.

Pasukan VACUUM PENUH membina semula kandungan jadual dan indeks dan hanya meninggalkan data yang berkaitan di dalamnya. Untuk menghapuskan bloat, ia berfungsi dengan sempurna, tetapi semasa pelaksanaannya, kunci eksklusif pada meja ditangkap (AccessExclusiveLock), yang tidak akan membenarkan melaksanakan pertanyaan pada jadual ini, malah memilih. Jika anda mampu untuk menghentikan perkhidmatan anda atau sebahagian daripadanya untuk beberapa waktu (daripada puluhan minit hingga beberapa jam bergantung pada saiz pangkalan data dan perkakasan anda), maka pilihan ini adalah yang terbaik. Malangnya, kami tidak mempunyai masa untuk menjalankan VACUUM FULL semasa penyelenggaraan berjadual, jadi kaedah ini tidak sesuai untuk kami.

Pasukan KLUSTER Membina semula kandungan jadual dengan cara yang sama seperti VACUUM FULL, tetapi membenarkan anda untuk menentukan indeks mengikut mana data akan dipesan secara fizikal pada cakera (tetapi pada masa hadapan pesanan itu tidak dijamin untuk baris baharu). Dalam situasi tertentu, ini adalah pengoptimuman yang baik untuk beberapa pertanyaan - dengan membaca berbilang rekod mengikut indeks. Kelemahan arahan adalah sama seperti VACUUM FULL - ia mengunci jadual semasa operasi.

Pasukan REINDEX serupa dengan dua sebelumnya, tetapi membina semula indeks tertentu atau semua indeks jadual. Kunci lebih lemah sedikit: ShareLock pada jadual (menghalang pengubahsuaian, tetapi membenarkan pilih) dan AccessExclusiveLock pada indeks dibina semula (menyekat pertanyaan menggunakan indeks ini). Walau bagaimanapun, dalam versi ke-12 Postgres parameter muncul SERENTAK, yang membolehkan anda membina semula indeks tanpa menyekat penambahan serentak, pengubahsuaian atau pemadaman rekod.

Dalam versi Postgres yang lebih awal, anda boleh mencapai hasil yang serupa dengan REINDEX SERENTAK menggunakan CIPTA INDEKS SECARA SAMBIL. Ia membolehkan anda mencipta indeks tanpa penguncian yang ketat (ShareUpdateExclusiveLock, yang tidak mengganggu pertanyaan selari), kemudian menggantikan indeks lama dengan yang baharu dan padamkan indeks lama. Ini membolehkan anda menghapuskan index bloat tanpa mengganggu permohonan anda. Adalah penting untuk mempertimbangkan bahawa apabila membina semula indeks akan ada beban tambahan pada subsistem cakera.

Oleh itu, jika untuk indeks terdapat cara untuk menghapuskan kembung "dengan cepat," maka tidak ada satu pun untuk jadual. Di sinilah pelbagai sambungan luaran dimainkan: pg_repack (dahulunya pg_reorg), pgcompact, pgcompacttable dan lain lain. Dalam artikel ini, saya tidak akan membandingkannya dan hanya akan bercakap tentang pg_repack, yang, selepas beberapa pengubahsuaian, kami menggunakan diri kami sendiri.

Cara pg_repack berfungsi

Postgres: kembung, pg_repack dan kekangan tertunda
Katakan kita mempunyai jadual yang benar-benar biasa - dengan indeks, sekatan dan, malangnya, dengan kembung. Langkah pertama pg_repack ialah membuat jadual log untuk menyimpan data tentang semua perubahan semasa ia sedang berjalan. Pencetus akan meniru perubahan ini untuk setiap sisipan, kemas kini dan padam. Kemudian jadual dibuat, serupa dengan yang asal dalam struktur, tetapi tanpa indeks dan sekatan, supaya tidak melambatkan proses memasukkan data.

Seterusnya, pg_repack memindahkan data daripada jadual lama ke jadual baharu, secara automatik menapis semua baris yang tidak berkaitan, dan kemudian mencipta indeks untuk jadual baharu. Semasa pelaksanaan semua operasi ini, perubahan terkumpul dalam jadual log.

Langkah seterusnya ialah memindahkan perubahan ke jadual baharu. Penghijrahan dilakukan melalui beberapa lelaran dan apabila terdapat kurang daripada 20 entri yang tinggal dalam jadual log, pg_repack memperoleh kunci yang kuat, memindahkan data terkini dan menggantikan jadual lama dengan yang baharu dalam jadual sistem Postgres. Ini adalah satu-satunya dan masa yang sangat singkat apabila anda tidak akan dapat bekerja dengan meja. Selepas ini, jadual lama dan jadual dengan log dipadamkan dan ruang dibebaskan dalam sistem fail. Proses selesai.

Segala-galanya kelihatan hebat dalam teori, tetapi apa yang berlaku dalam amalan? Kami menguji pg_repack tanpa beban dan di bawah beban, dan menyemak operasinya sekiranya berhenti pramatang (dengan kata lain, menggunakan Ctrl+C). Semua ujian adalah positif.

Kami pergi ke kedai makanan - dan kemudian semuanya tidak berjalan seperti yang kami harapkan.

Pancake pertama dijual

Pada kelompok pertama kami menerima ralat tentang pelanggaran kekangan 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.

Had ini mempunyai nama yang dijana secara automatik index_16508 - ia dicipta oleh pg_repack. Berdasarkan atribut yang disertakan dalam komposisinya, kami menentukan kekangan "kami" yang sepadan dengannya. Masalahnya ternyata bahawa ini bukan batasan biasa, tetapi tertunda (kekangan tertunda), iaitu pengesahannya dilakukan lewat daripada arahan sql, yang membawa kepada akibat yang tidak dijangka.

Kekangan tertunda: mengapa ia diperlukan dan cara ia berfungsi

Sedikit teori tentang sekatan tertunda.
Mari kita pertimbangkan contoh mudah: kita mempunyai buku rujukan jadual kereta dengan dua sifat - nama dan susunan kereta dalam direktori.
Postgres: kembung, pg_repack dan kekangan tertunda

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



Katakan kita perlu menukar kereta pertama dan kedua. Penyelesaian yang mudah adalah untuk mengemas kini nilai pertama kepada yang kedua, dan yang kedua kepada yang pertama:

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

Tetapi apabila kami menjalankan kod ini, kami menjangkakan pelanggaran kekangan kerana susunan nilai dalam jadual adalah unik:

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

Bagaimanakah saya boleh melakukannya secara berbeza? Pilihan satu: tambahkan penggantian nilai tambahan pada pesanan yang dijamin tidak wujud dalam jadual, contohnya "-1". Dalam pengaturcaraan, ini dipanggil "menukar nilai dua pembolehubah melalui satu pertiga." Satu-satunya kelemahan kaedah ini ialah kemas kini tambahan.

Pilihan dua: Reka bentuk semula jadual untuk menggunakan jenis data titik terapung untuk nilai pesanan dan bukannya integer. Kemudian, apabila mengemas kini nilai daripada 1, sebagai contoh, kepada 2.5, entri pertama akan "berdiri" secara automatik antara yang kedua dan ketiga. Penyelesaian ini berfungsi, tetapi terdapat dua batasan. Pertama, ia tidak akan berfungsi untuk anda jika nilai digunakan di suatu tempat dalam antara muka. Kedua, bergantung pada ketepatan jenis data, anda akan mempunyai bilangan sisipan yang terhad sebelum mengira semula nilai semua rekod.

Pilihan tiga: buat kekangan ditangguhkan supaya ia disemak hanya pada masa komit:

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

Memandangkan logik permintaan awal kami memastikan bahawa semua nilai adalah unik pada masa komit, ia akan berjaya.

Contoh yang dibincangkan di atas, sudah tentu, sangat sintetik, tetapi ia mendedahkan idea itu. Dalam aplikasi kami, kami menggunakan kekangan tertunda untuk melaksanakan logik yang bertanggungjawab untuk menyelesaikan konflik apabila pengguna bekerja secara serentak dengan objek widget yang dikongsi di papan. Menggunakan sekatan sedemikian membolehkan kami membuat kod aplikasi sedikit lebih mudah.

Secara umum, bergantung pada jenis kekangan, Postgres mempunyai tiga tahap butiran untuk menyemaknya: peringkat baris, transaksi dan ekspresi.
Postgres: kembung, pg_repack dan kekangan tertunda
Sumber: begriffs

CHECK dan NOT NULL sentiasa disemak pada peringkat baris; untuk sekatan lain, seperti yang boleh dilihat dari jadual, terdapat pilihan yang berbeza. Anda boleh membaca lebih lanjut di sini.

Untuk meringkaskan secara ringkas, kekangan tertunda dalam beberapa situasi menyediakan lebih banyak kod yang boleh dibaca dan lebih sedikit arahan. Walau bagaimanapun, anda perlu membayar untuk ini dengan merumitkan proses penyahpepijatan, memandangkan saat ralat berlaku dan saat anda mengetahui tentangnya dipisahkan dalam masa. Satu lagi masalah yang mungkin adalah bahawa penjadual mungkin tidak selalu dapat membina pelan optimum jika permintaan itu melibatkan kekangan tertunda.

Penambahbaikan pg_repack

Kami telah membincangkan apa itu kekangan tertunda, tetapi bagaimana ia berkaitan dengan masalah kami? Mari kita ingat kesilapan yang kita terima sebelum ini:

$ ./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.

Ia berlaku apabila data disalin daripada jadual log ke jadual baharu. Ini kelihatan pelik kerana... data dalam jadual log dilakukan bersama-sama dengan data dalam jadual sumber. Jika mereka memenuhi kekangan jadual asal, bagaimana mereka boleh melanggar kekangan yang sama dalam yang baharu?

Ternyata, punca masalah terletak pada langkah pg_repack sebelumnya, yang hanya mencipta indeks, tetapi bukan kekangan: jadual lama mempunyai kekangan yang unik, dan yang baharu mencipta indeks unik sebaliknya.

Postgres: kembung, pg_repack dan kekangan tertunda

Adalah penting untuk diperhatikan di sini bahawa jika kekangan adalah normal dan tidak ditangguhkan, maka indeks unik yang dicipta sebaliknya adalah bersamaan dengan kekangan ini, kerana Kekangan unik dalam Postgres dilaksanakan dengan mencipta indeks unik. Tetapi dalam kes kekangan tertunda, tingkah laku tidak sama, kerana indeks tidak boleh ditangguhkan dan sentiasa diperiksa pada masa arahan sql dilaksanakan.

Oleh itu, intipati masalah terletak pada "kelewatan" semakan: dalam jadual asal ia berlaku pada masa komit, dan dalam jadual baru pada masa arahan sql dilaksanakan. Ini bermakna kita perlu memastikan bahawa semakan dilakukan sama dalam kedua-dua kes: sama ada sentiasa ditangguhkan atau sentiasa serta-merta.

Jadi apa idea yang kita ada?

Buat indeks yang serupa dengan tertunda

Idea pertama ialah melakukan kedua-dua semakan dalam mod segera. Ini mungkin menghasilkan beberapa sekatan positif palsu, tetapi jika terdapat sedikit daripadanya, ini tidak sepatutnya menjejaskan kerja pengguna, kerana konflik sebegitu adalah situasi biasa bagi mereka. Ia berlaku, sebagai contoh, apabila dua pengguna mula mengedit widget yang sama pada masa yang sama, dan pelanggan pengguna kedua tidak mempunyai masa untuk menerima maklumat bahawa widget itu telah disekat untuk diedit oleh pengguna pertama. Dalam keadaan sedemikian, pelayan menolak pengguna kedua, dan pelanggannya melancarkan semula perubahan dan menyekat widget. Tidak lama kemudian, apabila pengguna pertama menyelesaikan penyuntingan, pengguna kedua akan menerima maklumat bahawa widget tidak lagi disekat dan akan dapat mengulangi tindakan mereka.

Postgres: kembung, pg_repack dan kekangan tertunda

Untuk memastikan bahawa semakan sentiasa dalam mod tidak tertunda, kami mencipta indeks baharu yang serupa dengan kekangan tertunda asal:

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

Dalam persekitaran ujian, kami hanya menerima beberapa ralat yang dijangkakan. Berjaya! Kami menjalankan pg_repack sekali lagi pada pengeluaran dan mendapat 5 ralat pada kelompok pertama dalam satu jam kerja. Ini adalah keputusan yang boleh diterima. Walau bagaimanapun, sudah pada kelompok kedua bilangan ralat meningkat dengan ketara dan kami terpaksa menghentikan pg_repack.

Mengapa ia berlaku? Kemungkinan ralat berlaku bergantung pada bilangan pengguna yang bekerja dengan widget yang sama pada masa yang sama. Nampaknya, pada masa itu terdapat lebih sedikit perubahan kompetitif dengan data yang disimpan pada kelompok pertama berbanding yang lain, i.e. kami hanya "bertuah".

Idea itu tidak berjaya. Pada ketika itu, kami melihat dua penyelesaian lain: tulis semula kod aplikasi kami untuk mengetepikan kekangan tertunda atau "ajar" pg_repack untuk bekerja dengan mereka. Kami memilih yang kedua.

Gantikan indeks dalam jadual baharu dengan kekangan tertunda daripada jadual asal

Tujuan semakan adalah jelas - jika jadual asal mempunyai kekangan tertunda, maka untuk yang baharu anda perlu membuat kekangan sedemikian, dan bukan indeks.

Untuk menguji perubahan kami, kami menulis ujian mudah:

  • jadual dengan kekangan tertunda dan satu rekod;
  • masukkan data dalam gelung yang bercanggah dengan rekod sedia ada;
  • lakukan kemas kini - data tidak lagi bercanggah;
  • melakukan perubahan.

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 asal pg_repack sentiasa ranap pada sisipan pertama, versi yang diubah suai berfungsi tanpa ralat. Hebat.

Kami pergi ke pengeluaran dan sekali lagi mendapat ralat pada fasa yang sama menyalin data daripada jadual log kepada yang baharu:

$ ./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.

Situasi klasik: semuanya berfungsi dalam persekitaran ujian, tetapi tidak dalam pengeluaran?!

APPLY_COUNT dan persimpangan dua kelompok

Kami mula menganalisis kod secara literal baris demi baris dan menemui satu perkara penting: data dipindahkan dari jadual log kepada yang baharu dalam kelompok, pemalar APPLY_COUNT menunjukkan saiz kumpulan:

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

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

Masalahnya ialah data daripada transaksi asal, di mana beberapa operasi berpotensi melanggar kekangan, apabila dipindahkan, boleh berakhir di persimpangan dua kelompok - separuh daripada arahan akan dilakukan dalam kelompok pertama, dan separuh lagi dalam yang kedua. Dan di sini, bergantung pada nasib anda: jika pasukan tidak melanggar apa-apa dalam kumpulan pertama, maka semuanya baik-baik saja, tetapi jika mereka melakukannya, ralat berlaku.

APPLY_COUNT adalah bersamaan dengan 1000 rekod, yang menerangkan sebab ujian kami berjaya - mereka tidak meliputi kes "simpang kelompok". Kami menggunakan dua arahan - masukkan dan kemas kini, jadi tepat 500 transaksi dua arahan sentiasa diletakkan dalam satu kelompok dan kami tidak mengalami sebarang masalah. Selepas menambah kemas kini kedua, pengeditan kami berhenti berfungsi:

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;

Jadi, tugas seterusnya adalah untuk memastikan bahawa data daripada jadual asal, yang telah ditukar dalam satu transaksi, berakhir dalam jadual baharu juga dalam satu transaksi.

Keengganan daripada batching

Dan sekali lagi kami mempunyai dua penyelesaian. Pertama: mari kita tinggalkan sepenuhnya pembahagian ke dalam kelompok dan pindahkan data dalam satu transaksi. Kelebihan penyelesaian ini adalah kesederhanaannya - perubahan kod yang diperlukan adalah minimum (dengan cara ini, dalam versi lama pg_reorg berfungsi sama seperti itu). Tetapi ada masalah - kami mencipta transaksi yang berjalan lama, dan ini, seperti yang dikatakan sebelum ini, adalah ancaman kepada kemunculan kembung baru.

Penyelesaian kedua adalah lebih kompleks, tetapi mungkin lebih betul: buat lajur dalam jadual log dengan pengecam transaksi yang menambahkan data pada jadual. Kemudian, apabila kami menyalin data, kami boleh mengumpulkannya mengikut atribut ini dan memastikan perubahan yang berkaitan dipindahkan bersama-sama. Kumpulan akan dibentuk daripada beberapa transaksi (atau satu transaksi besar) dan saiznya akan berbeza-beza bergantung pada jumlah data yang telah diubah dalam transaksi ini. Adalah penting untuk ambil perhatian bahawa kerana data daripada urus niaga yang berbeza memasuki jadual log dalam susunan rawak, ia tidak lagi boleh membacanya secara berurutan, seperti sebelum ini. seqscan untuk setiap permintaan dengan penapisan oleh tx_id adalah terlalu mahal, indeks diperlukan, tetapi ia juga akan memperlahankan kaedah kerana overhed mengemas kininya. Secara umum, seperti biasa, anda perlu mengorbankan sesuatu.

Jadi, kami memutuskan untuk memulakan dengan pilihan pertama, kerana ia lebih mudah. Pertama, adalah perlu untuk memahami sama ada transaksi yang panjang akan menjadi masalah sebenar. Memandangkan pemindahan data utama dari jadual lama kepada yang baharu juga berlaku dalam satu urus niaga yang panjang, soalan berubah menjadi "berapa banyak kami akan meningkatkan transaksi ini?" Tempoh transaksi pertama bergantung terutamanya pada saiz jadual. Tempoh yang baharu bergantung pada berapa banyak perubahan terkumpul dalam jadual semasa pemindahan data, i.e. pada keamatan beban. Larian pg_repack berlaku semasa masa beban perkhidmatan yang minimum, dan volum perubahan adalah kecil secara tidak seimbang berbanding dengan saiz asal jadual. Kami memutuskan bahawa kami boleh mengabaikan masa transaksi baharu (sebagai perbandingan, secara purata ialah 1 jam 2-3 minit).

Eksperimen adalah positif. Pelancaran pada pengeluaran juga. Untuk kejelasan, berikut ialah gambar dengan saiz salah satu pangkalan data selepas dijalankan:

Postgres: kembung, pg_repack dan kekangan tertunda

Memandangkan kami benar-benar berpuas hati dengan penyelesaian ini, kami tidak cuba melaksanakan yang kedua, tetapi kami sedang mempertimbangkan kemungkinan untuk membincangkannya dengan pembangun sambungan. Semakan semasa kami, malangnya, masih belum bersedia untuk diterbitkan, kerana kami hanya menyelesaikan masalah dengan sekatan tertunda yang unik, dan untuk tampalan sepenuhnya adalah perlu untuk menyediakan sokongan untuk jenis lain. Kami berharap dapat melakukan ini pada masa akan datang.

Mungkin anda mempunyai soalan, mengapa kami terlibat dalam cerita ini dengan pengubahsuaian pg_repack, dan tidak, sebagai contoh, menggunakan analognya? Pada satu ketika kami juga memikirkan perkara ini, tetapi pengalaman positif menggunakannya lebih awal, di atas meja tanpa kekangan tertunda, mendorong kami untuk cuba memahami intipati masalah dan memperbaikinya. Di samping itu, menggunakan penyelesaian lain juga memerlukan masa untuk menjalankan ujian, jadi kami memutuskan bahawa kami akan cuba menyelesaikan masalah di dalamnya terlebih dahulu, dan jika kami menyedari bahawa kami tidak dapat melakukan ini dalam masa yang munasabah, maka kami akan mula melihat analog .

Penemuan

Perkara yang boleh kami cadangkan berdasarkan pengalaman kami sendiri:

  1. Pantau kembung perut anda. Berdasarkan data pemantauan, anda boleh memahami sejauh mana autovakum dikonfigurasikan.
  2. Laraskan AUTOVACUUM untuk mengekalkan kembung pada tahap yang boleh diterima.
  3. Jika kembung masih berkembang dan anda tidak dapat mengatasinya menggunakan alat luar kotak, jangan takut untuk menggunakan sambungan luaran. Perkara utama adalah untuk menguji segala-galanya dengan baik.
  4. Jangan takut untuk mengubah suai penyelesaian luaran untuk memenuhi keperluan anda - kadangkala ini boleh menjadi lebih berkesan dan lebih mudah daripada menukar kod anda sendiri.

Sumber: www.habr.com

Tambah komen