← Назад к вопросам

Что решает каждый уровень изоляции транзакций?

2.3 Middle🔥 201 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Что решает каждый уровень изоляции транзакций?

Уровни изоляции транзакций в SQL определяют, как транзакции взаимодействуют между собой и какие проблемы они предотвращают. В стандарте SQL определены четыре уровня изоляции, каждый из которых решает определённые проблемы параллельного доступа к данным.

Проблемы параллельных транзакций

Прежде чем рассматривать уровни, нужно понимать, какие проблемы они решают:

1. Dirty Read (грязное чтение)

Транзакция A читает данные, которые была изменены транзакция B, но B ещё не зафиксировала (не выполнила COMMIT) свои изменения.

-- Транзакция A              -- Транзакция B
BEGIN;                       BEGIN;
SELECT balance              UPDATE accounts
FROM accounts                SET balance = 500
WHERE id = 1;               WHERE id = 1;
-- Прочитала 500             
                             ROLLBACK;  -- Откатилась!
-- Теперь balance не 500!

2. Non-Repeatable Read (нестабильное чтение)

Транзакция A читает данные дважды, но между чтениями другая транзакция B изменила эти данные.

-- Транзакция A              -- Транзакция B
BEGIN;                       BEGIN;
SELECT balance = 100         UPDATE accounts
FROM accounts                SET balance = 200
WHERE id = 1;               WHERE id = 1;
COMMIT;

SELECT balance = 200         
FROM accounts
WHERE id = 1;               
-- Значения отличаются!
COMMIT;

3. Phantom Read (фантомное чтение)

Транзакция A выполняет запрос и получает набор строк. Затем другая транзакция B добавляет или удаляет строки. Когда A повторит запрос, появятся новые или исчезнут старые строки.

-- Транзакция A              -- Транзакция B
BEGIN;                       BEGIN;
SELECT COUNT(*)              INSERT INTO orders
FROM orders                  VALUES (...);
WHERE user_id = 1;          
-- Результат: 5              COMMIT;

SELECT COUNT(*)              
FROM orders
WHERE user_id = 1;          
-- Результат: 6 (новая строка)!
COMMIT;

Четыре уровня изоляции

1. READ UNCOMMITTED (Чтение грязных данных)

Что решает: Самый слабый уровень, по сути не решает никаких проблем параллелизма.

Какие проблемы остаются:

  • Dirty Read — ДА (возможны)
  • Non-Repeatable Read — ДА (возможны)
  • Phantom Read — ДА (возможны)

Когда использовать: Практически никогда в продакшене. Только когда нужна максимальная производительность и данные некритичны.

Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);

2. READ COMMITTED (Чтение зафиксированных данных)

Что решает: Предотвращает Dirty Read. Транзакция может читать только те данные, которые уже были зафиксированы другими транзакциями.

Какие проблемы остаются:

  • Dirty Read — НЕТ (решено)
  • Non-Repeatable Read — ДА (возможны)
  • Phantom Read — ДА (возможны)

Когда использовать: Уровень по умолчанию в большинстве СУБД (PostgreSQL, Oracle). Хороший компромисс между безопасностью и производительностью.

Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

Пример:

-- Транзакция A              -- Транзакция B
BEGIN;                       BEGIN;
SELECT balance              UPDATE accounts
FROM accounts                SET balance = 500
WHERE id = 1;               WHERE id = 1;
-- Прочитает старое значение COMMIT;  -- Теперь данные зафиксированы
                            
SELECT balance              -- Может прочитать новое значение
FROM accounts
WHERE id = 1;               
COMMIT;

3. REPEATABLE READ (Повторяемое чтение)

Что решает: Предотвращает Dirty Read и Non-Repeatable Read. Транзакция видит снимок данных, сделанный в начале транзакции, и одна и та же выборка вернёт одинаковые результаты.

Какие проблемы остаются:

  • Dirty Read — НЕТ (решено)
  • Non-Repeatable Read — НЕТ (решено)
  • Phantom Read — ДА (возможны в большинстве СУБД)

Когда использовать: Когда нужна согласованность в пределах одной транзакции, но можно допустить вставку новых строк.

Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

Пример с MVCC (как в PostgreSQL и MySQL):

-- Транзакция A              -- Транзакция B
BEGIN;                       BEGIN;
SELECT balance = 100         UPDATE accounts
FROM accounts                SET balance = 200
WHERE id = 1;               WHERE id = 1;
                             COMMIT;  

SELECT balance = 100         -- Видит старый снимок
FROM accounts
WHERE id = 1;               
COMMIT;

4. SERIALIZABLE (Сериализуемость)

Что решает: Самый высокий уровень изоляции. Полностью предотвращает все три проблемы: Dirty Read, Non-Repeatable Read и Phantom Read.

Какие проблемы остаются:

  • Dirty Read — НЕТ (полностью решено)
  • Non-Repeatable Read — НЕТ (полностью решено)
  • Phantom Read — НЕТ (полностью решено)

Когда использовать: Когда нужна максимальная безопасность данных, даже ценой производительности. Критичные финансовые операции.

Connection conn = dataSource.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);

Как это работает: СУБД использует блокировки или версионирование, чтобы гарантировать, что транзакции выполняются так, как если бы они выполнялись последовательно (одна за другой).

Таблица сравнения

УровеньDirty ReadNon-RepeatablePhantom ReadПроизводительность
READ UNCOMMITTEDДАДАДАМаксимальная
READ COMMITTEDНЕТДАДАХорошая
REPEATABLE READНЕТНЕТДА*Средняя
SERIALIZABLEНЕТНЕТНЕТМинимальная

*В некоторых реализациях (PostgreSQL) Phantom Read также предотвращается на уровне REPEATABLE READ.

Практический пример в Java

@Service
public class TransferService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        // Использует READ_COMMITTED — баланс между безопасностью и скоростью
        BigDecimal balance = getBalance(fromId);
        if (balance.compareTo(amount) >= 0) {
            withdrawMoney(fromId, amount);
            depositMoney(toId, amount);
        }
    }
    
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void criticalFinancialOperation() {
        // Максимальная безопасность для критичных операций
    }
    
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public void reportGeneration() {
        // Гарантирует стабильное чтение данных для отчёта
    }
}

Выбор правильного уровня

  1. По умолчанию используй READ COMMITTED — это хороший компромисс
  2. REPEATABLE READ — если нужна стабильность в пределах одной транзакции
  3. SERIALIZABLE — только для критичных операций (деньги, инвентарь)
  4. READ UNCOMMITTED — практически никогда

Каждый уровень решает определённый набор проблем параллелизма и имеет свою стоимость в производительности. Правильный выбор уровня изоляции — это баланс между безопасностью данных и производительностью приложения.

Что решает каждый уровень изоляции транзакций? | PrepBro