WAL di PostgreSQL: 4. Pengaturan log

Jadi, kami berkenalan dengan perangkat buffer cache dan, menggunakan contohnya, kami menyadari bahwa ketika konten RAM hilang selama kegagalan, log pra-tulis diperlukan untuk memulihkan. Ukuran file log yang diperlukan dan waktu pemulihan terbatas karena pos pemeriksaan yang dieksekusi secara berkala.

Dalam artikel sebelumnya, kita telah melihat sejumlah besar pengaturan penting yang terkait dengan jurnal dengan satu atau lain cara. Dalam artikel ini (yang terakhir dalam seri ini), kami akan mempertimbangkan masalah penyetelan yang belum dibahas: level log dan tujuannya, serta keandalan dan kinerja logging.

Tingkat log


Tujuan utama dari prerecord log adalah untuk memberikan kemampuan untuk pulih dari kegagalan. Tetapi, jika Anda masih harus membuat jurnal, itu dapat disesuaikan untuk tugas-tugas lain, menambahkan sejumlah informasi tambahan ke dalamnya. Ada beberapa level logging. Mereka diatur oleh parameter wal_level dan diatur sehingga log dari setiap level berikutnya mencakup semua yang masuk ke log dari level sebelumnya, ditambah sesuatu yang baru.

Minimal


Level minimum yang dimungkinkan ditetapkan oleh nilai wal_level = minimal dan menjamin pemulihan hanya setelah kegagalan. Untuk menghemat ruang, operasi yang terkait dengan pemrosesan data massal (seperti CREATE TABLE AS SELECT atau CREATE INDEX) tidak dicatat. Sebagai gantinya, data yang diperlukan segera ditulis ke disk, dan objek baru ditambahkan ke direktori sistem dan menjadi terlihat ketika transaksi dilakukan. Jika kegagalan terjadi selama operasi, data yang sudah direkam tetap tidak terlihat dan tidak melanggar konsistensi. Jika kegagalan terjadi setelah operasi selesai, semua yang diperlukan sudah pergi ke disk dan tidak perlu dicatat.

Ayo lihat. Pertama, atur level yang diperlukan (untuk ini Anda juga perlu mengubah parameter lain - max_wal_senders ).

=> ALTER SYSTEM SET wal_level = minimal; => ALTER SYSTEM SET max_wal_senders = 0; 

 student$ sudo pg_ctlcluster 11 main restart 

Perhatikan bahwa mengubah level memerlukan restart server.

Ingat posisi saat ini di log:

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353927BC (1 row) 

Sekarang mari kita buat tabel (CREATE TABLE AS SELECT) dan tulis posisi di log lagi. Jumlah data yang dipilih oleh pernyataan SELECT tidak masalah dalam kasus ini, jadi kami akan membatasi diri untuk satu baris.

 => CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353A7DFC (1 row) 

Dengan utilitas pg_waldump yang akrab, mari kita lihat entri log.

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353927BC -e 0/353A7DFC 

Beberapa detail, tentu saja, mungkin berbeda dari peluncuran ke peluncuran, tetapi dalam kasus ini, inilah yang terjadi. Entri manajer Heap2 merujuk pada pembersihan, di sini adalah pembersihan di halaman dari salah satu tabel dalam katalog sistem (objek sistem mudah dibedakan dengan mata telanjang dengan nomor "pendek" dalam rel):

 rmgr: Heap2 len (rec/tot): 59/ 7587, tx: 0, lsn: 0/353927BC, prev 0/35392788, desc: CLEAN remxid 101126, blkref #0: rel 1663/16386/1247 blk 8 FPW 

Lalu ada catatan tentang mendapatkan OID berikutnya untuk tabel yang akan kita buat:

 rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/35394574, prev 0/353927BC, desc: NEXTOID 82295 

Sekarang pembuatan tabel:

 rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/35394594, prev 0/35394574, desc: CREATE base/16386/74103 

