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:
-
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
xmin
vàxmax
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ơnxmax
. -
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_infomask
và t_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
là 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
.
-
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ànhINSERT ... ON CONFLICT
). Tuple này không còn hiển thị nữa và sẽ bị xóa trongVACUUM
.t_infomask
cho thấy rằng cảxmin
vàxmax
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 (xemsrc/backend/access/heap/README.HOT
). -
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ứaKEY 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 khiINSERT ... ON CONFLICT
Chế biến.t_infomask2
cho thấy rằng đây là một bộ giá HOT. -
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ằngxmin
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.