Bagaimana tepatnya visibilitas baris ditentukan?

10

Dalam kasus paling sederhana, ketika kita memasukkan baris baru ke dalam tabel (dan transaksi dilakukan), itu akan terlihat oleh semua transaksi selanjutnya. Lihat xmaxmenjadi 0 dalam contoh ini:

CREATE TABLE vis (
  id serial,
  is_active boolean
);

INSERT INTO vis (is_active) VALUES (FALSE);

SELECT ctid, xmin, xmax, * FROM vis;

  ctid xmin  xmax  id  is_active 
───────┼─────┼──────┼────┼───────────
 (0,1) 2699     0   1  f

Ketika kami memperbaruinya (karena bendera disetel FALSEsecara tidak sengaja), itu sedikit berubah:

UPDATE vis SET is_active = TRUE;

SELECT ctid, xmin, xmax, * FROM vis;

 ctid  xmin  xmax  id  is_active 
──────┼──────┼──────┼────┼───────────
(0,2)  2700     0   1  t

Menurut model MVCC yang digunakan PostgreSQL, baris fisik baru ditulis dan yang lama tidak valid (ini bisa dilihat dari ctid). Yang baru masih terlihat oleh semua transaksi selanjutnya.

Sekarang ada hal menarik yang terjadi ketika kita memutar kembali UPDATE:

BEGIN;

    UPDATE vis SET is_active = TRUE;

ROLLBACK;

SELECT ctid, xmin, xmax, * FROM vis;

 ctid   xmin  xmax  id  is_active 
───────┼──────┼──────┼────┼───────────
 (0,2)  2700  2702   1  t

Versi baris tetap sama, tetapi sekarang xmaxdiatur ke sesuatu. Meskipun demikian, transaksi selanjutnya dapat melihat baris ini (jika tidak berubah).

Setelah membaca sedikit tentang ini, Anda dapat mengetahui beberapa hal tentang visibilitas baris. Ada peta visibilitas , tetapi hanya memberitahu jika seluruh halaman terlihat - itu pasti tidak bekerja pada level baris (tuple). Lalu ada log komit (alias clog) - tetapi bagaimana Postgres mengetahui jika harus mengunjunginya?

Saya memutuskan untuk melihat bit infomask untuk mengetahui bagaimana visibilitas sebenarnya bekerja. Untuk melihatnya, cara termudah adalah dengan menggunakan ekstensiinspeksi halaman . Untuk mengetahui bit mana yang diset, saya membuat tabel untuk menyimpannya:

CREATE TABLE infomask (
  i_flag text,
  i_bits bit(16)
);

INSERT INTO infomask
VALUES 
('HEAP_HASNULL', x'0001'::bit(16)),
('HEAP_HASVARWIDTH', x'0002'::bit(16)),
('HEAP_HASEXTERNAL', x'0004'::bit(16)),
('HEAP_HASOID', x'0008'::bit(16)),
('HEAP_XMAX_KEYSHR_LOCK', x'0010'::bit(16)),
('HEAP_COMBOCID', x'0020'::bit(16)),
('HEAP_XMAX_EXCL_LOCK', x'0040'::bit(16)),
('HEAP_XMAX_LOCK_ONLY', x'0080'::bit(16)),
('HEAP_XMIN_COMMITTED', x'0100'::bit(16)),
('HEAP_XMIN_INVALID', x'0200'::bit(16)),
('HEAP_XMAX_COMMITTED', x'0400'::bit(16)),
('HEAP_XMAX_INVALID', x'0800'::bit(16)),
('HEAP_XMAX_IS_MULTI', x'1000'::bit(16)),
('HEAP_UPDATED', x'2000'::bit(16)),
('HEAP_MOVED_OFF', x'4000'::bit(16)),
('HEAP_MOVED_IN', x'8000'::bit(16)),
('HEAP_XACT_MASK', x'FFF0'::bit(16));

Kemudian periksa apa yang ada di dalam vismeja saya - catatan yang pageinspectmenunjukkan isi fisik tumpukan, jadi tidak hanya baris yang terlihat dikembalikan:

SELECT t_xmin, t_xmax, string_agg(i_flag, ', ') FILTER (WHERE (t_infomask::bit(16) & i_bits)::integer::boolean)
  FROM heap_page_items(get_raw_page('vis', 0)),
       infomask
 GROUP BY t_xmin, t_xmax;

 t_xmin  t_xmax                       string_agg                      
────────┼────────┼──────────────────────────────────────────────────────
   2699    2700  HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
   2700    2702  HEAP_XMIN_COMMITTED, HEAP_XMAX_INVALID, HEAP_UPDATED
   2702       0  HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED

Apa yang saya mengerti dari di atas adalah bahwa versi pertama dihidupkan dengan transaksi 2699, kemudian berhasil digantikan oleh versi baru di 2700.
Kemudian yang berikutnya, yang hidup sejak 2700, memiliki upaya mundur UPDATEpada 2702, dilihat dari HEAP_XMAX_INVALID.
Yang terakhir tidak pernah benar-benar lahir, seperti yang ditunjukkan oleh HEAP_XMIN_INVALID.