Namun, memasukkan data ke dalam tabel tidak dicatat. Lalu ada banyak entri tentang memasukkan baris ke dalam tabel dan indeks yang berbeda - PostgreSQL ini mendaftarkan tabel yang dibuat dalam direktori sistem (saya berikan dalam bentuk singkatan):

 rmgr: Heap len (rec/tot): 203/ 203, tx: 101127, lsn: 0/353945C0, prev 0/35394594, desc: INSERT off 71, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 685, tx: 101127, lsn: 0/3539468C, prev 0/353945C0, desc: INSERT_LEAF off 37, blkref #0: rel 1663/16386/2703 blk 2 FPW ... rmgr: Btree len (rec/tot): 53/ 2393, tx: 101127, lsn: 0/353A747C, prev 0/353A6788, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW 

Dan akhirnya, fiksasi transaksi:

 rmgr: Transaction len (rec/tot): 34/ 34, tx: 101127, lsn: 0/353A7DD8, prev 0/353A747C, desc: COMMIT 2019-07-23 18:59:34.923124 MSK 

Replika


Ketika kami memulihkan sistem dari cadangan, kami mulai dari beberapa keadaan sistem file dan secara bertahap membawa data ke titik pemulihan, memutar ulang entri jurnal yang diarsipkan. Jumlah catatan tersebut bisa sangat besar (misalnya, beberapa hari), yaitu periode pemulihan tidak akan mencakup satu titik kontrol, tetapi banyak. Oleh karena itu, jelas bahwa tingkat minimum log tidak cukup - jika beberapa operasi tidak dicatat, kita tidak akan tahu bahwa itu perlu diulang. Untuk memulihkan dari cadangan, semua operasi harus dicatat.

Hal yang sama berlaku untuk replikasi - apa pun yang tidak dicatat tidak akan ditransfer ke replika dan tidak akan direproduksi. Tetapi, jika kita ingin mengeksekusi permintaan pada replika, itu masih rumit.

Pertama, kami memerlukan informasi tentang kunci eksklusif yang terjadi pada server utama, karena mungkin bertentangan dengan permintaan pada replika. Kunci tersebut dicatat dan diterapkan pada replika (atas nama proses startup).

Kedua, Anda harus dapat membuat snapshot data , dan untuk ini, seingat kami, informasi tentang transaksi yang sedang berlangsung diperlukan. Dalam kasus replika, kita berbicara tidak hanya tentang transaksi lokal, tetapi juga tentang transaksi di server utama. Satu-satunya cara untuk mengirimkan informasi ini adalah dengan menuliskannya secara berkala ke log (ini terjadi setiap 15 detik).

Level log, yang menjamin kemampuan untuk pulih dari cadangan dan kemungkinan replikasi fisik, ditentukan oleh nilai wal_level = replika . (Sebelum versi 9.6, ada dua level arsip terpisah dan hot_standby, tetapi kemudian digabungkan menjadi satu yang umum.)

Dimulai dengan PostgreSQL 10, level inilah yang ditetapkan secara default (dan sebelumnya minimal). Karenanya, atur ulang parameter ke nilai default:

 => ALTER SYSTEM RESET wal_level; => ALTER SYSTEM RESET max_wal_senders; 

 student$ sudo pg_ctlcluster 11 main restart 

Kami menghapus tabel dan mengulangi urutan tindakan yang sama persis seperti terakhir kali:

 => DROP TABLE wallevel; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353AF21C (1 row) 
 => CREATE TABLE wallevel AS SELECT 1 AS n; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/353BE51C (1 row) 

Sekarang periksa entri jurnal.

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/353AF21C -e 0/353BE51C 

