Ответ
Транзакции — это последовательность операций с базой данных, которая рассматривается как единая, неделимая логическая единица работы. Они гарантируют целостность данных с помощью свойств ACID.
Преимущества (свойства ACID):
- Атомарность (Atomicity): Гарантирует, что либо все операции внутри транзакции успешно выполняются, либо ни одна из них. Если происходит сбой, все изменения откатываются. Это предотвращает частичное обновление данных.
- Согласованность (Consistency): Транзакция переводит базу данных из одного согласованного состояния в другое. Все правила и ограничения (constraints, triggers) базы данных соблюдаются.
- Изоляция (Isolation): Параллельно выполняющиеся транзакции не должны влиять друг на друга. Результат их одновременного выполнения должен быть таким же, как если бы они выполнялись последовательно.
- Долговечность (Durability): После успешного завершения транзакции (коммита) ее результаты сохраняются в базе данных и не будут потеряны даже в случае системного сбоя (например, перезагрузки или сбоя питания).
Недостатки:
- Снижение производительности: Механизмы блокировок, необходимые для обеспечения изоляции, могут приводить к ожиданиям и замедлению работы, особенно при высокой конкуренции за одни и те же данные.
- Сложность в распределенных системах: Обеспечение ACID-гарантий в распределенных базах данных — сложная и дорогостоящая задача (см. двухфазный коммит).
- Риск взаимоблокировок (Deadlocks): Две или более транзакции могут взаимно блокировать друг друга, ожидая освобождения ресурсов, что требует специальных механизмов для их обнаружения и разрешения.
Пример на Go:
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
// defer tx.Rollback() — это идиоматический способ гарантировать откат транзакции,
// если функция завершится с ошибкой до вызова tx.Commit().
defer tx.Rollback()
// Списываем средства с одного счета
_, err = tx.Exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
if err != nil {
// Если здесь произойдет ошибка, defer tx.Rollback() сработает
return err
}
// Зачисляем средства на другой счет
_, err = tx.Exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
if err != nil {
return err
}
// Если все операции успешны, фиксируем изменения
return tx.Commit()