Зачем нужны транзакции

Транзакции — это механизм, обеспечивающий надёжное выполнение групп операций над базой данных (или другой системой хранения), как **атомарного** и **целостного** блока, который либо выполняется полностью, либо не выполняется вовсе. Их основное назначение — **гарантировать согласованность данных, устойчивость к сбоям и корректную работу при одновременном доступе**. Особенно важны транзакции в многооперационных изменениях и в многопользовательских средах.

---

## 1. **Определение транзакции**

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

---

## 2. **Свойства ACID**

Свойства ACID описывают требования к любой полноценной транзакционной системе:

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

* Все операции внутри транзакции либо выполняются полностью, либо не выполняются вовсе.

* Если одна из операций в транзакции завершится ошибкой, система откатит все уже выполненные операции.

* Например, при переводе денег с одного счёта на другой: либо списание и зачисление произойдут оба, либо ни одно не произойдёт.

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

* После завершения транзакции данные должны оставаться в согласованном состоянии.

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

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

* Одновременные транзакции не должны влиять друг на друга. Результат параллельного выполнения должен быть эквивалентен какому-то последовательному порядку выполнения.

* Это предотвращает "грязные чтения", "неповторяющиеся чтения" и "фантомные чтения".

### D — **Durability** (Надёжность/Устойчивость)

* После фиксации (commit) изменений они должны сохраняться даже при сбоях, например, при отключении питания.

* Это достигается через журналы (write-ahead log), репликацию и устойчивые хранилища.

---

## 3. **Когда нужны транзакции**

### 3.1. **Финансовые операции**

Например, при переводе средств между двумя счетами:

BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

Если произойдёт сбой после первого запроса, но до второго — данные останутся неконсистентными без транзакции.

### 3.2. **Обновление связанных таблиц**

При добавлении заказа и связанных с ним записей:

BEGIN;
INSERT INTO orders ...;
INSERT INTO order_items ...;
COMMIT;

Если одна операция не выполнится, обе должны быть отменены.

### 3.3. **Конкурентный доступ**

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

---

## 4. **Структура транзакции**

Типичный жизненный цикл транзакции:

1. **Begin** — начало транзакции.

2. **Операции** — чтение/запись/изменение данных.

3. **Commit** — фиксирование изменений (успешное завершение).

4. **Rollback** — откат всех изменений (в случае ошибки или по инициативе приложения).

---

## 5. **Типы изоляции транзакций**

Разные уровни изоляции балансируют между производительностью и безопасностью от ошибок параллельного доступа:

| Уровень | Грязные чтения | Неповторяющиеся чтения | Фантомные чтения |

| ---------------- | -------------- | ---------------------- | ---------------- |

| Read Uncommitted | ✅ | ✅ | ✅ |

| Read Committed | ❌ | ✅ | ✅ |

| Repeatable Read | ❌ | ❌ | ✅ |

| Serializable | ❌ | ❌ | ❌ |

**Примеры:**

* **Грязное чтение (Dirty Read)**: одна транзакция читает данные, которые другая ещё не зафиксировала.

* **Неповторяющееся чтение**: повторное чтение тех же данных возвращает разные значения.

* **Фантомное чтение**: повторное выполнение запроса возвращает другие строки (новые или удалённые).

---

## 6. **Транзакции в SQL**

BEGIN TRANSACTION;
UPDATE users SET age = 30 WHERE id = 5;
DELETE FROM logs WHERE user_id = 5;
COMMIT;

Если ошибка:

ROLLBACK;

---

## 7. **Автоматические и ручные транзакции**

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

// пример на Go
tx, err := db.Begin()
...
tx.Commit() // или tx.Rollback()

---

## 8. **Транзакции вне баз данных**

Транзакции используются не только в СУБД:

* **Файловые системы**: при копировании или записи нескольких файлов.

* **Системы сообщений (например, Kafka)**: поддерживают транзакционные отправки и подтверждения.

* **Распределённые системы**: используют распределённые транзакции и протоколы типа 2PC, Saga и т.п.

---

## 9. **Распределённые транзакции**

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

### 9.1. **Two-Phase Commit (2PC)**

* **Prepare**: все участники подтверждают готовность.

* **Commit**: координатор отправляет команду зафиксировать.

Минус: уязвимость к блокировке в случае сбоя координатора.

### 9.2. **Saga Pattern**

* Многошаговый бизнес-процесс, каждая операция имеет **компенсационную** (отменяющую) операцию.

* Используется в микросервисах.

---

## 10. **Журналирование и WAL**

Механизм WAL (Write-Ahead Log) используется для сохранения устойчивости:

1. Все операции записываются в журнал.

2. Только после этого данные записываются в таблицы.

3. В случае сбоя — данные можно восстановить, «переиграв» журнал.

---

## 11. **Почему не всегда нужны транзакции**

* Повышают накладные расходы (особенно `Serializable` изоляция).

* В высоконагруженных системах иногда используются **eventual consistency** или **идемпотентные операции** без строгих транзакций.

---

## 12. **Безопасность и логика приложения**

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

---

## 13. **Примеры проблем без транзакций**

### 13.1. **Потеря денег**

* Списали деньги с одного счёта, а на второй зачислить не успели — пользователь потерял средства.

### 13.2. **Дублирование заказов**

* Один заказ может быть создан дважды, если два потока одновременно видят "свободный" слот.

### 13.3. **Гонки данных**

* Два запроса обновляют одну и ту же строку, и один затирает изменения другого.

---

## 14. **Контроль ошибок в транзакциях**

Пример на Go:

tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if p := recover(); p != nil {
tx.Rollback()
panic(p)
} else if err != nil {
tx.Rollback()
} else {
err = tx.Commit()
}
}()

Такой шаблон гарантирует корректный rollback при ошибке и commit при успехе.

---

## 15. **Проверка и логгирование**

Транзакции позволяют лучше отслеживать и анализировать сбои, так как всегда можно узнать:

* Что выполнилось.

* Что откатилось.

* Почему произошёл rollback.