Membersihkan, mendapatkan OID, membuat tabel dan mendaftar di direktori sistem - untuk saat ini, semuanya seperti sebelumnya:

 rmgr: Heap2 len (rec/tot): 58/ 58, tx: 0, lsn: 0/353AF21C, prev 0/353AF044, desc: CLEAN remxid 101128, blkref #0: rel 1663/16386/1247 blk 8 rmgr: XLOG len (rec/tot): 30/ 30, tx: 0, lsn: 0/353AF258, prev 0/353AF21C, desc: NEXTOID 82298 rmgr: Storage len (rec/tot): 42/ 42, tx: 0, lsn: 0/353AF278, prev 0/353AF258, desc: CREATE base/16386/74106 rmgr: Heap len (rec/tot): 203/ 203, tx: 101129, lsn: 0/353AF2A4, prev 0/353AF278, desc: INSERT off 73, blkref #0: rel 1663/16386/1247 blk 8 rmgr: Btree len (rec/tot): 53/ 717, tx: 101129, lsn: 0/353AF370, prev 0/353AF2A4, … rmgr: Btree len (rec/tot): 53/ 2413, tx: 101129, lsn: 0/353BD954, prev 0/353BCC44, desc: INSERT_LEAF off 10, blkref #0: rel 1664/0/1233 blk 1 FPW 

Tapi sesuatu yang baru. Catatan kunci eksklusif terkait dengan manajer Siaga - dalam hal ini, memblokir nomor transaksi (mengapa itu diperlukan, kami akan berbicara secara rinci dalam seri artikel berikutnya):

 rmgr: Standby len (rec/tot): 42/ 42, tx: 101129, lsn: 0/353BE2D8, prev 0/353BD954, desc: LOCK xid 101129 db 16386 rel 74106 

Dan ini adalah catatan tentang memasukkan baris ke dalam tabel kami (bandingkan rel nomor file dengan yang ditunjukkan di atas dalam catatan BUAT):

 rmgr: Heap len (rec/tot): 59/ 59, tx: 101129, lsn: 0/353BE304, prev 0/353BE2D8, desc: INSERT+INIT off 1, blkref #0: rel 1663/16386/74106 blk 0 

Catatan komitmen:

 rmgr: Transaction len (rec/tot): 421/ 421, tx: 101129, lsn: 0/353BE340, prev 0/353BE304, desc: COMMIT 2019-07-23 18:59:37.870333 MSK; inval msgs: catcache 74 catcache 73 catcache 74 catcache 73 catcache 50 catcache 49 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 catcache 7 catcache 6 snapshot 2608 relcache 74106 snapshot 1214 

Dan catatan lain, yang terjadi secara berkala dan tidak terikat pada transaksi yang diselesaikan, merujuk pada manajer Siaga dan melaporkan transaksi yang sedang berlangsung saat ini:

 rmgr: Standby len (rec/tot): 50/ 50, tx: 0, lsn: 0/353BE4E8, prev 0/353BE340, desc: RUNNING_XACTS nextXid 101130 latestCompletedXid 101129 oldestRunningXid 101130 

Logis


Akhirnya, level terakhir diatur oleh nilai parameter wal_level = logical dan memberikan kemungkinan decoding logis dan replikasi logis. Itu harus diaktifkan di server penerbitan.

Dari sudut pandang entri jurnal, level ini praktis tidak berbeda dari replika - catatan yang terkait dengan asal replikasi dan entri logis sewenang-wenang yang dapat ditambahkan ke log aplikasi ditambahkan. Pada dasarnya, decoding logis tergantung pada informasi tentang transaksi yang sedang berlangsung, karena Anda perlu membuat snapshot data untuk melacak perubahan dalam katalog sistem.

Sekarang kita tidak akan membahas detail operasi pencadangan dan replikasi - ini adalah topik besar untuk serangkaian artikel terpisah.

Rekam keandalan


Jelas bahwa mekanisme penjurnalan harus dapat diandalkan dan memberikan jaminan kemungkinan pemulihan dalam situasi apa pun (tidak terkait, tentu saja, untuk merusak pembawa data). Keandalan dipengaruhi oleh banyak faktor, di mana kami akan mempertimbangkan caching, korupsi data, dan atomitas rekaman.

Caching


Ada banyak cache di jalur data ke penyimpanan non-volatile (seperti hard disk drive).

Ketika sebuah program (ada, tetapi dalam kasus kami PostgreSQL) meminta sistem operasi untuk menulis sesuatu ke disk, sistem operasi akan mentransfer data ke cache dalam RAM. Perekaman aktual terjadi secara tidak sinkron, tergantung pada pengaturan penjadwal I / O sistem operasi.

