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

Что такое принцип ACID?

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

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

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

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

ACID — принцип надёжности баз данных

ACID — это набор из четырёх фундаментальных свойств, которые гарантируют надёжность и целостность данных в системах управления базами данных. Это акроним для Atomicity, Consistency, Isolation, Durability.

A — Atomicity (Атомарность)

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

// Пример: перевод денег со счёта А на счёт Б
async function transferMoney(fromAccountId: number, toAccountId: number, amount: number) {
  const db = getDatabase();
  
  try {
    // Начинаем транзакцию
    await db.query('BEGIN TRANSACTION');
    
    // Снимаем со счёта А
    await db.query(
      'UPDATE accounts SET balance = balance - $1 WHERE id = $2',
      [amount, fromAccountId]
    );
    
    // Добавляем на счёт Б
    await db.query(
      'UPDATE accounts SET balance = balance + $1 WHERE id = $2',
      [amount, toAccountId]
    );
    
    // Коммитим изменения
    await db.query('COMMIT');
  } catch (error) {
    // Откатываем ВСЕ изменения
    await db.query('ROLLBACK');
    throw error;
  }
}

Почему это важно: Если произойдёт ошибка после снятия денег, но до добавления на другой счёт, деньги просто исчезнут. Атомарность гарантирует, что либо обе операции выполнены, либо ни одна.

C — Consistency (Согласованность)

Согласованность означает, что база данных переходит из одного валидного состояния в другое. Все бизнес-правила и ограничения (constraints) выполняются.

-- Пример: таблица с ограничениями целостности
CREATE TABLE orders (
  id INT PRIMARY KEY,
  user_id INT NOT NULL,
  status VARCHAR(20) CHECK (status IN ('pending', 'completed', 'cancelled')),
  total_amount DECIMAL(10, 2) CHECK (total_amount > 0),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

База данных не позволит вставить:

  • Заказ с несуществующим user_id (нарушение FK)
  • Заказ с invalid status (нарушение CHECK)
  • Заказ с отрицательной суммой (нарушение CHECK)
// Пример в приложении
class Order {
  async create(userId: number, items: OrderItem[]): Promise<Order> {
    // Бизнес-правило: заказ не может быть пустым
    if (!items || items.length === 0) {
      throw new InvalidOrderError('Order must have at least one item');
    }
    
    // Бизнес-правило: все товары имеют валидную цену
    const hasInvalidPrice = items.some(item => item.price <= 0);
    if (hasInvalidPrice) {
      throw new InvalidOrderError('All items must have positive price');
    }
    
    return this.repository.save(this);
  }
}

I — Isolation (Изолированность)

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

// Сценарий: две параллельные операции

// Транзакция 1 (T1)
async function buyProduct(productId: number, quantity: number) {
  await db.query('BEGIN ISOLATION LEVEL SERIALIZABLE');
  
  const product = await db.query(
    'SELECT stock FROM products WHERE id = $1 FOR UPDATE',
    [productId]
  );
  
  if (product.stock < quantity) {
    throw new OutOfStockError();
  }
  
  await db.query(
    'UPDATE products SET stock = stock - $1 WHERE id = $2',
    [quantity, productId]
  );
  
  await db.query('COMMIT');
}

// Транзакция 2 (T2) начинается одновременно с T1
// Благодаря ISOLATION LEVEL, T2 не увидит изменения T1 пока та не закоммитилась

Уровни изолированности:

  • READ UNCOMMITTED — может читать незакоммиченные данные (dirty read)
  • READ COMMITTED — читает только закоммиченные данные (по умолчанию в большинстве БД)
  • REPEATABLE READ — гарантирует, что одни и те же строки на протяжении транзакции вернут одни и те же данные
  • SERIALIZABLE — самый высокий уровень, полная изолированность, но медленнее

D — Durability (Долговечность)

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

// Пример: сохраняем критические данные
async function saveCriticalTransaction(transaction: Transaction) {
  await db.query('BEGIN');
  
  // Все операции выполняются в памяти и в логе
  await db.query(
    'INSERT INTO transactions (id, amount, status) VALUES ($1, $2, $3)',
    [transaction.id, transaction.amount, 'pending']
  );
  
  // COMMIT гарантирует, что данные физически записаны на диск
  await db.query('COMMIT');
  
  // Даже если сервер упадёт в следующую секунду,
  // данные останутся в БД
}

Базы данных достигают долговечности через:

  • Write-Ahead Logging (WAL) — запись в лог перед записью на диск
  • fsync() — принудительное сброс буфера на диск
  • Репликация — копирование данных на несколько серверов

Практический пример: критичная операция

async function processPayment(orderId: number, amount: number) {
  const transaction = await db.transaction(async (trx) => {
    // ATOMICITY: всё выполнится или ничего
    
    // Проверяем баланс (CONSISTENCY)
    const wallet = await trx('wallets')
      .where('user_id', userId)
      .forUpdate()
      .first();
    
    if (wallet.balance < amount) {
      throw new InsufficientFundsError();
    }
    
    // ISOLATION: другие транзакции не видят эти изменения
    await trx('wallets').update({
      balance: wallet.balance - amount,
    });
    
    await trx('orders').update({
      status: 'paid',
      paid_at: new Date(),
    }).where('id', orderId);
    
    // DURABILITY: при COMMIT все изменения безопасно записаны
    return { success: true };
  });
  
  return transaction;
}

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

  • Финансовые системы — недопустимы потери денег
  • Система заказов — нельзя потерять или задублировать заказ
  • Критичные данные — где потеря данных = материальный ущерб

Для менее критичных операций (например, счётчики просмотров) можно использовать BASE (Basically Available, Soft state, Eventually consistent) вместо ACID для лучшей производительности.

Что такое принцип ACID? | PrepBro