Вы находитесь на странице: 1из 60

Базы данных. SQL.

Транзакции.
Окулов Антон

R.class
Определение
Транзакция (англ. transaction, от лат. transactio — соглашение, договор) —
минимальная логически осмысленная операция, которая имеет смысл и
может быть совершена только полностью.
Определение
Транзакция (англ. transaction, от лат. transactio — соглашение, договор) —
минимальная логически осмысленная операция, которая имеет смысл и
может быть совершена только полностью.

Транзакция - последовательность операторов SQL, выполняющаяся как


единое целое (всё или ничего), которая не прерывается другими
пользователями базы.
Примеры
- Товар продан, должен создаться заказ с указанием количества и это
же самое количество должно списаться со склада
Примеры
- Товар продан, должен создаться заказ с указанием количества и это
же самое количество должно списаться со склада

- Денежный перевод: деньги должны быть списаны с одного счета и


начислены на второй
Примеры
- Товар продан, должен создаться заказ с указанием количества и это
же самое количество должно списаться со склада

- Денежный перевод: деньги должны быть списаны с одного счета и


начислены на второй

- И т.д.
ACID
- это требования к транзакционной системе (например, к СУБД),
обеспечивающие наиболее надёжную и предсказуемую её работу.
Требования ACID были в основном сформулированы в конце 70-х годов
Джимом Греем.
ACID
- это требования к транзакционной системе (например, к СУБД),
обеспечивающие наиболее надёжную и предсказуемую её работу.
Требования ACID были в основном сформулированы в конце 70-х годов
Джимом Греем.
ACID
- Atomicity — Атомарность

- Consistency — Согласованность

- Isolation — Изолированность

- Durability — Стойкость
ACID
- Atomicity — Атомарность - Атомарность гарантирует, что никакая
транзакция не будет зафиксирована в системе частично. Будут либо
выполнены все её подоперации, либо не выполнено ни одной.
ACID
- Atomicity — Атомарность - Атомарность гарантирует, что никакая
транзакция не будет зафиксирована в системе частично. Будут либо
выполнены все её подоперации, либо не выполнено ни одной.

- Consistency — Согласованность - каждая успешная транзакция по


определению фиксирует только допустимые результаты
ACID
- Atomicity — Атомарность - Атомарность гарантирует, что никакая
транзакция не будет зафиксирована в системе частично. Будут либо
выполнены все её подоперации, либо не выполнено ни одной.

- Consistency — Согласованность - каждая успешная транзакция по


определению фиксирует только допустимые результаты

- Isolation — Изолированность - во время выполнения транзакции


параллельные транзакции не должны оказывать влияния на её
результат
ACID
- Atomicity — Атомарность - Атомарность гарантирует, что никакая
транзакция не будет зафиксирована в системе частично. Будут либо
выполнены все её подоперации, либо не выполнено ни одной.

- Consistency — Согласованность - каждая успешная транзакция по


определению фиксирует только допустимые результаты

- Isolation — Изолированность - во время выполнения транзакции


параллельные транзакции не должны оказывать влияния на её
результат

- Durability — Стойкость - изменения, сделанные успешно завершённой


транзакцией, должны остаться сохранёнными после возвращения
системы в работу
Пример
START TRANSACTION;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

COMMIT;
Пример
START TRANSACTION;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

COMMIT;
Пример
BEGIN;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

COMMIT;
Пример
START TRANSACTION;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

COMMIT;
Пример - откат
START TRANSACTION;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

ROLLBACK;
Пример - savepoint
START TRANSACTION;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

SAVEPOINT before_charging;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

ROLLBACK TO before_charging;
Пример - savepoint
START TRANSACTION;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

SAVEPOINT before_charging;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

ROLLBACK;
Пример - savepoint
START TRANSACTION;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

SAVEPOINT before_charging;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

COMMIT;
Уровни изоляции
- READ UNCOMMITTED - транзакции видят результаты других
незавершенных транзакций.

- READ COMMITTED - параллельно исполняющиеся транзакции видят


только зафиксированные изменения из других транзакций.

- REPEATABLE READ - не видны изменения UPDATE и DELETE, но видны


результаты INSERT. Фантомное чтение. В InnoDB проблема
фантомного чтения решена. В MySQL уровень по-умолчанию.

- SERIALIZABLE - самый высокий и тяжелый уровень изоляции.


Блокирует чтение.
SET TRANSACTION
-- Будет действовать на 1 следующую транзакцию в текущей сессии
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- Будет действовать на все транзакции в текущей сессии


SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- Будет действовать на все транзакции


SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
FOR SHARE, FOR UPDATE
-- Заблокирует выбранные строки для изменения
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR SHARE;

-- Заблокирует выбранные строки для изменения и для


блокирующего чтения
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;
FOR SHARE - пример
START TRANSACTION;
FOR SHARE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance`


WHERE `balance`.`user_id` = 1 FOR SHARE;

`balance`.`sum` = 1
FOR SHARE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR SHARE;

`balance`.`sum` = 1
FOR SHARE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR SHARE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR SHARE;

`balance`.`sum` = 1 `balance`.`sum` = 1
FOR SHARE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR SHARE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR SHARE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;
FOR SHARE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR SHARE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR SHARE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;
UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
COMMIT; WHERE `balance`.`user_id` = 1;
FOR SHARE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR SHARE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR SHARE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;
UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
COMMIT; WHERE `balance`.`user_id` = 1;

COMMIT;
FOR SHARE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR SHARE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR SHARE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;
UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
COMMIT; WHERE `balance`.`user_id` = 1;

COMMIT;

`balance`.`sum` = 2
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance`