Ketika OS memutuskan untuk menulis data, mereka jatuh ke dalam cache drive (hard disk). Drive elektronik juga dapat menunda perekaman, misalnya, mengumpulkan data dalam kelompok yang lebih menguntungkan untuk direkam pada saat bersamaan. Dan jika pengontrol RAID digunakan, level caching lain muncul antara OS dan drive.

Jadi, jika Anda tidak mengambil tindakan khusus, itu sama sekali tidak jelas kapan data akan benar-benar disimpan dengan aman. Ini biasanya tidak penting, tetapi ada tempat-tempat penting di mana PostgreSQL perlu memastikan bahwa data ditulis dengan aman. Pertama-tama, ini adalah penjurnalan (jika entri jurnal tidak mencapai disk, itu akan hilang bersama dengan sisa isi RAM) dan sebuah pos pemeriksaan (harus yakin bahwa halaman-halaman yang kotor benar-benar ditulis ke disk). Tetapi ada situasi lain, katakanlah, pelaksanaan operasi non-jurnal pada tingkat minimum, dll.

Sistem operasi menyediakan alat yang harus menjamin penulisan data segera ke memori non-volatile. Ada beberapa opsi, tetapi turun menjadi dua opsi utama: perintah sinkronisasi diberikan setelah perekaman (fsync, fdatasync), atau ketika membuka file (atau menulisnya), bendera khusus diindikasikan untuk sinkronisasi atau bahkan perekaman langsung, melewati cache OS.

Adapun log, utilitas pg_test_fsync memungkinkan Anda untuk memilih metode yang paling cocok untuk OS tertentu dan sistem file tertentu, dan itu diinstal dalam parameter konfigurasi wal_sync_method . File biasa selalu disinkronkan menggunakan fsync.

Poin halusnya adalah bahwa ketika memilih metode, karakteristik peralatan harus diperhitungkan. Misalnya, jika Anda menggunakan pengontrol yang didukung oleh baterai cadangan, tidak ada alasan untuk tidak menggunakan cache, karena baterai akan menyimpan data jika terjadi kegagalan daya.

Dokumentasi berisi banyak detail tentang hal ini.

Dalam kasus apa pun, sinkronisasi itu mahal dan berlangsung tidak lebih dari yang diperlukan (kami akan kembali ke masalah ini sedikit lebih rendah ketika kami berbicara tentang kinerja).

Secara umum, sinkronisasi dapat dimatikan (parameter fsync bertanggung jawab untuk ini), tetapi dalam hal ini Anda harus melupakan keandalan penyimpanan. Dengan menonaktifkan fsync , Anda setuju bahwa data dapat hilang kapan saja. Mungkin satu-satunya pilihan yang masuk akal untuk menggunakan opsi ini adalah untuk sementara meningkatkan produktivitas, ketika data dapat dengan mudah dipulihkan dari sumber lain (misalnya, selama migrasi awal).

Korupsi data


Peralatan tidak sempurna dan data mungkin rusak pada media, saat mengirim data melalui kabel antarmuka, dll. Beberapa kesalahan ini diproses pada tingkat perangkat keras, tetapi beberapa tidak.

Untuk mendeteksi masalah pada waktunya, entri jurnal selalu dilengkapi dengan checksum.

Halaman data juga dapat dilindungi dengan checksum. Untuk saat ini, ini hanya dapat dilakukan ketika cluster diinisialisasi, tetapi dalam PostgreSQL 12 akan dimungkinkan untuk menghidupkan dan mematikannya menggunakan utilitas pg_checksums (meskipun belum on the fly, tetapi hanya ketika server dihentikan).

Dalam lingkungan produksi, checksum harus dimasukkan, meskipun perhitungan dan kontrolnya terlalu mahal. Ini mengurangi kemungkinan bahwa kegagalan tidak akan terdeteksi pada waktunya.

