Ответ
Транзакция — это последовательность операций с базой данных, которая рассматривается как единая, неделимая логическая единица работы. Либо все операции внутри транзакции успешно выполняются, либо ни одна из них не оказывает влияния на состояние БД.
Свойства ACID
Транзакции гарантируют соблюдение принципов ACID:
- Atomicity (Атомарность): Транзакция выполняется полностью или не выполняется вовсе. Не может быть частичного выполнения.
- Consistency (Согласованность): После завершения транзакции база данных переходит из одного корректного состояния в другое. Все правила и ограничения (constraints) должны быть соблюдены.
- Isolation (Изолированность): Параллельно выполняющиеся транзакции не должны влиять друг на друга. Результаты одной транзакции становятся видны другим только после её фиксации (commit).
- Durability (Долговечность): Если транзакция успешно завершена, её результаты сохраняются даже в случае сбоев системы (например, перезагрузки или отключения питания).
Пример: банковский перевод
BEGIN; -- Начать транзакцию
-- Снять 100 со счета A
UPDATE accounts SET balance = balance - 100 WHERE user_id = 'A';
-- Добавить 100 на счет B
UPDATE accounts SET balance = balance + 100 WHERE user_id = 'B';
COMMIT; -- Зафиксировать изменения
-- В случае ошибки на любом из шагов выполняется ROLLBACK, отменяющий все изменения
Уровни изоляции
Уровни изоляции определяют, насколько одна транзакция "видит" изменения, сделанные в другой параллельной транзакции. В PostgreSQL их четыре (от самого слабого к самому сильному):
- Read Uncommitted: Чтение незафиксированных данных. Возможны "грязные чтения" (dirty reads). В PostgreSQL этот уровень работает как Read Committed.
- Read Committed: Чтение зафиксированных данных. Каждое новое чтение внутри транзакции может вернуть новые данные, если другая транзакция их зафиксировала. Защищает от "грязных чтений". Уровень по умолчанию в PostgreSQL.
- Repeatable Read: Повторяемое чтение. Гарантирует, что при повторном чтении в рамках одной транзакции данные будут теми же. Защищает от "неповторяемых чтений", но возможны "фантомные чтения".
- Serializable: Полная изоляция. Транзакции выполняются так, как будто они идут одна за другой, последовательно. Защищает от всех аномалий, но ценой снижения производительности.
Работа с транзакциями в Go
В Go для работы с транзакциями используется пакет database/sql
.
func transferMoney(db *sql.DB, fromID, toID int, amount int64) error {
// 1. Начинаем транзакцию
tx, err := db.Begin()
if err != nil {
return err
}
// 2. Отложенный откат на случай ошибки
defer tx.Rollback() // Ничего не сделает, если был Commit
// 3. Выполняем операции внутри транзакции
_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE id = ?", amount, fromID)
if err != nil {
return err
}
_, err = tx.Exec("UPDATE accounts SET balance = balance + ? WHERE id = ?", amount, toID)
if err != nil {
return err
}
// 4. Если все успешно, фиксируем транзакцию
return tx.Commit()
}