Статья
Транзакции

Транзакции

7 января 2021

Транзакции должны удовлетворять требованиям ACID:

1) Atomicity (атомарность). Должны быть выполнены либо все операции, либо ни одной.
2) Consistency (консистентность, согласованность). Каждая транзация должна фиксировать только допустимые результаты (например, в примере с банковским счетом должно быть списание суммы с одного счета и зачисление на другой).
3) Isolation (изолированность). Параллельные транзакции должны быть изолированы друг от друга (на практике есть режимы изоляции, не полностью изолирующие транзакцию).
4) Durability (прочность, долговечность). Если транзакция успешно завершилась, сделанные ей изменения не должны быть отменены из-за сбоя.

 

Уровни изоляции транзакций:


1) Read uncommited. Самая плохая изолированность, самая высокая скорость. Возможно грязное чтение (dirty read).

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

mysql> set session transaction isolation level read uncommitted;

В 1 терминале убедимся, что данных нет

mysql> start transaction;
Query OK, 0 rows affected (0,00 sec)

mysql> select * from test;
Empty set (0,01 sec)

Во 2 терминале стартуем транзакцию и добавим данные, но не зафиксируем

mysql> start transaction;
Query OK, 0 rows affected (0,00 sec)

mysql> insert into test (value) values (100);
Query OK, 1 row affected (0,00 sec)

В 1 терминале мы уже видим новую строку, хотя транзакция не завершена. Это проявление проблемы "грязного чтения" (dirty read)

mysql> select * from test;
+----+-------+
| id | value |
+----+-------+
|  1 |   100 |
+----+-------+
1 row in set (0,01 sec)

Если откатить транзакцию во 2 терминале, в 1 терминале запрос выдаст пустой результат.

2) Read committed. Параллельно выполняющиеся транзакции видят только зафиксированные изменения (решается проблема "грязного чтения"). Возможны проблемы "неповторяемого чтения" (unrepeatable read) и "потерянных изменений" (lost updates).

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

mysql> set session transaction isolation level read committed;

В 1 терминале убедимся, что данных нет

mysql> start transaction;
Query OK, 0 rows affected (0,00 sec)

mysql> select * from test;
Empty set (0,01 sec)

Во 2 терминале стартуем транзакцию и добавим данные, но не зафиксируем

mysql> start transaction;
Query OK, 0 rows affected (0,00 sec)

mysql> insert into test (value) values (100);
Query OK, 1 row affected (0,00 sec)

В 1 терминале повторно убедимся, что данных нет

mysql> select * from test;
Empty set (0,01 sec)

Во 2 терминале фиксируем транзакцию

mysql> commit;
Query OK, 0 rows affected (0,00 sec)

В 1 терминале видим новую строку

mysql> select * from test;
+----+-------+
| id | value |
+----+-------+
|  1 |   100 |
+----+-------+
1 row in set (0,01 sec)

Теперь посмотрим проблему "неповторяемого чтения". В рамках 2 независимых транзакций обновим данные, при этом одна из транзакций при чтении будет получать разные результаты.

mysql> start transaction;
Query OK, 0 rows affected (0,00 sec)

mysql> update test set value=200 where id=1;
Query OK, 1 row affected (0,00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected (0,00 sec)

В 1 терминале мы видим новое значение, хотя транзакция в этом терминале не была зафиксирована

mysql> select * from test;
+----+-------+
| id | value |
+----+-------+
|  1 |   200 |
+----+-------+
1 row in set (0,01 sec)


3) Repeatable read. Транзакция не видит изменения данных, которые были ей ранее прочитаны. При этом другая транзакция не может изменить данные, которые читаются текущей транзакцией, до момента ее фиксации. Таким образом решаются проблемы "неповторяемого чтения" (unrepeatable read) и "потерянных данных" (lost updates).

На данном уровне изоляции возможна проблема "фантомного чтения" (phantom read) - первая транзакция выбирает данные по условию (например сумма баланса) несколько раз, в промежутках между запросами вторая транзакция добавляет строки (или изменяет данные некоторых строк).


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

 

mysql> set session transaction isolation level repeatable read;

В 1 терминале в рамках транзакции найдем сумму

mysql> start transaction;
Query OK, 0 rows affected (0,00 sec)

mysql> select sum(value) from test;
+------------+
| sum(value) |
+------------+
|        100 |
+------------+
1 row in set (0,00 sec)

Во 2 терминале добавим данные

mysql> start transaction;
Query OK, 0 rows affected (0,00 sec)

mysql> insert into test (value) values (200);
Query OK, 1 row affected (0,00 sec)

mysql> commit;
Query OK, 0 rows affected (0,00 sec)

В 1 терминале делаем повторный запрос

mysql> select sum(value) from test;
+------------+
| sum(value) |
+------------+
|        300 |
+------------+
1 row in set (0,00 sec)


4) Serializable. Самый высокий уровень изоляции. Транзакции полностью изолируются друг от друга. Нет эффекта "фантомного чтения".

 

 

Источники:
https://en.wikipedia.org/wiki/Isolation_(database_systems)