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

Зачем нужны низкие уровни изоляции в БД?

2.4 Senior🔥 161 комментариев
#Архитектура и паттерны#Базы данных (SQL)

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

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

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

Низкие уровни изоляции в БД

Уровни изоляции (isolation levels) контролируют, как конкурирующие транзакции видят изменения друг друга. Низкие уровни позволяют большую конкурентность, но создают проблемы с консистентностью.

Иерархия уровней изоляции

OT СЛАБОГО К СИЛЬНОМУ:

1. Dirty Read (READ UNCOMMITTED)      ← Низкие уровни
2. Non-Repeatable Read (READ COMMITTED)
3. Phantom Read (REPEATABLE READ)
4. Serializable (SERIALIZABLE)        ← Высокие уровни

1. READ UNCOMMITTED (самый слабый)

Транзакция может читать НЕПОДТВЕРЖДЁННЫЕ изменения (грязное чтение).

Транзакция 1:                  Транзакция 2:
BEGIN;
UPDATE users 
SET balance = 500 
WHERE id = 1;
                               BEGIN;
                               SELECT balance FROM users WHERE id = 1;
                               → Прочитала 500! (не подтверждено)
ROLLBACK;  ← Откатилась!
                               SELECT balance FROM users WHERE id = 1;
                               → Теперь 1000 (откатилась, но мы видели 500)

Проблемы: Грязные чтения Используется: Почти никогда (слишком опасно)

2. READ COMMITTED

Транзакция видит только подтверждённые данные, но возможны non-repeatable reads.

Транзакция 1:                  Транзакция 2:
BEGIN;
SELECT balance FROM users 
WHERE id = 1;
→ balance = 1000
                               BEGIN;
                               UPDATE users SET balance = 500 WHERE id = 1;
                               COMMIT;
SELECT balance FROM users 
WHERE id = 1;
→ balance = 500! (изменилось в одной транзакции)

Проблемы: Non-repeatable reads (один запрос → разные результаты) Используется: PostgreSQL по умолчанию, часто достаточно

3. REPEATABLE READ

В рамках транзакции одни и те же данные всегда одинаковые, но возможны phantom reads.

Транзакция 1:                  Транзакция 2:
BEGIN;
SELECT * FROM users 
WHERE age > 18;
→ 10 пользователей
                               BEGIN;
                               INSERT INTO users (name, age) 
                               VALUES ('Alice', 25);
                               COMMIT;
SELECT * FROM users 
WHERE age > 18;
→ 11 пользователей! (фантомные строки)

Проблемы: Phantom reads (новые строки появляются) Используется: MySQL InnoDB по умолчанию, хороший баланс

4. SERIALIZABLE (самый сильный)

Транзакции выполняются как если бы они выполнялись по одной (последовательно), без конкурентности.

Транзакция 1:                  Транзакция 2:
BEGIN;
SELECT * FROM users;
                               BEGIN;
                               UPDATE users SET active = true;
                               ← Ждёт! (блокируется)
COMMIT;
                               ← Теперь может выполняться
OK

Проблемы: Очень медленно (много блокировок) Используется: Только когда абсолютная консистентность критична

Зачем нужны НИЗКИЕ уровни?

1. Производительность

Низкие уровни позволяют более высокую конкурентность и пропускную способность:

# При READ COMMITTED
# Транзакция 1 читает строку X
# Транзакция 2 может сразу же обновить строку X
# Оба выполняются быстро

# При SERIALIZABLE
# Если Транзакция 1 читает X
# Транзакция 2 ЖДЁТ, пока Транзакция 1 закончит
# Общее время выполнения: сумма времени

2. Масштабируемость

Высокие нагрузки требуют низких уровней:

-- Веб-приложение с 1000 одновременных юзеров
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;  
-- SERIALIZABLE взорвёт БД от количества блокировок

3. Практические сценарии

READ COMMITTED — для большинства приложений:

# Интернет-магазин
BEGIN;
SELECT stock FROM products WHERE id = 123;  
-- Одна транзакция прочитала stock = 10
-- Другая транзакция уменьшила его до 5
-- Первая увидит 5 при следующем запросе
-- Это нормально для магазина!
COMMIT;

REPEATABLE READ — для транзакций, требующих консистентности данных:

# Банковский перевод
BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ;
balance_from = SELECT balance FROM accounts WHERE id = 1;  # 1000
balance_to = SELECT balance FROM accounts WHERE id = 2;    # 500

if balance_from >= 100:
    UPDATE accounts SET balance = balance_from - 100 WHERE id = 1;
    UPDATE accounts SET balance = balance_to + 100 WHERE id = 2;
COMMIT;

Проблема: когда читаешь разные значения?

# Non-repeatable read
BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
name = SELECT name FROM users WHERE id = 1;  # "Alice"
# Другая транзакция обновила: UPDATE users SET name = "Bob" WHERE id = 1;
name = SELECT name FROM users WHERE id = 1;  # "Bob" — изменилось!

Решение: используй REPEATABLE READ если нужна стабильность.

Практическое руководство

import psycopg2

# PostgreSQL
conn = psycopg2.connect("dbname=mydb")

# Установить уровень изоляции
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
# или
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_REPEATABLE_READ)
# или
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)

# SQL
cursor = conn.cursor()
cursor.execute("""
    BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED;
    SELECT * FROM users;
    COMMIT;
""")

Таблица выбора

СитуацияУровеньПричина
Веб-приложение (обычное)READ COMMITTEDБаланс скорости и консистентности
Финансовые операцииREPEATABLE READНужна стабильность данных
Критичные платежиSERIALIZABLEАбсолютная консистентность
Аналитика, отчётыREAD UNCOMMITTEDСкорость важнее точности (редко)

Низкие уровни изоляции — необходимо зло для балансировки между консистентностью и производительностью.