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

Что такое ACID в контексте баз данных?

1.0 Junior🔥 201 комментариев
#SQL и базы данных

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

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

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

ACID в контексте баз данных

ACID — это набор фундаментальных принципов, определяющих надежность и согласованность транзакций в базах данных. Эти принципы критичны для систем, где целостность данных имеет первостепенное значение (финансовые системы, e-commerce, учет). ACID обеспечивает, что данные остаются в согласованном состоянии даже при сбоях.

ACID расшифровка

1. Atomicity (Атомарность)

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

Пример: Перевод денег с счёта A на счёт B:

BEGIN TRANSACTION;
  -- Шаг 1: списать со счёта A
  UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';
  
  -- Шаг 2: зачислить на счёт B
  UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';
  
  -- Если произойдёт сбой между шагом 1 и 2, ВЕСЬ перевод отменяется (ROLLBACK)
COMMIT;  -- Оба шага выполнены успешно

Почему это важно: Если система упадёт между шагом 1 и 2, деньги не исчезнут. БД откатит обе операции и восстановит баланс.

# Python/SQLAlchemy пример
try:
    db.session.begin()
    account_a.balance -= 100
    account_b.balance += 100
    db.session.commit()  # Атомарный коммит
except Exception as e:
    db.session.rollback()  # Откат обеих операций при ошибке
    print(f"Транзакция отменена: {e}")

Уровень гарантии: В СУБД реализовано на уровне transaction log. Если коммит не записался в лог — транзакция считается невыполненной.

2. Consistency (Согласованность)

Определение: База данных переходит из одного согласованного состояния в другое. Все правила целостности (constraints, triggers, checks) выполняются до и после транзакции.

Примеры constraints:

-- PRIMARY KEY: каждый ID уникален
CREATE TABLE users (
  user_id SERIAL PRIMARY KEY,
  email VARCHAR UNIQUE NOT NULL
);

-- FOREIGN KEY: связь должна существовать
CREATE TABLE orders (
  order_id SERIAL PRIMARY KEY,
  user_id INT REFERENCES users(user_id)
);

-- CHECK: значение должно быть в диапазоне
CREATE TABLE products (
  product_id SERIAL PRIMARY KEY,
  price DECIMAL(10,2) CHECK (price > 0)
);

-- NOT NULL: значение обязательно
CREATE TABLE employees (
  employee_id SERIAL PRIMARY KEY,
  salary INT NOT NULL
);

Пример нарушения consistency:

-- Плохо: нарушение FOREIGN KEY
INSERT INTO orders (order_id, user_id) VALUES (1, 999);
-- ERROR: violates foreign key constraint (user_id 999 не существует)

-- Плохо: нарушение CHECK
INSERT INTO products (product_id, price) VALUES (1, -10);
-- ERROR: violates check constraint (price должна быть > 0)

Правильный подход:

-- Сначала создать пользователя
INSERT INTO users (user_id, email) VALUES (1, 'john@example.com');

-- Потом заказ
INSERT INTO orders (order_id, user_id) VALUES (1, 1);
-- OK: integrity maintained

3. Isolation (Изолированность)

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

Проблемы без Isolation:

-- Проблема 1: Dirty Read (чтение грязных данных)
TX1: SELECT balance FROM accounts WHERE id = 1;  -- Читает 1000
TX2: UPDATE accounts SET balance = 500 WHERE id = 1;  -- Меняет
TX1: SELECT balance FROM accounts WHERE id = 1;  -- Видит 500?
TX2: ROLLBACK;  -- Откат! Баланс должен остаться 1000

-- Проблема 2: Non-repeatable Read (неповторяющееся чтение)
TX1: SELECT balance FROM accounts WHERE id = 1;  -- Читает 1000
TX2: UPDATE accounts SET balance = 2000 WHERE id = 1;  -- Меняет
TX2: COMMIT;
TX1: SELECT balance FROM accounts WHERE id = 1;  -- Видит 2000 (inconsistent!)

-- Проблема 3: Phantom Read (фантомное чтение)
TX1: SELECT COUNT(*) FROM accounts WHERE type = 'savings';  -- Читает 5
TX2: INSERT INTO accounts (type) VALUES ('savings');  -- Вставляет
TX2: COMMIT;
TX1: SELECT COUNT(*) FROM accounts WHERE type = 'savings';  -- Видит 6 (inconsistent!)

