Як саме визначається видимість рядків?


10

У найпростішому випадку, коли ми вставимо новий рядок у таблицю (і транзакція здійсниться), це буде видно для всіх наступних транзакцій. Дивіться xmax0 у цьому прикладі:

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

Коли ми його оновлюємо (оскільки прапор встановлено FALSEвипадково), він дещо змінюється:

UPDATE vis SET is_active = TRUE;

SELECT ctid, xmin, xmax, * FROM vis;

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

Згідно з моделлю MVCC, яку використовує PostgreSQL, було записано новий фізичний рядок, а старий - недійсний (це можна побачити з розділу ctid). Новий все ще видно для всіх наступних транзакцій.

Тепер відбувається цікава річ, коли ми повертаємо назад 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

Версія рядка залишається такою ж, але тепер xmaxвстановлена ​​на щось. Незважаючи на це, наступні транзакції можуть бачити цей (інакше незмінний) рядок.

Прочитавши трохи про це, ви можете з’ясувати кілька речей про видимість рядків. Існує карта видимості , але вона вказує лише на те, що видно цілу сторінку - вона точно не працює на рівні рядка (кортежу). Тоді є журнал фіксування (ака clog) - але як Postgres з'ясовує, чи потрібно його відвідувати?

Я вирішив подивитися на шматочки інформації, щоб зрозуміти, як видимість працює насправді. Щоб побачити їх, найпростіший спосіб - це розширення pageinspect . Для того, щоб дізнатися, які біти встановлені, я створив таблицю для їх зберігання:

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

Потім перевірте, що знаходиться всередині моєї visтаблиці - зверніть увагу, що pageinspectпоказує фізичний вміст купи, тому не тільки видимі рядки повертаються:

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

Що я розумію з вищесказаного, це те, що перша версія ожила трансакцією 2699, потім її успішно замінили новою версією в 2700.
Потім наступну, яка була жива з 2700 року, була спроба UPDATEвідкоту в 2702 році, видно з HEAP_XMAX_INVALID.
Останній ніколи насправді не народився, як показав HEAP_XMIN_INVALID.

Отже, здогадуючись з вищесказаного, перший та останній випадок очевидні - вони вже не видно для транзакції 2703 або вище.
Другий слід десь шукати - я вважаю, що це журнал фіксування, ака clog.

Щоб ще більше ускладнити проблеми, подальшим UPDATEрезультатом є наступне:

 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

Тут я бачу вже двох кандидатів, які могли бути видні. Отже, нарешті, ось мої запитання:

  • Чи моє припущення, що саме clogтут слід подивитися, щоб визначити видимість у цих випадках?
  • Які прапори (або комбінації прапорів) повідомляють системі відвідувати clog?
  • Чи є спосіб вивчити, що знаходиться всередині clog? У clogпопередніх версіях Postgres згадується корупція та натяк на те, що можна створити підроблений файл вручну. Ця інформація допоможе нам у цьому.

Відповіді:


6

Отже, здогадуючись з вищесказаного, перший та останній випадок очевидні - вони вже не видно для транзакції 2703 або вище. Другий треба десь шукати - я вважаю, що це журнал фіксування, він також засмічується.

2-й має HEAP_XMAX_INVALID. Це означає, що не потрібно звертатися до засмічення, тому що хтось це вже зробив, побачивши, що xmaxпереривається, і встановив "біт підказки", щоб майбутнім процесам не потрібно знову відвідувати цей блок.

Які прапори (або комбінації прапорів) повідомляють системі відвідувати засмічення?

Якщо немає heap_xmin_committedабо heap_xmin_invalid, вам доведеться відвідати засмічення, щоб побачити, якою була диспозиція xmin. Якщо транзакція ще триває, рядок вам не видно, і ви не можете встановити жодні прапори. Якщо транзакція здійснена або відкатана, ви встановлюєте heap_xmin_committedабо heap_xmin_invalidвідповідно (якщо це зручно робити - це не обов'язково), тому майбутнім людям не потрібно її шукати.

Якщо xminдійсний і вчинений, а якщо xmaxвін не дорівнює нулю, і немає heap_max_committedабо heap_max_invalid, вам доведеться відвідати засмічення, щоб побачити, якою була диспозиція цієї транзакції.

Чи є спосіб вивчити, що знаходиться всередині засмічення? У попередніх версіях Postgres є згадки про пошкодження блокування та натяк на те, що можна створити підроблений файл вручну. Ця інформація допоможе нам у цьому.

Я не знаю зручного для цього способу. Ви можете використовувати "od", щоб скинути файли засмічення відповідним чином для їх огляду та з'ясувати, де їх перевірити, використовуючи макроси, визначені вsrc/backend/access/transam/clog.c

Я здивований, що на PGXN немає розширень, які працюють для вас, але я не зміг його знайти. Але я думаю, це було б не все так корисно, тому що вам дійсно потрібно вміти це робити, поки ваш сервер не працює.


4

Подивіться на реалізацію HeapTupleSatisfiesMVCC () : фактична clogперевірка відбувається в TransactionIdDidCommit () , але вона викликається лише в тому випадку, якщо статус транзакції неможливо зробити з бітів інформаційних завдань (HeapTupleHeaderXminCommitted () макрос та друзів).

Я простежив доступ до pg_clogфункцій, TransactionDidCommit()а TransactionDidAbort()потім подивився, куди вони викликаються, і, здається, знаходиться єдине місце в коді, пов’язаному з вашим запитанням HeapTupleSatisfiesMVCC(). З коду цієї функції видно, що фактичний пошук засмічення може статися лише в тому випадку, якщо в кортежі немає встановлених відповідних бітів інформаційної задачі: код починається з перевірки бітів з HeapTupleHeaderXminCommitted()співавт. А пошук засмічення відбувається лише в тому випадку, якщо біт (и) не встановлені.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.