Mengurangi, tetapi tidak menghilangkan.
Pertama, checksum hanya diperiksa ketika mengakses halaman - oleh karena itu, kerusakan bisa tidak diketahui sampai masuk ke semua cadangan. Itu sebabnya pg_probackup memeriksa checksum dari semua halaman cluster selama cadangan.
Kedua, halaman diisi dengan nol dianggap benar - jika sistem file keliru "membatalkan" file, ini mungkin tidak diperhatikan.
Ketiga, checksum hanya melindungi lapisan utama file data. Lapisan yang tersisa dan file lainnya (misalnya, status transaksi XACT) tidak dilindungi oleh apa pun.
Sayang

Mari kita lihat cara kerjanya. Pertama, pastikan checksum diaktifkan (perlu diingat bahwa ini tidak terjadi ketika menginstal paket pada sistem seperti Debian):

 => SHOW data_checksums; 
  data_checksums ---------------- on (1 row) 

Parameter data_checksums hanya-baca.

Berikut adalah file di mana tabel kami berada:

 => SELECT pg_relation_filepath('wallevel'); 
  pg_relation_filepath ---------------------- base/16386/24890 (1 row) 

Hentikan server dan ubah beberapa byte di halaman nol, misalnya, hapus entri log terakhir dari header LSN.

 student$ sudo pg_ctlcluster 11 main stop 

 postgres$ dd if=/dev/zero of=/var/lib/postgresql/11/main/base/16386/24890 oflag=dsync conv=notrunc bs=1 count=8 
 8+0 records in 8+0 records out 8 bytes copied, 0,0083022 s, 1,0 kB/s 

Pada prinsipnya, server tidak bisa dihentikan. Cukup bahwa halaman ditulis ke disk dan dipaksa keluar dari cache (jika tidak server akan bekerja dengan halaman dari cache). Tetapi skenario seperti itu lebih sulit untuk direproduksi.

Sekarang kita mulai server dan mencoba membaca tabel.

 student$ sudo pg_ctlcluster 11 main start 

 => SELECT * FROM wallevel; 
 WARNING: page verification failed, calculated checksum 23222 but expected 50884 ERROR: invalid page in block 0 of relation base/16386/24890 

Tetapi bagaimana jika data tidak dapat dipulihkan dari cadangan? Parameter ign_checksum_failure memungkinkan Anda mencoba membaca tabel, secara alami dengan risiko mendapatkan data yang terdistorsi.

 => SET ignore_checksum_failure = on; => SELECT * FROM wallevel; 
 WARNING: page verification failed, calculated checksum 23222 but expected 50884 n --- 1 (1 row) 

Tentu saja, dalam hal ini, semuanya berjalan dengan baik, karena kami hanya mengacaukan judul halaman, dan bukan data itu sendiri.

Dan satu hal lagi. Ketika checksum diaktifkan, bit prompt dituliskan ke log (kami memeriksanya sebelumnya), karena perubahan bit apa pun, bahkan yang tidak esensial, juga mengarah pada perubahan checksum. Ketika checksum dimatikan, parameter wal_log_hints bertanggung jawab untuk menulis bit petunjuk ke log .

Perubahan pada bit tooltip selalu dicatat sebagai gambar halaman penuh (FPI, gambar halaman penuh), yang meningkatkan ukuran log secara berurutan. Dalam hal ini, masuk akal untuk mengaktifkan kompresi gambar penuh menggunakan parameter wal_compression (parameter ini muncul di versi 9.5). Di bawah ini kita melihat angka-angka tertentu.

Catatan atomitas


Dan akhirnya, ada masalah atomitas catatan. Halaman database membutuhkan setidaknya 8 KB (bisa 16 atau 32 KB), dan pada level rendah, perekaman terjadi dalam blok yang biasanya lebih kecil (biasanya 512 byte atau 4 KB). Oleh karena itu, dalam hal terjadi kegagalan daya, halaman data dapat direkam sebagian. Jelas bahwa selama pemulihan tidak masuk akal untuk menerapkan entri jurnal biasa ke halaman seperti itu.

Untuk perlindungan, PostgreSQL memungkinkan Anda menulis ke log gambar penuh halaman ketika pertama kali diubah setelah dimulainya titik kontrol (gambar yang sama direkam ketika bit tooltip berubah). Parameter full_page_writes mengontrol ini , dan diaktifkan secara default.

