PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

PostgreSQL Upsert phân biệt các hàng được chèn và cập nhật bằng cách sử dụng các cột hệ thống XMIN, XMAX và các cột khác

Tôi nghĩ rằng đây là một câu hỏi thú vị xứng đáng có một câu trả lời chuyên sâu; Vui lòng cho tôi biết nếu nó hơi dài dòng.

Tóm lại:Phỏng đoán của bạn là đúng và bạn có thể sử dụng RETURNING sau để xác định xem hàng đã được chèn và chưa được cập nhật hay chưa:

RETURNING (xmax = 0) AS inserted

Bây giờ là phần giải thích chi tiết:

Khi một hàng được cập nhật, PostgreSQL không sửa đổi dữ liệu, nhưng tạo một phiên bản mới của hàng; phiên bản cũ sẽ bị xóa bởi autovacuum khi nó không còn cần thiết nữa. Phiên bản của một hàng được gọi là tuple , vì vậy trong PostgreSQL có thể có nhiều hơn một bộ giá trị trên mỗi hàng.

xmax phục vụ hai mục đích khác nhau:

  1. Như đã nêu trong tài liệu, nó có thể là ID giao dịch của giao dịch đã xóa (hoặc cập nhật) tuple (“tuple” là một từ khác của “hàng”). Chỉ các giao dịch có ID giao dịch giữa xminxmax có thể nhìn thấy tuple. Một tuple cũ có thể được xóa một cách an toàn nếu không có giao dịch nào có ID giao dịch nhỏ hơn xmax .

  2. xmax cũng được sử dụng để lưu trữ khóa hàng . Trong PostgreSQL, các khóa hàng không được lưu trữ trong bảng khóa mà trong bộ mã để tránh tràn bảng khóa.
    Nếu chỉ một giao dịch có khóa trên hàng, xmax sẽ chứa ID giao dịch của giao dịch khóa. Nếu có nhiều giao dịch bị khóa trên hàng, xmax chứa số lượng cái gọi là multixact , là cấu trúc dữ liệu chứa ID giao dịch của các giao dịch khóa.

Tài liệu của xmax không hoàn chỉnh, vì ý nghĩa chính xác của trường này được coi là chi tiết triển khai và không thể hiểu được nếu không biết t_infomask của bộ tuple, không hiển thị ngay lập tức qua SQL.

Bạn có thể cài đặt mô-đun đóng góp pageinspect để xem trường này và các trường khác của một bộ tuple.

Tôi đã chạy ví dụ của bạn và đây là những gì tôi thấy khi sử dụng heap_page_items chức năng kiểm tra chi tiết (số ID giao dịch tất nhiên là khác trong trường hợp của tôi):

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)

Ý nghĩa của t_infomaskt_infomask2 có thể được tìm thấy trong src/include/access/htup_details.h . lp_off là phần bù của bộ dữ liệu trong trang và t_ctid tuple ID hiện tại bao gồm số trang và một bộ số trong trang. Vì bảng mới được tạo nên tất cả dữ liệu đều ở trang 0.

Hãy để tôi thảo luận về ba hàng được trả về bởi heap_page_items .

  1. Tại con trỏ dòng (lp ) 1, chúng tôi tìm thấy tuple cũ, được cập nhật. Ban đầu nó có ctid = (0,1) , nhưng điều đó đã được sửa đổi để chứa ID tuple của phiên bản hiện tại trong quá trình cập nhật. Tuple được tạo ra bởi giao dịch 102507 và bị vô hiệu bởi giao dịch 102508 (giao dịch phát hành INSERT ... ON CONFLICT ). Tuple này không còn hiển thị nữa và sẽ bị xóa trong VACUUM .

    t_infomask cho thấy rằng cả xminxmax thuộc về các giao dịch đã cam kết và do đó hiển thị khi các bộ giá trị được tạo và xóa. t_infomask2 cho thấy rằng tuple đã được cập nhật bằng HOT ( heap only tuple ) cập nhật, có nghĩa là bộ mã được cập nhật nằm trong cùng một trang với bộ nguyên gốc và không có cột được lập chỉ mục nào được sửa đổi (xem src/backend/access/heap/README.HOT ).

  2. Tại con trỏ dòng 2, chúng ta thấy bộ giá trị mới, cập nhật được tạo bởi giao dịch INSERT ... ON CONFLICT (giao dịch 102508).

    t_infomask cho thấy rằng tuple này là kết quả của một bản cập nhật, xmin là hợp lệ và xmax chứa KEY SHARE khóa hàng (không còn phù hợp kể từ khi giao dịch đã hoàn tất). Khóa hàng này được thực hiện trong khi INSERT ... ON CONFLICT Chế biến. t_infomask2 cho thấy rằng đây là một bộ giá HOT.

  3. Tại con trỏ dòng 3, chúng ta thấy hàng mới được chèn.

    t_infomask cho thấy rằng xmin là hợp lệ và xmax không có hiệu lực. xmax được đặt thành 0 vì giá trị này luôn được sử dụng cho các bộ giá trị mới được chèn vào.

Vì vậy, nonzero xmax của hàng được cập nhật là phần mềm triển khai do khóa hàng gây ra. Có thể hình dung rằng INSERT ... ON CONFLICT được thực hiện lại vào một ngày nào đó để hành vi này thay đổi, nhưng tôi nghĩ rằng điều đó khó xảy ra.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Hủy một phiên / kết nối postgresql

  2. Cách phân tích cú pháp JSON trong postgresql

  3. Trừ giờ cho hàm now ()

  4. CASCADE XÓA chỉ một lần

  5. Thực hiện WHERE IN trên nhiều cột trong Postgresql