WHERE `balance`.`user_id` = 1 FOR UPDATE;
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance`


WHERE `balance`.`user_id` = 1 FOR UPDATE;

`balance`.`sum` = 1
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;
`balance`.`sum` = 1
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;

Ожидание ...
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;
Ожидание ...
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1; Ожидание ...

COMMIT;
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;

COMMIT;

`balance`.`sum` = 2
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;

COMMIT;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;

COMMIT;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;

COMMIT;
FOR UPDATE - пример
START TRANSACTION;

SELECT `balance`.`sum` FROM `balance` START TRANSACTION;


WHERE `balance`.`user_id` = 1 FOR UPDATE;
SELECT `balance`.`sum` FROM `balance`
WHERE `balance`.`user_id` = 1 FOR UPDATE;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;

COMMIT;

UPDATE `balance`
SET `balance`.`sum` = :prev_balance + 1
WHERE `balance`.`user_id` = 1;

COMMIT;
`balance`.`sum` = 3
LOCK TABLES
LOCK TABLES `balance`;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

UNLOCK TABLES;
LOCK TABLES
LOCK TABLES `balance` READ, `users` WRITE;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

UNLOCK TABLES;
LOCK TABLES - опасно и устарело
LOCK TABLES `balance` READ, `users` WRITE;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1;

UPDATE `balance`
SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2;

UNLOCK TABLES;
AUTOCOMMIT
-- Эти изменения сразу запишутся на диск
INSERT INTO `balance` (`sum`, `user_id`) VALUES (100, 1);
AUTOCOMMIT
-- Эти изменения сразу запишутся на диск
INSERT INTO `balance` (`sum`, `user_id`) VALUES (100, 1);

-- Отключаем режим автокоммита


SET AUTOCOMMIT = 0;
AUTOCOMMIT
-- Эти изменения сразу запишутся на диск
INSERT INTO `balance` (`sum`, `user_id`) VALUES (100, 1);

-- Отключаем режим автокоммита


SET AUTOCOMMIT = 0;

-- Эти изменения НЕ запишутся на диск


INSERT INTO `balance` (`sum`, `user_id`) VALUES (50, 2);
AUTOCOMMIT
-- Эти изменения сразу запишутся на диск
INSERT INTO `balance` (`sum`, `user_id`) VALUES (100, 1);

-- Отключаем режим автокоммита


SET AUTOCOMMIT = 0;

-- Эти изменения НЕ запишутся на диск


INSERT INTO `balance` (`sum`, `user_id`) VALUES (50, 2);

-- Запишем изменения
COMMIT;
AUTOCOMMIT
-- Эти изменения сразу запишутся на диск
INSERT INTO `balance` (`sum`, `user_id`) VALUES (100, 1);

-- Отключаем режим автокоммита


SET AUTOCOMMIT = 0;

-- Эти изменения НЕ запишутся на диск


INSERT INTO `balance` (`sum`, `user_id`) VALUES (50, 2);

-- Запишем изменения
COMMIT;

-- Вот так можно включить режим автокоммита по-умолчанию


SET AUTOCOMMIT = 1;
DEADLOCK
DEADLOCK
START TRANSACTION; START TRANSACTION;
DEADLOCK
START TRANSACTION; START TRANSACTION;

UPDATE `balance` UPDATE `balance`


SET `balance`.`sum` = `balance`.`sum` - 100 SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1; WHERE `balance`.`user_id` = 2;
DEADLOCK
START TRANSACTION; START TRANSACTION;

UPDATE `balance` UPDATE `balance`


SET `balance`.`sum` = `balance`.`sum` - 100 SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1; WHERE `balance`.`user_id` = 2;

UPDATE `balance` UPDATE `balance`


SET `balance`.`sum` = `balance`.`sum` + 100 SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2; WHERE `balance`.`user_id` = 1;

COMMIT; COMMIT;
DEADLOCK
START TRANSACTION; START TRANSACTION;

UPDATE `balance` UPDATE `balance`


SET `balance`.`sum` = `balance`.`sum` - 100 SET `balance`.`sum` = `balance`.`sum` - 100
WHERE `balance`.`user_id` = 1; WHERE `balance`.`user_id` = 2;

UPDATE `balance` UPDATE `balance`


SET `balance`.`sum` = `balance`.`sum` + 100 SET `balance`.`sum` = `balance`.`sum` + 100
WHERE `balance`.`user_id` = 2; WHERE `balance`.`user_id` = 1;

COMMIT; COMMIT;

Deadlock found when trying to get lock;


try restarting transaction
Write-Ahead Log или Redo Log
- Данные перед тем как быть записаными на диск в базу пишутся в
некий “журнал изменений” или как его еще называют “журнал
транзакций”
Write-Ahead Log или Redo Log
- Данные перед тем как быть записаными на диск в базу пишутся в
некий “журнал изменений” или как его еще называют “журнал
транзакций”

- Писать журнал изменений быстрее и проще чем менять данные в


базе
Write-Ahead Log или Redo Log
- Данные перед тем как быть записаными на диск в базу пишутся в
некий “журнал изменений” или как его еще называют “журнал
транзакций”

- Писать журнал изменений быстрее и проще чем менять данные в


базе

- При этом при отказе системы - у нас есть все изменения и мы можем
их “проиграть” из WAL
Почитать?
https://ru.wikipedia.org/wiki/ACID

https://habr.com/ru/post/469415/

https://habr.com/ru/post/446662/

https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B0%D0%BD%D0%B7%D0%
B0%D0%BA%D1%86%D0%B8%D1%8F_(%D0%B8%D0%BD%D1%84%D0%BE%D1%
80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0)

https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D0%BE%D1%80%D0%B5%D0%B
C%D0%B0_CAP
Всё :)