Jika gambar halaman ditemukan selama pemulihan dalam log, itu tanpa syarat (tanpa pemeriksaan LSN) ditulis ke disk: ada lebih banyak kepercayaan di dalamnya, karena, seperti catatan log apa pun, itu dilindungi oleh checksum. Dan entri jurnal yang sudah biasa diterapkan pada gambar yang benar dijamin ini.

Meskipun PostgreSQL mengecualikan ruang yang tidak terisi dari gambar halaman penuh (kami sebelumnya melihat struktur blok), volume entri jurnal yang dihasilkan meningkat secara signifikan. Seperti yang telah disebutkan, situasinya dapat ditingkatkan dengan mengompresi gambar penuh (parameter wal_compression ).

Untuk merasakan perubahan ukuran log, kami akan melakukan percobaan sederhana menggunakan utilitas pgbench. Mari kita inisialisasi:

 student$ pgbench -i test 
 dropping old tables... creating tables... generating data... 100000 of 100000 tuples (100%) done (elapsed 0.15 s, remaining 0.00 s) vacuuming... creating primary keys... done. 

Opsi full_page_writes diaktifkan:

 => SHOW full_page_writes; 
  full_page_writes ------------------ on (1 row) 

Jalankan breakpoint dan segera jalankan tes selama 30 detik.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/38E04A08 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26851 latency average = 1.117 ms tps = 895.006720 (including connections establishing) tps = 895.095229 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3A69C478 (1 row) 

Ukuran Log:

 => SELECT pg_size_pretty('0/3A69C478'::pg_lsn - '0/38E04A08'::pg_lsn); 
  pg_size_pretty ---------------- 25 MB (1 row) 

Sekarang matikan parameter full_page_writes:

 => ALTER SYSTEM SET full_page_writes = off; => SELECT pg_reload_conf(); 

Dan ulangi percobaannya.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3A69C530 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 27234 latency average = 1.102 ms tps = 907.783080 (including connections establishing) tps = 907.895326 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3BE87658 (1 row) 

Ukuran Log:

 => SELECT pg_size_pretty('0/3BE87658'::pg_lsn - '0/3A69C530'::pg_lsn); 
  pg_size_pretty ---------------- 24 MB (1 row) 

Ya, ukurannya telah menurun, tetapi sama sekali tidak sepenting yang diharapkan.

Alasannya adalah bahwa cluster diinisialisasi dengan checksum di halaman data, dan karena itu Anda masih harus menulis gambar halaman penuh ke log ketika mengubah bit tooltip. Data ini (dalam kasus kami) membentuk sekitar setengah dari total volume, yang dapat dilihat dengan melihat statistik:

 postgres$ /usr/lib/postgresql/11/bin/pg_waldump --stats -p /var/lib/postgresql/11/main/pg_wal -s 0/3A69C530 -e 0/3BE87658 
 Type N (%) Record size (%) FPI size (%) ---- - --- ----------- --- -------- --- XLOG 1721 ( 1,03) 84329 ( 0,77) 13916104 (100,00) Transaction 27235 ( 16,32) 926070 ( 8,46) 0 ( 0,00) Storage 1 ( 0,00) 42 ( 0,00) 0 ( 0,00) CLOG 1 ( 0,00) 30 ( 0,00) 0 ( 0,00) Standby 4 ( 0,00) 240 ( 0,00) 0 ( 0,00) Heap2 27522 ( 16,49) 1726352 ( 15,76) 0 ( 0,00) Heap 109691 ( 65,71) 8169121 ( 74,59) 0 ( 0,00) Btree 756 ( 0,45) 45380 ( 0,41) 0 ( 0,00) -------- -------- -------- Total 166931 10951564 [44,04%] 13916104 [55,96%] 

Untuk kekompakan, saya menghapus nol baris dari tabel. Perhatikan garis total (Total) dan bandingkan ukuran gambar penuh (ukuran FPI) dengan ukuran rekaman biasa (Ukuran rekam).

