11 вопросов
Транзакция объединяет несколько операций в одну логическую единицу: либо выполняются все, либо ни одна (atomicity). Обеспечивает консистентность при сбоях и конкурентном доступе. Без транзакции частичное выполнение при ошибке оставляет БД в некорректном состоянии.
$pdo->beginTransaction();
try {
$pdo->exec("INSERT ..."); $pdo->exec("UPDATE ...");
$pdo->commit();
} catch (\Throwable $e) { $pdo->rollBack(); throw $e; }Read Uncommitted - видны незакоммиченные изменения других (грязное чтение). Read Committed - только закоммиченные; повторное чтение может вернуть другую строку (non-repeatable read). Repeatable Read - снимок на начало транзакции; в PG защита от phantom read через snapshot. Serializable - полная изоляция, как последовательное выполнение. По умолчанию в PG - Read Committed, в MySQL - Repeatable Read. Более высокий уровень - меньше аномалий, но больше блокировок/откатов.
Dirty read - чтение незакоммиченных данных другой транзакции; при откате той транзакции прочитанное неверно. Non-repeatable read - в одной транзакции два чтения одной строки дают разные значения (другая транзакция закоммитила изменение). Phantom read - два чтения по одному условию возвращают разный набор строк (другая транзакция вставила/удалила подходящие строки). Read Uncommitted допускает dirty; Read Committed - non-repeatable; Repeatable Read в PG устраняет phantom за счет snapshot; Serializable устраняет все.
Взаимная блокировка: транзакция A ждет ресурс, занятый B, а B ждет ресурс, занятый A. СУБД обнаруживает deadlock и откатывает одну из транзакций (жертва). Избежание: фиксированный порядок блокировки ресурсов (всегда блокировать в одном порядке, например по id); короткие транзакции; избегать цепочек ожидания; ретраи при deadlock. Мониторинг: логирование deadlock, pg_stat_activity.
Пессимистичная - блокируем строку/запись на время транзакции (SELECT FOR UPDATE), другие ждут. Гарантирует консистентность, но снижает параллелизм. Оптимистичная - не блокируем при чтении; при обновлении проверяем, что версия/значения не изменились (version column или WHERE condition); при конфликте - ретраи или ошибка. Меньше блокировок, подходит при низкой конкуренции. Высокая конкуренция - часто пессимистичная или явные блокировки.
Блокирует выбранные строки до конца транзакции. Другая транзакция с SELECT FOR UPDATE или изменением этих строк будет ждать (или откатится при nowait). Используется для пессимистичной блокировки при "прочитал-изменил-записал". FOR UPDATE SKIP LOCKED - пропускать уже заблокированные строки (очереди задач). FOR SHARE - блокировка на чтение (shared lock).
SELECT * FROM orders WHERE id = 1 FOR UPDATE;Точка сохранения внутри транзакции. Можно откатиться к SAVEPOINT, не отменяя всю транзакцию. Полезно для вложенной логики: часть операций откатить, остальное оставить.
SAVEPOINT sp1;
-- операции
ROLLBACK TO sp1; -- откат только до sp1
-- или RELEASE sp1; - убрать точку, сохранив измененияВ PHP/PDO: $pdo->exec('SAVEPOINT name'); $pdo->exec('ROLLBACK TO SAVEPOINT name');
Две транзакции читают одну строку, обе вычисляют новое значение и записывают - последняя запись перезаписывает первую, изменения первой теряются. Решения: блокировка при чтении (SELECT FOR UPDATE), оптимистичная блокировка (version/timestamp или проверка старых значений в UPDATE), атомарное обновление (UPDATE ... SET count = count + 1) вместо read-modify-write.
Аномалия при изоляции ниже Serializable: две транзакции читают согласованное состояние, каждая обновляет "свою" строку по прочитанным данным, результат нарушает инвариант. Пример: два врача читают дежурства, оба видят "есть еще один дежурный", оба снимают себя - никто не дежурит. Решение: Serializable изоляция или явные блокировки так, чтобы решение учитывало актуальное состояние (например, блокировка всех строк, участвующих в инварианте).
Протокол распределенной фиксации транзакции с участием нескольких ресурсов (например, две БД). Фаза 1: координатор запрашивает всех участников "готовы ли закоммитить?" (prepare); если все да - фаза 2: координатор шлет commit. При отказе любого - всем rollback. Гарантирует атомарность в распределенной среде, но координатор и участники могут "зависнуть" при сбое (блокировки до решения). Часто заменяют на саги с компенсирующими действиями.
Журнал упреждающей записи: изменения сначала записываются в лог на диск, затем в сами данные. При сбое восстановление по WAL: повторное применение закоммиченных изменений. Обеспечивает durability. В PostgreSQL WAL используется также для репликации (streaming). Регулируется размер сегмента, checkpoint, archive_mode для бэкапов.