Уровни Isolation (от слабого к сильному):

-- 1. READ UNCOMMITTED — самый слабый
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- Позволяет Dirty Read, Non-repeatable Read, Phantom Read

-- 2. READ COMMITTED — умолчание в PostgreSQL, MySQL
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Предотвращает Dirty Read
-- Позволяет Non-repeatable Read, Phantom Read

-- 3. REPEATABLE READ
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- Предотвращает Dirty Read, Non-repeatable Read
-- Позволяет Phantom Read

-- 4. SERIALIZABLE — самый сильный
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- Предотвращает все три проблемы
-- Медленнее, но полностью изолирует транзакции

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

# SQLAlchemy с изоляцией
from sqlalchemy import create_engine, select
from sqlalchemy.orm import Session

engine = create_engine('postgresql://...')

with Session(engine) as session:
    # Установить уровень изоляции
    session.connection().execution_options(
        isolation_level="REPEATABLE_READ"
    )
    
    # Транзакция выполняется в этом контексте с выбранным уровнем
    result = session.query(Account).filter(Account.id == 1).first()
    result.balance -= 100
    session.commit()

4. Durability (Долговечность)

Определение: Как только транзакция закоммитена, данные остаются в БД даже при сбоях (отключение питания, краш, ошибка диска).

Реализация:

  • Write-Ahead Logging (WAL): перед записью в main file, запись идёт в log
  • Persistent Storage: данные на диске, а не только в памяти
  • Fsync: операция записи завершается только когда данные физически на диске
-- PostgreSQL для Durability
BEGIN;
  INSERT INTO transactions (id, amount) VALUES (1, 100);
COMMIT;  -- Данные теперь в WAL и готовы к диску

-- Даже если БД упадёт сейчас, данные восстановятся из WAL

Параметры durability (PostgreSQL):

-- Безопасно и медленно (гарантирует durability)
SET synchronous_commit = 'on';

-- Быстрее, но меньше гарантий
SET synchronous_commit = 'local';

-- Быстро, но данные могут потеряться при краше
SET synchronous_commit = 'off';

ACID в действии: Реальный пример

-- Сценарий: платёж 50 USD с карты на счёт
BEGIN TRANSACTION;
  -- Atomicity: все или ничего
  -- Consistency: все constraints проверяются
  -- Isolation: другие транзакции не видят промежуточное состояние
  
  UPDATE cards SET balance = balance - 50 WHERE card_id = 'A' AND balance >= 50;
  -- Consistency: проверка balance >= 50 (避免overspend)
  -- Isolation: блокировка строки для других транзакций
  
  UPDATE accounts SET balance = balance + 50 WHERE account_id = 'B';
  -- Atomicity: если ошибка здесь, первый UPDATE откатится
  
  INSERT INTO transaction_log (card_id, account_id, amount, timestamp)
    VALUES ('A', 'B', 50, NOW());
  -- Durability: лог записывается на диск
  
COMMIT;  -- Все изменения сохранены атомарно

Когда ACID критичен

Высокий приоритет ACID:

  • Финансовые системы (банки, платежи)
  • E-commerce (заказы, платежи)
  • Бронирование (авиа, отели)
  • Учёт и бухгалтерия

Можно ослабить ACID:

  • Analytics (историческая аналитика)
  • Кэширование (Redis, Memcached)
  • NoSQL для масштабирования (BASE вместо ACID)

BASE: альтернатива ACID

Dля масштабируемых распределённых систем часто используется BASE (Basic Availability, Soft state, Eventually consistent):

ACID: Strongly consistent, но медленнее
BASE: Eventually consistent, но быстрее и масштабируемее

Примеры: MongoDB, Cassandra, DynamoDB используют BASE вместо ACID для высокой доступности.

Практический чеклист для Data Analyst

  • Atomicity: данные не потеряются при сбое?
  • Consistency: все constraints соблюдаются?
  • Isolation: параллельные запросы не конфликтуют?
  • Durability: committed данные безопасны?
  • Выбрать правильный isolation level: REPEATABLE READ для critical operations
  • Мониторить долгие транзакции: они блокируют другие
  • Используй ACID для критичных данных, BASE для масштабирования

ACID принципы — основа надёжных баз данных. Понимание этих концепций критично для работы с данными и проектирования систем.