Làm thế nào chính xác tầm nhìn hàng được xác định?


10

Trong trường hợp đơn giản nhất, khi chúng tôi chèn một hàng mới vào một bảng (và các giao dịch cam kết), nó sẽ hiển thị cho tất cả các giao dịch tiếp theo. Xem xmaxlà 0 trong ví dụ này:

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

Khi chúng tôi cập nhật nó (vì cờ được đặt FALSEngẫu nhiên), nó sẽ thay đổi một chút:

UPDATE vis SET is_active = TRUE;

SELECT ctid, xmin, xmax, * FROM vis;

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

Theo mô hình MVCC mà PostgreSQL sử dụng, một hàng vật lý mới đã được viết và hàng cũ bị vô hiệu (điều này có thể được nhìn thấy từ ctid). Giao dịch mới vẫn hiển thị cho tất cả các giao dịch tiếp theo.

Bây giờ có một điều thú vị xảy ra khi chúng ta quay lại 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

Phiên bản hàng vẫn giữ nguyên, nhưng bây giờ xmaxđược đặt thành một cái gì đó. Mặc dù vậy, các giao dịch tiếp theo có thể thấy hàng này (nếu không thay đổi).

Sau khi đọc một chút về điều này, bạn có thể tìm ra một vài điều về khả năng hiển thị hàng. Có bản đồ hiển thị , nhưng nó chỉ cho biết nếu toàn bộ trang có thể nhìn thấy - nó chắc chắn không hoạt động ở cấp độ hàng (tuple). Sau đó, có nhật ký cam kết (aka clog) - nhưng làm thế nào Postgres tìm ra nếu nó phải truy cập nó?

Tôi quyết định xem xét các bit thông tin để tìm hiểu mức độ hiển thị thực sự hoạt động. Để xem chúng, cách dễ nhất là sử dụng phần mở rộng pageinspect . Để tìm ra bit nào được đặt, tôi đã tạo một bảng để lưu trữ chúng:

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));

Sau đó kiểm tra những gì bên trong visbảng của tôi - lưu ý pageinspecthiển thị nội dung vật lý của heap, vì vậy không chỉ các hàng hiển thị được trả về:

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

Điều tôi hiểu từ bên trên là phiên bản đầu tiên xuất hiện với giao dịch 2699, sau đó được thay thế thành công bằng phiên bản mới vào lúc 2700.
Sau đó, phiên bản tiếp theo, tồn tại từ năm 2700, đã có một nỗ lực quay trở lại UPDATEvào năm 2702, được nhìn thấy từ năm 2702 HEAP_XMAX_INVALID.
Người cuối cùng không bao giờ thực sự được sinh ra, như thể hiện bởi HEAP_XMIN_INVALID.

Vì vậy, đoán từ trên, trường hợp đầu tiên và cuối cùng là rõ ràng - chúng không còn hiển thị nữa đối với giao dịch 2703 trở lên.
Cái thứ hai phải được tra cứu ở đâu đó - tôi cho rằng đó là nhật ký cam kết, aka clog.

Để làm phức tạp thêm các vấn đề, một UPDATEkết quả tiếp theo sau:

 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

Ở đây tôi thấy đã có hai ứng cử viên có thể nhìn thấy. Vì vậy, cuối cùng, đây là câu hỏi của tôi:

  • Có phải giả định của tôi rằng đó cloglà nơi để xem xét để xác định khả năng hiển thị trong những trường hợp này?
  • Những cờ nào (hoặc kết hợp các cờ) cho hệ thống truy cập clog?
  • Có cách nào để kiểm tra những gì bên trong clog? Có đề cập về clogtham nhũng trong các phiên bản trước của Postgres và một gợi ý rằng người ta có thể xây dựng một tệp giả mạo bằng tay. Thông tin này sẽ giúp rất nhiều với nó.

Câu trả lời:


6

Vì vậy, đoán từ trên, trường hợp đầu tiên và cuối cùng là rõ ràng - chúng không còn hiển thị nữa đối với giao dịch 2703 trở lên. Cái thứ hai phải được tra cứu ở đâu đó - tôi cho rằng đó là nhật ký cam kết, hay còn gọi là guốc.

Cái thứ 2 có HEAP_XMAX_INVALID. Điều đó có nghĩa là không cần phải tham khảo guốc, bởi vì ai đó đã làm như vậy, thấy rằng xmaxnó bị hủy bỏ và đặt "bit gợi ý" để các quy trình trong tương lai không cần phải truy cập lại guốc cho hàng đó.

Những cờ nào (hoặc kết hợp các cờ) cho hệ thống truy cập vào guốc?

Nếu không có heap_xmin_committedhoặc heap_xmin_invalid, sau đó bạn phải truy cập vào guốc để xem cách bố trí của xmin là gì. Nếu giao dịch vẫn đang được tiến hành, thì hàng không hiển thị cho bạn và bạn không thể đặt bất kỳ cờ nào. Nếu giao dịch được cam kết hoặc khôi phục, bạn thiết lập heap_xmin_committedhoặc heap_xmin_invalidtheo đó (nếu thuận tiện để thực hiện điều đó - không bắt buộc) để mọi người trong tương lai không cần phải tìm kiếm.

Nếu xminlà hợp lệ và được cam kết, và nếu xmaxkhông bằng không, và không có heap_max_committedhoặc heap_max_invalid, thì bạn phải truy cập vào guốc để xem cách xử lý giao dịch đó là gì.

Có cách nào để kiểm tra những gì bên trong guốc? Có đề cập về tắc nghẽn tham nhũng trong các phiên bản trước của Postgres và một gợi ý rằng người ta có thể xây dựng một tệp giả mạo bằng tay. Thông tin này sẽ giúp rất nhiều với nó.

Tôi không biết về cách làm việc thân thiện với người dùng. Bạn có thể sử dụng "od" để kết xuất các tệp guốc theo cách phù hợp để kiểm tra chúng và tìm ra nơi cần kiểm tra bằng cách sử dụng các macro được xác định trongsrc/backend/access/transam/clog.c

Tôi ngạc nhiên không có tiện ích mở rộng nào trên PGXN phù hợp với bạn, nhưng tôi không thể tìm thấy. Nhưng tôi nghĩ nó sẽ không hữu ích lắm, bởi vì bạn thực sự cần phải làm điều này trong khi máy chủ của bạn không chạy.


4

Hãy xem triển khai HeapTupleSatisfiesMVCC () : clogkiểm tra thực tế xảy ra trong TransactionIdDidCommit () , nhưng nó chỉ được gọi nếu trạng thái giao dịch có thể được suy ra từ các bit infomask (macro HeapTupleHeaderXminCommned ( ).

Tôi đã truy ngược lại quyền truy cập vào các pg_clogchức năng TransactionDidCommit()TransactionDidAbort()sau đó tôi đã tìm kiếm nơi chúng được gọi và nơi duy nhất trong mã liên quan đến câu hỏi của bạn xuất hiện HeapTupleSatisfiesMVCC(). Từ mã của hàm này, bạn có thể thấy rằng việc tra cứu guốc thực tế chỉ có thể xảy ra nếu bộ dữ liệu không có các bit thông tin liên quan được đặt: mã bắt đầu bằng việc kiểm tra các bit bằng HeapTupleHeaderXminCommitted()et al. Và việc tra cứu guốc chỉ xảy ra nếu bit (s) không được đặt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.