Пошук уроків, статей та іншого контенту
З’ясуєте особливості рівня Read Uncommitted і чому PostgreSQL фактично використовує поведінку Read Committed.
Read Uncommitted — найнижчий рівень ізоляції транзакцій у SQL-стандарті. Він допускає читання змін, які інша транзакція ще не підтвердила через COMMIT.
Таке читання називають «брудним» (dirty read).
Наприклад:
Транзакція A змінює баланс рахунку.
Транзакція A ще не виконала COMMIT.
Транзакція B читає цей баланс і бачить тимчасове значення.
Транзакція A виконує ROLLBACK.
Значення, яке побачила транзакція B, фактично ніколи не існувало в підтвердженому стані бази даних.
Це може призводити до некоректних звітів, помилкових розрахунків і порушення бізнес-логіки.
PostgreSQL приймає рівень ізоляції READ UNCOMMITTED, але не реалізує справжнє брудне читання.
У PostgreSQL:
READ UNCOMMITTEDмає таку саму поведінку, як:
READ COMMITTEDТобто PostgreSQL дозволяє вказати READ UNCOMMITTED для сумісності з SQL-кодом та іншими СУБД, але незавершені зміни інших транзакцій прочитати неможливо.
Це пов’язано з використанням MVCC — механізму багатоверсійного керування конкурентністю. PostgreSQL зберігає версії рядків і показує транзакції лише ті версії, які дозволені її знімком даних.
Рівень ізоляції можна встановити для поточної транзакції:
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SHOW transaction_isolation;
COMMIT;Результат SHOW покаже:
read uncommittedАле цей напис описує вибраний режим, а не фактичну можливість читати незакомічені дані. За поведінкою транзакція все одно працюватиме як READ COMMITTED.
SET TRANSACTION потрібно виконати до першого запиту, який читає або змінює дані в поточній транзакції.
Щоб побачити реальну поведінку, відкрийте два підключення до однієї бази даних.
Спочатку виконайте підготовчий код у будь-якій сесії:
DROP TABLE IF EXISTS accounts;
CREATE TABLE accounts (
id integer PRIMARY KEY,
balance integer NOT NULL
);
INSERT INTO accounts (id, balance)
VALUES (1, 100);У першій сесії почніть транзакцію та змініть баланс, але не підтверджуйте зміни:
BEGIN;
UPDATE accounts
SET balance = 500
WHERE id = 1;На цьому етапі інші транзакції не повинні бачити значення 500.
У другій сесії використайте READ UNCOMMITTED і прочитайте рядок:
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT balance
FROM accounts
WHERE id = 1;Результат:
balance
---------
100Сесія B бачить підтверджене значення 100, а не незакомічене значення 500.
Тепер у сесії A підтвердьте зміну:
COMMIT;Виконайте запит у сесії B ще раз:
SELECT balance
FROM accounts
WHERE id = 1;Результат:
balance
---------
500Сесія B побачила нове значення лише після COMMIT сесії A.
Завершіть транзакцію в сесії B:
COMMIT;Цей приклад показує дві властивості PostgreSQL:
незакомічені зміни іншої транзакції не читаються;
READ UNCOMMITTED фактично поводиться як READ COMMITTED.
У PostgreSQL на рівні READ COMMITTED кожна SQL-команда отримує власний знімок даних на початку виконання.
У нашому прикладі:
Перший SELECT у сесії B виконався до COMMIT сесії A і побачив 100.
Сесія A виконала COMMIT.
Другий SELECT у сесії B виконався вже після COMMIT і побачив 500.
Те, що обидва запити були всередині однієї транзакції B, не змушує їх використовувати один і той самий знімок даних. Для цього PostgreSQL має інші рівні ізоляції, але READ UNCOMMITTED таким рівнем не є — він лише має назву, сумісну зі стандартом, і працює як READ COMMITTED.
У деяких СУБД READ UNCOMMITTED справді може дозволяти:
читати незакомічені зміни;
бачити значення, які пізніше буде скасовано через ROLLBACK;
отримувати нестабільні результати під час паралельних змін.
У PostgreSQL ці властивості відсутні. Вибір READ UNCOMMITTED не вимикає перевірку видимості рядків і не дозволяє обійти MVCC.
Тому перенесення SQL-коду з іншої СУБД до PostgreSQL може зберегти назву рівня ізоляції, але змінити його фактичну поведінку.
У PostgreSQL з практичного погляду — нічим:
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- Поведінка як у READ COMMITTED
COMMIT;і
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Та сама поведінка
COMMIT;Обидва варіанти:
не читають незакомічені зміни інших транзакцій;
використовують знімок на рівні окремої SQL-команди;
бачать підтверджені зміни, які стали доступними до початку чергової команди.
Через це в PostgreSQL зазвичай немає практичної причини явно використовувати READ UNCOMMITTED. Якщо потрібна зрозуміла назва режиму, краще вказувати READ COMMITTED.
Помилково вважати, що такий запит прочитає незакомічені дані:
BEGIN;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT *
FROM accounts;У PostgreSQL він побачить лише доступні підтверджені версії рядків.
SHOWКоманда:
SHOW transaction_isolation;може повернути:
read uncommittedАле це не означає, що PostgreSQL дозволяє брудне читання. Назва рівня зберігається, тоді як його реалізація відповідає READ COMMITTED.
Такий порядок може бути неправильним:
BEGIN;
SELECT 1;
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;Рівень ізоляції потрібно встановити на початку транзакції, до роботи з даними.
У режимі, сумісному з READ COMMITTED, різні SELECT в одній транзакції можуть бачити різні підтверджені стани бази даних. Знімок створюється для кожної команди, а не один раз для всієї транзакції.
READ UNCOMMITTED у SQL-стандарті допускає брудне читання.
PostgreSQL приймає цей рівень ізоляції, але не реалізує брудне читання.
У PostgreSQL READ UNCOMMITTED фактично еквівалентний READ COMMITTED.
Незакомічені зміни інших транзакцій не видно.
У режимі READ COMMITTED кожна команда отримує власний знімок даних.
Значення read uncommitted у SHOW transaction_isolation описує вибрану назву режиму, а не його відмінну від READ COMMITTED поведінку.