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

Какой уровень изоляции самый высокий?

1.8 Middle🔥 141 комментариев
#Основы Java

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

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

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

Уровень изоляции транзакций в базах данных

Ответ

SERIALIZABLE — это самый высокий уровень изоляции транзакций.

Он гарантирует, что транзакции выполняются так, как если бы они были выполнены последовательно, один за другим, без перекрытия.

4 уровня изоляции (ACID)

По возрастанию уровня: READ UNCOMMITTED < READ COMMITTED < REPEATABLE READ < SERIALIZABLE

1. READ UNCOMMITTED (самый низкий)

Разрешает все проблемы:

  • Dirty reads (грязное чтение)
  • Non-repeatable reads
  • Phantom reads
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void processTransaction() {
    // Транзакция может прочитать незафиксированные изменения из других транзакций
    User user = userRepository.findById(1L);
    // user может содержать данные, которые ещё не committed
}

// Редко используется, очень опасно
// SQL пример:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

Проблема: Dirty Read

Транзакция A              | Транзакция B
─────────────────────────────────────
BEGIN TRANSACTION;        |
UPDATE user SET 
  balance = 50            |
  WHERE id = 1;           |
                          | BEGIN TRANSACTION;
                          | SELECT balance FROM user WHERE id = 1;
                          | -- Видит 50 (незафиксированное значение!)
                          | COMMIT;
ROLLBACK;                 |
-- Откатили, balance = 100|
-- Но B уже прочитал 50!

2. READ COMMITTED (по умолчанию в большинстве БД)

Разрешает только:

  • Non-repeatable reads
  • Phantom reads
@Transactional(isolation = Isolation.READ_COMMITTED)
public void processTransaction() {
    // По умолчанию в PostgreSQL, SQL Server
    // Вы видите только зафиксированные данные
    User user = userRepository.findById(1L);
}

// SQL пример:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
BEGIN TRANSACTION;
SELECT * FROM users WHERE id = 1;
-- видит только committed данные
COMMIT;

Проблема: Non-repeatable Read

Транзакция A              | Транзакция B
─────────────────────────────────────
BEGIN TRANSACTION;        |
SELECT balance FROM user  |
  WHERE id = 1;           |
-- Видит: 100             |
                          | BEGIN TRANSACTION;
                          | UPDATE user SET balance = 50 WHERE id = 1;
                          | COMMIT;
SELECT balance FROM user  |
  WHERE id = 1;           |
-- Видит: 50 (изменилось!)
-- Один и тот же SELECT дал разные результаты
COMMIT;

3. REPEATABLE READ (MySQL по умолчанию)

Разрешает только:

  • Phantom reads
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void processTransaction() {
    // Один SELECT всегда вернёт одинаковый результат в границах транзакции
    List<User> users = userRepository.findAll();
    // Если другая транзакция добавит новый user после этого SELECT,
    // мы не увидим его (кроме как в следующем SELECT)
}

// SQL пример:
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT * FROM users WHERE age > 18;
-- SELECT вернёт X рядов
-- Даже если другая транзакция добавит/удалит юзеров,
-- эти изменения не будут видны в этом SELECT
COMMIT;

Проблема: Phantom Read

Транзакция A              | Транзакция B
─────────────────────────────────────
BEGIN TRANSACTION;        |
SELECT COUNT(*) FROM users|
  WHERE status='ACTIVE';  |
-- Результат: 5           |
                          | BEGIN TRANSACTION;
                          | INSERT INTO users (status) VALUES ('ACTIVE');
                          | COMMIT;
SELECT COUNT(*) FROM users|
  WHERE status='ACTIVE';  |
-- Результат: 6 (новая строка появилась!)
COMMIT;

4. SERIALIZABLE (самый высокий)

Не разрешает никакие проблемы:

  • Нет dirty reads
  • Нет non-repeatable reads
  • Нет phantom reads
@Transactional(isolation = Isolation.SERIALIZABLE)
public void processTransaction() {
    // Полная изоляция
    List<User> users = userRepository.findAll();
    // Никаких изменений в других транзакциях не влияют
    // Транзакции выполняются как if they were serial
}

// SQL пример:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
SELECT * FROM users WHERE status='ACTIVE';
UPDATE users SET balance = balance - 100 WHERE id = 5;
COMMIT;

-- Во время этой транзакции никакая другая транзакция не может
-- ни читать, ни писать данные, затронутые этой транзакцией

Механизм SERIALIZABLE

Range locks (блокировки диапазонов):

public class OrderService {
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void createOrder(CreateOrderRequest request) {
        // 1. Читаем товары в диапазоне (price > 100)
        List<Product> expensive = productRepository.findByPriceGreaterThan(100);
        
        // 2. Вся таблица товаров ЗАБЛОКИРОВАНА для других транзакций
        // Никто не может:
        //   - Изменять товары
        //   - Добавлять товары
        //   - Удалять товары
        //   - Читать те же товары
        
        // 3. Создаём заказ
        Order order = new Order(expensive);
        orderRepository.save(order);
        
        // 4. Блокировка снимается при COMMIT
    }
}

Таблица изоляции

УровеньDirty ReadNon-rep. ReadPhantom ReadПроизводительностьИспользование
READ UNCOMMITTEDДаДаДаВысокаяРедко
READ COMMITTEDНетДаДаХорошаяDefault большинства БД
REPEATABLE READНетНетДаСредняяMySQL default
SERIALIZABLEНетНетНетНизкаяКритичные транзакции

Практические примеры

SERIALIZABLE для финансовых операций:

@Service
public class PaymentService {
    
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void transferMoney(Long fromUserId, Long toUserId, BigDecimal amount) {
        // Полная изоляция от других платежей
        User from = userRepository.findById(fromUserId).orElseThrow();
        User to = userRepository.findById(toUserId).orElseThrow();
        
        if (from.getBalance().compareTo(amount) < 0) {
            throw new InsufficientFundsException();
        }
        
        from.setBalance(from.getBalance().subtract(amount));
        to.setBalance(to.getBalance().add(amount));
        
        userRepository.save(from);
        userRepository.save(to);
        
        transactionRepository.save(new Transaction(from, to, amount));
    }
}

READ COMMITTED для обычных операций:

@Transactional(isolation = Isolation.READ_COMMITTED)
public List<Product> getProductsByCategory(String category) {
    // Обычная бизнес-логика
    // Видим committed данные
    // Производительность нормальная
    return productRepository.findByCategory(category);
}

Трейд-офф

SERIALIZABLE:

  • Максимальная безопасность данных
  • Минимальная конкурентность (потоки ждут)
  • Риск deadlock-ов
  • Может быть медленнее в 10-100 раз

READ COMMITTED:

  • Хороший баланс
  • Хорошая конкурентность
  • Приемлемый уровень безопасности
  • Нужно обрабатывать non-repeatable reads в коде

Вывод

SERIALIZABLE — самый высокий уровень изоляции. Используется редко из-за performance penalty, но критичен для:

  • Финансовых систем
  • Платежных шлюзов
  • Критичных бизнес-операций
  • Когда data consistency важнее, чем throughput
Какой уровень изоляции самый высокий? | PrepBro