PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

PostgreSQL Upsert membedakan baris yang disisipkan dan diperbarui menggunakan kolom sistem XMIN, XMAX, dan lainnya

Saya pikir ini adalah pertanyaan menarik yang layak mendapatkan jawaban mendalam; mohon bersabar jika agak panjang.

Singkatnya:Tebakan Anda benar, dan Anda dapat menggunakan RETURNING berikut ini klausa untuk menentukan apakah baris dimasukkan dan tidak diperbarui:

RETURNING (xmax = 0) AS inserted

Sekarang penjelasan detailnya:

Saat baris diperbarui, PostgreSQL tidak mengubah data, tetapi membuat versi baru dari baris; versi lama akan dihapus oleh autovacuum ketika sudah tidak dibutuhkan lagi. Versi baris disebut tuple , jadi di PostgreSQL bisa ada lebih dari satu tupel per baris.

xmax melayani dua tujuan yang berbeda:

  1. Sebagaimana dinyatakan dalam dokumentasi, itu dapat berupa ID transaksi dari transaksi yang menghapus (atau memperbarui) tuple (“tuple” adalah kata lain untuk “baris”). Hanya transaksi dengan ID transaksi antara xmin dan xmax dapat melihat tupel. Tuple lama dapat dihapus dengan aman jika tidak ada transaksi dengan ID transaksi kurang dari xmax .

  2. xmax juga digunakan untuk menyimpan kunci baris . Di PostgreSQL, kunci baris tidak disimpan di tabel kunci, tetapi di tupel untuk menghindari meluapnya tabel kunci.
    Jika hanya satu transaksi yang memiliki kunci di baris, xmax akan berisi ID transaksi dari transaksi penguncian. Jika lebih dari satu transaksi memiliki kunci pada baris, xmax berisi jumlah yang disebut multixact , yang merupakan struktur data yang pada gilirannya berisi ID transaksi dari transaksi penguncian.

Dokumentasi xmax tidak lengkap, karena arti sebenarnya dari bidang ini dianggap sebagai detail implementasi dan tidak dapat dipahami tanpa mengetahui t_infomask dari tuple, yang tidak langsung terlihat melalui SQL.

Anda dapat menginstal modul contrib pageinspect untuk melihat ini dan bidang lain dari sebuah tuple.

Saya menjalankan contoh Anda, dan inilah yang saya lihat ketika saya menggunakan heap_page_items fungsi untuk memeriksa detail (nomor ID transaksi tentu saja berbeda dalam kasus saya):

SELECT *, ctid, xmin, xmax FROM t;

┌───┬────┬───────┬────────┬────────┐
│ i │ x  │ ctid  │  xmin  │  xmax  │
├───┼────┼───────┼────────┼────────┤
│ 1 │ 11 │ (0,2) │ 102508 │ 102508 │
│ 2 │ 22 │ (0,3) │ 102508 │      0 │
└───┴────┴───────┴────────┴────────┘
(2 rows)

SELECT lp, lp_off, t_xmin, t_xmax, t_ctid,
       to_hex(t_infomask) AS t_infomask, to_hex(t_infomask2) AS t_infomask2
FROM heap_page_items(get_raw_page('laurenz.t', 0));

┌────┬────────┬────────┬────────┬────────┬────────────┬─────────────┐
│ lp │ lp_off │ t_xmin │ t_xmax │ t_ctid │ t_infomask │ t_infomask2 │
├────┼────────┼────────┼────────┼────────┼────────────┼─────────────┤
│  1 │   8160 │ 102507 │ 102508 │ (0,2)  │ 500        │ 4002        │
│  2 │   8128 │ 102508 │ 102508 │ (0,2)  │ 2190       │ 8002        │
│  3 │   8096 │ 102508 │      0 │ (0,3)  │ 900        │ 2           │
└────┴────────┴────────┴────────┴────────┴────────────┴─────────────┘
(3 rows)

Arti dari t_infomask dan t_infomask2 dapat ditemukan di src/include/access/htup_details.h . lp_off adalah offset dari data Tuple di halaman, dan t_ctid adalah ID tupel saat ini yang terdiri dari nomor halaman dan nomor tuple di dalam halaman. Karena tabel baru dibuat, semua data ada di halaman 0.

Biarkan saya membahas tiga baris yang dikembalikan oleh heap_page_items .

  1. Di penunjuk garis (lp ) 1 kami menemukan tuple lama yang diperbarui. Awalnya memiliki ctid = (0,1) , tetapi itu dimodifikasi untuk memuat ID Tuple dari versi saat ini selama pembaruan. Tuple dibuat oleh transaksi 102507 dan dibatalkan oleh transaksi 102508 (transaksi yang mengeluarkan INSERT ... ON CONFLICT ). Tuple ini tidak terlihat lagi dan akan dihapus selama VACUUM .

    t_infomask menunjukkan bahwa keduanya xmin dan xmax milik transaksi yang berkomitmen dan akibatnya menunjukkan kapan tupel dibuat dan dihapus. t_infomask2 menunjukkan bahwa tupel telah diperbarui dengan HOT (heap only Tuple ) update, yang berarti bahwa tupel yang diperbarui berada di halaman yang sama dengan tupel asli dan tidak ada kolom terindeks yang diubah (lihat src/backend/access/heap/README.HOT ).

  2. Pada penunjuk baris 2 kita melihat tupel baru yang diperbarui yang dibuat oleh transaksi INSERT ... ON CONFLICT (transaksi 102508).

    t_infomask menunjukkan bahwa tuple ini adalah hasil pembaruan, xmin valid, dan xmax berisi KEY SHARE kunci baris (yang tidak lagi relevan sejak transaksi selesai). Kunci baris ini diambil selama INSERT ... ON CONFLICT pengolahan. t_infomask2 menunjukkan bahwa ini adalah tupel PANAS.

  3. Pada penunjuk baris 3 kita melihat baris yang baru dimasukkan.

    t_infomask menunjukkan bahwa xmin valid dan xmax tidak valid. xmax disetel ke 0 karena nilai ini selalu digunakan untuk tupel yang baru dimasukkan.

Jadi xmax bukan nol dari baris yang diperbarui adalah artefak implementasi yang disebabkan oleh kunci baris. Bisa dibayangkan bahwa INSERT ... ON CONFLICT diterapkan kembali suatu hari sehingga perilaku ini berubah, tetapi saya pikir itu tidak mungkin.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Baca Berkomitmen adalah suatu keharusan untuk database SQL terdistribusi yang kompatibel dengan Postgres

  2. DROP TABLE JIKA ADA Contoh di PostgreSQL

  3. Hubungan tidak ada

  4. Menggunakan gabungan untuk menggabungkan data dari tabel yang berbeda di PostgreSQL

  5. Masalah untuk penyisipan menggunakan psycopg