Parameter full_page_writes hanya dapat dinonaktifkan jika sistem file dan perangkat keras yang digunakan sendiri menjamin perekaman atom. Tapi, seperti yang bisa kita lihat, tidak ada alasan yang bagus untuk ini (dengan asumsi bahwa checksum dimasukkan).

Sekarang mari kita lihat bagaimana kompresi membantu.

 => ALTER SYSTEM SET full_page_writes = on; => ALTER SYSTEM SET wal_compression = on; => SELECT pg_reload_conf(); 

Ulangi percobaan yang sama.

 => CHECKPOINT; => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3BE87710 (1 row) 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 26833 latency average = 1.118 ms tps = 894.405027 (including connections establishing) tps = 894.516845 (excluding connections establishing) 

 => SELECT pg_current_wal_insert_lsn(); 
  pg_current_wal_insert_lsn --------------------------- 0/3CBD3EA8 (1 row) 

Ukuran Log:

 => SELECT pg_size_pretty('0/3CBD3EA8'::pg_lsn - '0/3BE87710'::pg_lsn); 
  pg_size_pretty ---------------- 13 MB (1 row) 

Kesimpulan: di hadapan sejumlah besar gambar halaman penuh (karena checksum atau full_page_writes , yaitu, hampir selalu), kemungkinan besar masuk akal untuk menggunakan kompresi meskipun faktanya ini memuat prosesor.

Performa


Selama operasi server normal, terjadi perekaman berkelanjutan file log. Karena tidak ada akses acak, HDD biasa juga mengatasi tugas ini. Namun sifat beban ini sangat berbeda dari cara file data diakses.

Oleh karena itu, biasanya menguntungkan untuk menempatkan log pada disk fisik yang terpisah (atau array disk) yang dipasang pada sistem file server. Alih-alih direktori $ PGDATA / pg_wal, Anda perlu membuat tautan simbolis ke direktori yang sesuai.

Ada beberapa situasi di mana file log tidak hanya perlu ditulis, tetapi juga dibaca. Yang pertama adalah kasus pemulihan yang dapat dimengerti setelah kegagalan. Yang kedua kurang sepele. Ini terjadi jika replikasi streaming digunakan, dan replika tidak berhasil menerima entri jurnal saat masih dalam buffer RAM server utama. Maka proses walsender harus membaca data yang diperlukan dari disk. Kami akan membicarakan hal ini secara lebih rinci ketika kami mendapatkan replikasi.

Pencatatan terjadi dalam salah satu dari dua mode:

  • sinkron - ketika transaksi dilakukan, kelanjutan pekerjaan tidak mungkin sampai semua entri jurnal tentang transaksi ini ada di disk;
  • asynchronous - transaksi selesai dengan segera, dan log ditulis di latar belakang.

Mode sinkron ditentukan oleh parameter syncous_commit dan diaktifkan secara default.

Karena sinkronisasi dikaitkan dengan I / O yang nyata (lambat), maka bermanfaat untuk melakukannya sesedikit mungkin. Untuk melakukan ini, proses servis yang menyelesaikan transaksi dan menulis log membutuhkan jeda singkat, ditentukan oleh parameter commit_delay .Tetapi ini hanya terjadi jika sistem memiliki setidaknya commit_siblings transaksi aktif. Taruhan di sini adalah pada kenyataan bahwa selama menunggu beberapa transaksi akan selesai dan akan memungkinkan untuk menyinkronkan catatan mereka dalam sekali jalan. Ini mirip dengan cara Anda memegang pintu elevator sehingga seseorang memiliki waktu untuk masuk ke dalam kabin.

Parameter defaultnya adalah commit_siblings = 5, dan commit_delay = 0, jadi tidak ada tunggu yang sebenarnya. Perubahan commit_delay hanya berguna dalam sistem yang melakukan sejumlah besar pendek OLTP-transaksi.

Kemudian proses mem-flush log ke disk ke LSN yang diperlukan (atau sedikit lebih jika entri baru ditambahkan selama waktu tunggu). Setelah itu, transaksi dianggap selesai.

