Какие уровни изоляции транзакций существуют в базах данных?

«Какие уровни изоляции транзакций существуют в базах данных?» — вопрос из категории Базы данных, который задают на 26% собеседований Node.js Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Уровни изоляции транзакций определяют, насколько транзакции изолированы друг от друга, балансируя между целостностью данных и производительностью. Стандарт SQL определяет четыре уровня (от наименее строгого к наиболее строгому):

  1. READ UNCOMMITTED

    • Проблема: Грязное чтение (Dirty Read). Транзакция может видеть незафиксированные изменения другой транзакции.
    • Использование: Крайне редко, из-за риска увидеть данные, которые могут быть откатаны.
  2. READ COMMITTED

    • Решает: Грязное чтение. Транзакция видит только зафиксированные данные.
    • Проблемы: Неповторяемое чтение (Non-repeatable Read) и фантомное чтение (Phantom Read). Между двумя чтениями одной строки в транзакции другая транзакция может изменить или удалить эту строку.
    • По умолчанию во многих СУБД (PostgreSQL, Oracle).
  3. REPEATABLE READ

    • Решает: Грязное и неповторяемое чтение. Гарантирует, что строки, прочитанные в транзакции, не изменятся.
    • Проблема: Фантомное чтение. Могут появиться новые строки, удовлетворяющие условию запроса.
    • По умолчанию в MySQL (InnoDB).
  4. SERIALIZABLE

    • Решает: Все проблемы (грязное, неповторяемое, фантомное чтение). Самый строгий уровень.
    • Механизм: Транзакции выполняются так, как если бы они шли последовательно. Часто реализуется через блокировки диапазонов.
    • Недостаток: Наибольшее влияние на производительность и параллелизм.

Пример настройки в Node.js с использованием клиента pg для PostgreSQL:

const { Client } = require('pg');
const client = new Client();
await client.connect();

// Установка уровня изоляции для транзакции
await client.query('BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ');

try {
  await client.query('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
  await client.query('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
  await client.query('COMMIT');
} catch (err) {
  await client.query('ROLLBACK');
  throw err;
}

Выбор уровня зависит от требований к согласованности данных и допустимых компромиссов в конкретном сценарии приложения.