Jadi, menebak dari yang di atas, kasus pertama dan terakhir jelas - mereka tidak terlihat lagi untuk transaksi 2703 atau lebih tinggi.
Yang kedua harus dilihat di suatu tempat - saya kira itu adalah log komit, alias clog.

Untuk semakin memperumit masalah, UPDATEhasil berikut dalam berikut ini:

 t_xmin  t_xmax                      string_agg                     
────────┼────────┼────────────────────────────────────────────────────
   2699    2700  HEAP_XMIN_COMMITTED, HEAP_XMAX_COMMITTED
   2702       0  HEAP_XMIN_INVALID, HEAP_XMAX_INVALID, HEAP_UPDATED
   2703       0  HEAP_XMAX_INVALID, HEAP_UPDATED
   2700    2703  HEAP_XMIN_COMMITTED, HEAP_UPDATED

Di sini saya melihat sudah dua kandidat yang bisa terlihat. Jadi, akhirnya, inilah pertanyaan saya:

  • Apakah asumsi saya bahwa clogadalah tempat untuk melihat untuk menentukan visibilitas dalam kasus ini?
  • Bendera mana (atau kombinasi dari bendera) yang memberi tahu sistem untuk mengunjungi clog?
  • Apakah ada cara untuk memeriksa apa yang ada di dalamnya clog? Ada yang menyebutkan tentang clogkorupsi di versi Postgres sebelumnya dan petunjuk bahwa seseorang dapat membuat file palsu secara manual. Sepotong informasi ini akan banyak membantu.
dezso
sumber

Jawaban:

6

Jadi, menebak dari yang di atas, kasus pertama dan terakhir jelas - mereka tidak terlihat lagi untuk transaksi 2703 atau lebih tinggi. Yang kedua harus dilihat di suatu tempat - saya kira itu adalah log komit, alias menyumbat.

Yang ke-2 punya HEAP_XMAX_INVALID. Itu berarti tidak perlu berkonsultasi dengan bakiak, karena seseorang telah melakukannya, melihat bahwa xmaxbatal, dan menetapkan "sedikit petunjuk" sehingga proses di masa depan tidak perlu mengunjungi bakiak lagi untuk baris itu.

Bendera mana (atau kombinasi dari bendera) yang memberitahu sistem untuk mengunjungi clog?

Jika tidak ada heap_xmin_committedatau heap_xmin_invalid, maka Anda harus mengunjungi clog untuk melihat apa disposisi xmin. Jika transaksi masih berlangsung, maka baris tersebut tidak terlihat oleh Anda dan Anda tidak dapat mengatur tanda apa pun. Jika transaksi dilakukan atau dibatalkan, Anda mengatur heap_xmin_committedatau heap_xmin_invalidsesuai (jika nyaman untuk melakukan itu - itu tidak wajib) sehingga orang di masa depan tidak perlu mencarinya.

Jika xminvalid dan berkomitmen, dan jika xmaxtidak nol, dan tidak ada heap_max_committedatau heap_max_invalid, maka Anda harus mengunjungi clog untuk melihat apa disposisi dari transaksi itu.

Apakah ada cara untuk memeriksa apa yang ada di dalam bakiak? Ada yang menyebutkan tentang penyumbatan korupsi di versi Postgres sebelumnya dan petunjuk bahwa seseorang dapat membuat file palsu secara manual. Sepotong informasi ini akan banyak membantu.

Saya tidak mengetahui cara yang ramah pengguna untuk melakukannya. Anda dapat menggunakan "od" untuk membuang file penyumbat dengan cara yang sesuai untuk memeriksanya, dan mencari tahu di mana untuk memeriksanya dengan menggunakan makro yang ditentukan dalamsrc/backend/access/transam/clog.c

Saya terkejut tidak ada ekstensi pada PGXN yang berfungsi untuk Anda, tetapi saya tidak dapat menemukannya. Tapi saya pikir itu tidak akan terlalu berguna, karena Anda benar-benar harus dapat melakukan ini saat server Anda tidak berjalan.

jjanes
sumber
4

Lihat implementasi HeapTupleSatisfiesMVCC () : clogcek aktual terjadi di TransactionIdDidCommit () , tetapi hanya dipanggil jika status transaksi tidak dapat disimpulkan dari bit infomask (HeapTupleHeaderXminCommitted () makro dan teman-teman).

Saya telah melacak kembali akses ke pg_clogfungsi TransactionDidCommit()dan TransactionDidAbort(), kemudian saya telah mencari di mana ini dipanggil dan satu-satunya tempat dalam kode yang terkait dengan pertanyaan Anda tampaknya berada di HeapTupleSatisfiesMVCC(). Dari kode fungsi ini Anda dapat melihat bahwa pencarian penyumbatan yang sebenarnya hanya dapat terjadi jika tupel tidak memiliki bit infomask terkait yang ditetapkan: kode dimulai dengan memeriksa bit dengan HeapTupleHeaderXminCommitted()et al. Dan pencarian bakiak hanya terjadi jika bit tidak diatur.

alex
sumber