Dengan perekaman sinkron, ketahanan terjamin (huruf D dalam akronim ACID) - jika transaksi dilakukan, maka semua entri jurnalnya sudah ada di disk dan tidak akan hilang. Kelemahannya adalah perekaman sinkron meningkatkan waktu respons (perintah COMMIT tidak mengembalikan kontrol sampai sinkronisasi berakhir) dan mengurangi kinerja sistem.

Tulisan asinkron dapat diperoleh dengan mengatur syncous_commit = off (atau lokal).

Selama perekaman asinkron, proses penulis log mengatur ulang entri jurnal, bergantian siklus tunggu (yang ditetapkan oleh parameterwal_writer_delay = 200 ms secara default).

Bangun setelah menunggu berikutnya, proses memeriksa apakah halaman WAL lengkap telah muncul dari terakhir kali. Jika ada, maka proses mengabaikan halaman saat ini, tidak terisi, dan catatan hanya yang benar-benar terisi. (Benar, itu tidak selalu sekaligus: rekaman berhenti, mencapai akhir cache, dan berlanjut dari awal cache di waktu berikutnya.)

Jika tidak satu halaman penuh, proses menulis halaman jurnal saat ini (tidak lengkap) - untuk alasan yang baik bangun?

Algoritma ini bertujuan untuk tidak menyinkronkan halaman yang sama beberapa kali jika memungkinkan, yang penting untuk aliran perubahan yang besar.

Perekaman asinkron lebih efisien daripada perekaman sinkron - perubahan yang dilakukan tidak menunggu perekaman. Namun, reliabilitas menurun: data yang diambil dapat menghilang jika terjadi kegagalan jika kurang dari 3 × wal_writer_delay waktu berlalu antara komit dan kegagalan (yang, secara default, sedikit lebih dari setengah detik).

Pilihan yang sulit - efisiensi atau keandalan - tetap ada pada administrator sistem.

Harap dicatat: tidak seperti mematikan sinkronisasi ( fsync = off), mode asinkron tidak menyebabkan ketidakmungkinan pemulihan. Jika terjadi kegagalan, sistem masih akan memulihkan keadaan yang konsisten, tetapi mungkin beberapa transaksi terbaru tidak ada di dalamnya.

Parameter Synchronous_commitdapat ditetapkan sebagai bagian dari transaksi individu. Ini memungkinkan Anda untuk meningkatkan produktivitas dengan mengorbankan keandalan hanya sebagian dari transaksi. Katakanlah, transaksi keuangan selalu harus diperbaiki secara sinkron, dan pesan obrolan terkadang dapat diabaikan.

Pada kenyataannya, kedua mode ini bekerja bersama. Bahkan dengan komit sinkron, log transaksi panjang akan ditulis secara tidak sinkron untuk membebaskan buffer WAL. Dan jika, ketika mengatur ulang halaman dari cache buffer, ternyata entri jurnal yang sesuai belum pada disk, itu akan segera diatur ulang dalam mode sinkron.

Untuk mendapatkan gambaran tentang apa yang diberikan asynchronous commit, kami mencoba mengulangi tes pgbench dalam mode ini.

 => ALTER SYSTEM SET synchronous_commit = off; => SELECT pg_reload_conf(); 

 student$ pgbench -T 30 test 
 starting vacuum...end. transaction type: <builtin: TPC-B (sort of)> scaling factor: 1 query mode: simple number of clients: 1 number of threads: 1 duration: 30 s number of transactions actually processed: 45439 latency average = 0.660 ms tps = 1514.561710 (including connections establishing) tps = 1514.710558 (excluding connections establishing) 

Dengan komit sinkron, kami menerima sekitar 900 transaksi per detik (tps), dengan komit asinkron - 1500. Tentu saja, dalam sistem nyata di bawah beban riil rasionya akan berbeda, tetapi jelas bahwa pengaruhnya bisa sangat signifikan dengan transaksi pendek.

Pada titik ini, serangkaian artikel tentang penjurnalan berakhir. Jika sesuatu yang penting tetap ada di belakang layar, jangan merasa sulit untuk menulis di komentar. Terima kasih semuanya!

Dan kemudian kita akan memiliki petualangan yang menarik di dunia gembok, tapi itu cerita lain.

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


All Articles