Ответ
ACID — это акроним, описывающий четыре ключевых свойства, которые гарантируют надежность транзакций в базе данных:
- Atomicity (Атомарность): Транзакция выполняется либо полностью, либо не выполняется вовсе. Не может быть промежуточного состояния. Если один из запросов внутри транзакции завершился ошибкой, все предыдущие изменения откатятся.
- Consistency (Согласованность): Транзакция переводит базу данных из одного согласованного состояния в другое. Все правила и ограничения (constraints, triggers) базы данных должны быть соблюдены.
- Isolation (Изолированность): Параллельно выполняющиеся транзакции не должны влиять друг на друга. Результат параллельных транзакций должен быть таким же, как если бы они выполнялись последовательно.
- Durability (Долговечность): Если транзакция успешно завершена (закоммичена), ее изменения сохраняются в базе данных навсегда, даже в случае сбоя системы (например, отключения питания).
Реализация в Go
В Go для работы с транзакциями в SQL-базах данных используется стандартный пакет database/sql
и объект sql.Tx
.
Алгоритм работы с транзакцией:
- Начать транзакцию с помощью
db.Begin()
илиdb.BeginTx()
. - Выполнять все запросы (INSERT, UPDATE, DELETE) в рамках этой транзакции, используя методы объекта
tx
(tx.ExecContext
,tx.QueryRowContext
и т.д.). - Если на любом из шагов возникает ошибка, необходимо откатить транзакцию с помощью
tx.Rollback()
. - Если все операции прошли успешно, транзакцию нужно подтвердить с помощью
tx.Commit()
.
Пример кода:
// ctx - контекст, db - установленное соединение с БД
func transferMoney(ctx context.Context, db *sql.DB, fromID, toID int64, amount int) error {
// 1. Начинаем транзакцию
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("could not begin transaction: %w", err)
}
// 3. Откатываем транзакцию в случае ошибки (defer гарантирует вызов)
defer tx.Rollback() // Rollback будет проигнорирован, если tx.Commit() выполнится успешно
// 2. Выполняем операции
_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromID)
if err != nil {
return fmt.Errorf("failed to withdraw: %w", err)
}
_, err = tx.ExecContext(ctx, "UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, toID)
if err != nil {
return fmt.Errorf("failed to deposit: %w", err)
}
// 4. Если все успешно, коммитим транзакцию
if err := tx.Commit(); err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}
Использование defer tx.Rollback()
— это идиоматический и безопасный способ гарантировать откат транзакции при любой ошибке или панике в функции.