Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
9
Добавлен:
15.01.2021
Размер:
14.96 Mб
Скачать

Пример 43: управление уровнем изолированности транзакций

 

:.1.a

 

1

COMMIT;

 

2

SELECT SYS_CONTEXT('userenv', 'sessionid')

3

FROM DUAL;

 

4

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

5

SELECT "sb finish"

 

6

FROM "subscriptions" ORDER BY

"sb_finish" ASC;

7— EXEC DBMS_LOCK.SLEEP(10);

8COMMIT;

Раскомментировав строку с EXEC DBMS_LOCK.SLEEP(10) в соответствую-

щем блоке кода, мы проэмулируем его долгое выполнение, что позволит нам не спеша несколько раз выполнить второй блок (в котором эта строка останется закомментированной) и посмотреть на результат.

На этом решение данной задачи завершено.

•Ч Решение 6.2.1. b 4 .

Решение данной задачи подчиняется общей логике разделения уровней изолированности транзакций:

чем уровень ниже, тем больше у СУБД возможностей выполнить запрос параллельно с другими, но тем выше вероятность получить некорректный результат;

чум уровень выше, тем меньше у СУБД возможностей выполнить запрос параллельно с другими, но тем ниже вероятность получить некорректный результат;

в MySQL и MS SQL Server самым низким уровнем является READ UNCOM-

MITTED, в Oracle — READ COMMITTED;

• во всех трёх СУБД самым высоким уровнем является SERIALIZABLE.

Учитывая эти факты, нам остаётся только написать код для выполнения одного и того же запроса на самом низком и самом высоком уровнях изолированности транзакций, а также подготовить проверочный код, который позволит увидеть разницу в работе этих двух вариантов выполнения основного кода.

Код для MySQL выглядит следующим образом.

MySQL I

Решение 6.2.1.b (максимально быстрое выполнение, возможны некорректные данные)

|

1

SELECT CONNECTION_ID();

 

 

2

SET autocommit = 0;

 

 

3

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

 

4

START

TRANSACTION;

 

 

5

SELECT 'sb subscriber',

 

 

6

 

 

COUNT('sb book')

AS 'sb has books'

 

7

FROM

 

'subscriptions'

 

 

8

WHERE

'sb is active' =

'Y'

 

9

GROUP

BY 'sb_subscriber ;

 

10

COMMIT ;

 

 

 

 

 

 

 

 

 

 

MySQL I

 

Решение 6.2.1.b (максимально корректные данные, возможно долгое выполнение)

|

1

SELECT CONNECTION_ID();

 

 

2

SET autocommit = 0;

 

 

3

SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

 

4

START

TRANSACTION;

 

 

5

SELECT 'sb subscriber',

 

 

6

 

 

COUNT('sb book')

AS 'sb has books'

 

7

FROM

 

'subscriptions'

 

 

8

WHERE

'sb is active' =

'Y'

 

9

GROUP

BY 'sb_subscriber ;

 

10

COMMIT ;

 

 

 

 

 

 

 

 

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 460/545

Пример 43: управление уровнем изолированности транзакций

MySQL I

Решение 6.2.1.b (проверочный код)

|

1

SELECT CONNECTION_ID();

 

2

SET autocommit = 0;

 

3

START TRANSACTION;

 

4

UPDATE 'subscriptions'

 

5

SET

'sb_is_active' =

 

6

 

CASE

 

 

 

 

WHEN

'sb_is_active' =

'Y' THEN 'N'

8

 

WHEN

'sb_is_active' =

'N' THEN 'Y'

9

 

END;

 

 

10

SELECT SLEEP(10 ;

 

11

COMMIT;

 

 

 

 

 

Код для MS SQL Server выглядит следующим образом.

 

 

MS SQL I

Решение 6.2.1.b (максимально быстрое выполнение, возможны некорректные данные) |

1SELECT @@SPID;

2SET IMPLICIT TRANSACTIONS ON;

3SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

4BEGIN TRANSACTION;

5SELECT [sb subscriber],

6COUNT([sb book]) AS [sb has books]

7 FROM [subscriptions]

8WHERE [sb is active] = 'Y'

9GROUP BY [sb subscriber];

10COMMIT TRANSACTION;

MS SQL I

Решение 6.2.1.b (максимально корректные данные, возможно долгое выполнение)

і

1SELECT @@SPID;

2SET IMPLICIT TRANSACTIONS ON;

3SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

4BEGIN TRANSACTION;

5SELECT [sb subscriber],

6COUNT([sb book] AS [sb has books]

7

FROM

[subscriptions]

8

WHERE

[sb is active] = 'Y'

9GROUP BY [sb subscriber];

10COMMIT TRANSACTION;

MS SQL I

Решение 6.2.1.b (проверочный код) |

1SELECT @@SPID;

2SET IMPLICIT_TRANSACTIONS ON;

3BEGIN TRANSACTION;

4UPDATE [subscriptions]

5

SET

[sb is active] =

 

 

6

 

CASE

 

 

7

 

WHEN [sb is active] = 'Y

THEN

'N'

8

 

WHEN [sb is active] = 'N

THEN

'Y'

9END;

10WAITFOR DELAY '00:00:10';

11COMMIT TRANSACTION;

Код для Oracle выглядит следующим образом.

Oracle Решение 6.2.1.b (максимально быстрое выполнение, возможны некорректные данные)

1 COMMIT;

2SELECT SYS_CONTEXT('userenv','sessionid') FROM DUAL;

3SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

4SELECT "sb_subscriber",

5COUNT "sb_book") AS "sb_has_books"

6

FROM

"subscriptions"

7

WHERE

"sb is active" = 'Y'

 

GROUP BY

"sb_subscriber";

9

COMMIT;

 

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 461/545

Пример 43: управление уровнем изолированности транзакций

Oracle

 

Решение 6.2.1.b (максимально корректные данные, возможно долгое выполнение)

 

1

COMMIT;

 

 

 

 

2

SELECT SYS_CONTEXT('userenv','sessionid') FROM DUAL

 

3

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

 

4

SELECT "sb_subscriber",

 

 

 

5

 

 

 

COUNT("sb_book") AS "sb_has_books"

 

6

FROM

"subscriptions"

 

 

 

 

WHERE

"sb_is_active" = 'Y'

 

 

 

8

GROUP BY "sb_subscriber"

 

 

 

9

COMMIT;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Oracle

 

Решение 6.2.1.b (проверочный код)

 

 

 

1

COMMIT;

 

 

 

 

2

SELECT SYS_CONTEXT('userenv','sessionid') FROM DUAL

 

 

UPDATE

"subscriptions"

 

 

 

4

SET

 

"sb_is_active" =

 

 

 

5

 

 

 

CASE

 

 

 

6

 

 

 

WHEN "sb_is_active" =

'Y' THEN 'N'

 

 

 

 

 

WHEN "sb_is_active" =

 

'N' THEN 'Y'

 

8

 

 

END;

 

 

 

9

EXEC DBMS_LOCK.SLEEP(10 ;

 

 

 

10

COMMIT;

 

 

 

 

 

 

 

 

 

 

 

 

Для всех трёх СУБД проверочный код необходимо выполнять в отдельной сессии (см. пояснения в решении{428} задачи 6.2.1.a{428}), при этом основной код надо выполнять до начала работы проверочного, во время его работы и после его завершения — это позволит наглядно увидеть, какие данные и в какой момент времени СУБД будет извлекать из базы данных.

Ещё один вариант поведения СУБД можно увидеть, заменив в проверочном коде последнюю команду с COMMIT на ROLLBACK.

Обратите особое внимание на отличие поведения Oracle от MySQL и MS SQL Server: даже в SERIALIZABLE-режиме запрос вернёт результаты без задержки.

На этом решение данной задачи завершено.

Задание 6.2.1.TSK.A: написать запросы, которые, будучи выполненными параллельно, обеспечивали бы следующий эффект:

первый запрос должен считать количество выданных на руки и возвращённых в библиотеку книг и не зависеть от запросов на обновление таблицы subscriptions (не ждать их завершения);

второй запрос должен инвертировать значения поля sb_is_active таблицы subscriptions с Y на N и наоборот и не зависеть от первого запроса (не ждать его завершения).

Задание 6.2.1.TSK.B: написать запросы, которые, будучи выполненными параллельно, обеспечивали бы следующий эффект:

первый запрос должен считать количество выданных на руки и возвращённых в библиотеку книг;

второй запрос должен инвертировать значения поля sb_is_active таблицы subscriptions с Y на N и наоборот для читателей с нечётными идентификаторами, после чего делать паузу в десять секунд и отменять

данное изменение (отменять транзакцию).

Исследовать поведение все трёх СУБД при выполнении первого запроса до, во время и после завершения выполнения второго запроса, повторив этот эксперимент для всех поддерживаемых конкретной СУБД уровней изолированности транзакций.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 462/545

Пример 44: взаимодействие конкурирующих транзакций

6.2.2.Пример 44: взаимодействие конкурирующих транзакций

ОЗадача 6.2.2.a{434}: продемонстрировать во всех трёх СУБД все аномалии конкурентного доступа для всех возможных комбинаций уровней изоли-

рованности транзакций.

О Задача 6.2.2.b{462}: продемонстрировать во всех трёх СУБД ситуацию гарантированного получения взаимной блокировки транзакций и реакцию СУБД

на такую ситуацию.

Ожидаемый результат 6.2.2.a.

Поскольку решение данной задачи и является ожидаемым результатом, см.

решение{434} 6.2.2.a.

Ожидаемый результат 6.2.2.b.

Поскольку решение данной задачи и является ожидаемым результатом, см.

решение{462} 6.2.2.b.

уЦ7

ЧР Решение 6.2.2.a{434}.

К аномалиям конкурентного доступа относятся:

грязное чтение (dirty read) — чтение промежуточного состояния данных до того, как модифицирующая их транзакция будет подтверждена или отменена;

потерянное обновление (lost update) — модификация одной и той же информации двумя и более транзакциями, при которой в силу вступают изменения, выполненные транзакцией, которая была подтверждена последней (а изменения, выполненные остальными транзакциями, теряются);

неповторяющееся чтение (non-repeatable read) — получение различных результатов выполнения одного и того же запроса на чтение в рамках одной транзакции;

фантомное чтение (phantom read) — временное появление (исчезновение) в наборе данных, с которым работает транзакция, тех или иных записей в силу их изменения другой транзакцией.

Для удобства навигации приведём таблицу, показывающую номера страниц, с которых начинается рассмотрение той или иной аномалии в каждой СУБД.

 

 

Потерянное

Неповторяю-

Фантомное

 

Грязное чтение

обновление

щееся чтение

чтение

MySQL

{435}

{437}

{440}

{442}

MS SQL Server

{445}

{447}

{450}

{452}

Oracle

{455}

{457}

{459}

{461}

Также отметим, что поскольку протоколы исследований будут выглядеть однотипно во всех СУБД, для экономии места мы ниже приведём их только для MySQL, причём в рамках исследования каждой аномалии конкурентного доступа для первой транзакции покажем только один уровень изолированности, а для второй — все поддерживаемые данной СУБД уровни изолированности.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 463/545

Пример 44: взаимодействие конкурирующих транзакций

Традиционно начинаем с MySQL. Данная СУБД поддерживает четыре уровня изолированности транзакций, комбинации которых мы и рассмотрим:

READ UNCOMMITTED;

READ COMMITTED;

REPEATABLE READ;

SERIALIZABLE.

Для выполнения эксперимента используем командный файл:

start cmd.exe /с "mysql -иПОЛЬЗЬВАТЕЛЬ ПАРОЛЬ БАЗА_ДАННЫХ < a.sql & pause" start cmd.exe /c "mysql иПОЛЬЗЬВАТЕЛЬ ^ПАРОЛЬ БАЗА ДАННЫХ < b.sql & pause"

Г рязное чтение в MySQL может быть исследовано выполнением в двух отдельных сессиях следующих блоков кода:

MySQL

Решение 6.2.2.a (код для исследования аномалии грязного чтения)

 

1

-- Транзакция A:

-- Транзакция B:

2

SELECT CONCAT('Tr A ID = ',

SELECT CONCAT('Tr B ID = ',

3

 

CONNECTION_ID());

 

CONNECTION_ID());

4

SET autocommit = 0;

SET autocommit = 0;

5

SET SESSION TRANSACTION

SET SESSION TRANSACTION

 

ISOLATION LEVEL {УРОВЕНЬ};

ISOLATION LEVEL {УРОВЕНЬ};

 

 

 

 

START TRANSACTION;

START TRANSACTION;

8

SELECT CONCAT('Tr A START: ',

SELECT CONCAT('Tr B START: ',

9

 

CURTIME(), ' in ');

 

CURTIME(), ' in ');

10

SELECT

'VARIABLE_VALUE'

SELECT 'VARIABLE_VALUE'

11

FROM

'information_schema'

FROM

'information_schema'

12

 

'session_variables'

 

'session_variables'

13

WHERE

'VARIABLE_NAME' =

WHERE

'VARIABLE_NAME' =

14

 

'tx isolation';

 

'tx isolation';

15

 

 

SELECT CONCAT('Tr B SELECT 1: ',

16

 

 

 

CURTIME());

17

SELECT SLEEP(5);

SELECT 'sb_is_active'

18

 

 

FROM

'subscriptions'

19

 

 

WHERE

'sb id' = 2;

 

SELECT CONCAT('Tr A UPDATE: ',

 

 

11

 

CURTIME());

 

 

12

UPDATE

'subscriptions'

 

 

13

SET

'sb_is_active' =

 

 

14

CASE

 

SELECT SLEEP (10);

 

WHEN

'sb_is_active' = 'Y' THEN 'N'

 

 

16

WHEN

'sb_is_active' = 'N' THEN 'Y'

 

 

17

END

 

 

 

 

WHERE

'sb id' = 2;

 

 

19

 

 

SELECT CONCAT('Tr B SELECT 2: ',

20

 

 

 

CURTIME());

21

SELECT SLEEP(20 ;

SELECT 'sb_is_active'

22

 

 

FROM

'subscriptions'

23

 

 

WHERE

'sb id' = 2;

24

 

 

SELECT CONCAT('Tr B COMMIT: ',

25

 

 

 

CURTIME());

26

 

 

COMMIT;

 

 

 

 

 

 

SELECT CONCAT('Tr A ROLLBACK: ',

 

 

28

 

CURTIME());

 

 

29

ROLLBACK;

 

 

Приведём пример журнала выполнения этого кода для ситуации, когда транзакция A выполняется на уровне изолированности READ UNCOMMITTED и конкурирует с транзакцией B, последовательно выполняемой во всех поддерживаемых MySQL уровнях изолированности.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 464/545

Пример 44: взаимодействие конкурирующих транзакций

 

A: READ UNCOMMITTED

B: READ UNCOMMITTED

Tr

A ID = 13

Tr B ID = 14

Tr

A START: 17:21:01 in READ-UNCOMMITTED

Tr B START: 17:21:01 in READ-UNCOMMITTED

Tr

A UPDATE: 17:21:06

Tr B SELECT 1: 17:21:01

Tr

A ROLLBACK: 17:21:26

sb is active = Y

 

 

Tr B SELECT 2: 17:21:11

 

 

sb_is_active = N

 

 

Tr B COMMIT: 17:21:11

 

A: READ UNCOMMITTED

B: READ COMMITTED

Tr

A ID = 15

Tr B ID = 16

Tr

A START: 17:38:06 in READ-UNCOMMITTED

Tr B START: 17:38:06 in READ-COMMITTED

Tr

A UPDATE: 17:38:12

Tr B SELECT 1: 17:38:06

Tr

A ROLLBACK: 17:38:32

sb is active = Y

 

 

Tr B SELECT 2: 17:38:16

 

 

sb_is_active = Y

 

 

Tr B COMMIT: 17:38:16

 

 

 

 

A: READ UNCOMMITTED

B: REPEATABLE READ

Tr

A ID = 18

Tr B ID = 17

Tr

A START: 17:42:37 in READ-UNCOMMITTED

Tr B START: 17:42:37 in REPEATABLE-READ

Tr

A UPDATE: 17:42:42

Tr B SELECT 1: 17:42:37

Tr

A ROLLBACK: 17:43:02

sb is active = Y

 

 

Tr B SELECT 2: 17:42:47

 

 

sb_is_active = Y

 

 

Tr B COMMIT: 17:42:47

 

 

 

 

 

A: READ UNCOMMITTED

 

 

 

 

B: SERIALIZABLE

Tr

A ID = 20

 

 

 

Tr B ID =

19

 

Tr

A START: 17:48:19 in READ-UNCOMMITTED

 

Tr B START: 17:48:19 in SERIALIZABLE

Tr

A UPDATE: 17:48:24

 

 

 

Tr B SELECT 1: 17:48:19

 

Tr

A ROLLBACK: 17:48:49

 

 

sb is active = Y

 

 

 

 

 

 

 

Tr B SELECT 2: 17:48:29

 

 

 

 

 

 

 

sb_is_active = Y

 

 

 

 

 

 

 

Tr B COMMIT: 17:48:29

 

 

 

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

 

 

 

 

 

 

 

 

 

 

 

 

Уровень изолированности транзакции B

 

 

 

READ

READ COMMITTED

 

REPEATABLE

SERIALIZABLE

 

 

 

UNCOMMITTED

 

READ

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Транзакция B оба раза

 

 

 

Транзакция B

Транзакция B оба

 

Транзакция B оба

читает исходное

 

 

READ

успевает прочитать

 

раза читает

 

раза читает

(корректное) значение,

 

 

UNCOMMITTED

незафикси-

исходное (кор-

 

исходное (кор-

UPDATE в транзакции A

 

 

 

рованное значение

ректное) значение

 

ректное) значение

ждёт завершения

 

A

 

 

 

 

 

 

 

транзакции B

 

транзакции

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Транзакция B оба раза

 

 

 

 

 

 

 

 

 

 

 

 

Транзакция B

Транзакция B оба

 

Транзакция B оба

читает исходное

 

 

READ

успевает прочитать

 

раза читает

 

раза читает

(корректное) значение,

 

изолированности

COMMITTED

незафикси-

исходное (кор-

 

исходное (кор-

UPDATE в транзакции A

 

READ

незафикси-

исходное (кор-

 

исходное (кор-

UPDATE в транзакции A

 

 

 

рованное значение

ректное) значение

 

ректное) значение

ждёт завершения

 

 

 

 

 

 

 

 

 

транзакции B

 

 

 

 

 

 

 

 

 

Транзакция B оба раза

 

 

 

Транзакция B

Транзакция B оба

 

Транзакция B оба

читает исходное

 

 

REPEATABLE

успевает прочитать

 

раза читает

 

раза читает

(корректное) значение,

 

Уровень

 

рованное значение

ректное) значение

 

ректное) значение

ждёт завершения

 

 

 

 

 

 

 

 

Транзакция B оба раза

 

 

 

 

 

 

 

 

 

транзакции B

 

 

 

Транзакция B

Транзакция B оба

 

Транзакция B оба

читает исходное

 

 

SERIALIZABLE

успевает прочитать

 

раза читает

 

раза читает

(корректное) значение,

 

 

незафикси-

исходное (кор-

 

исходное (кор-

UPDATE в транзакции A

 

 

 

 

 

 

 

рованное значение

ректное) значение

 

ректное) значение

ждёт завершения

 

 

 

 

 

 

 

 

 

транзакции B

 

 

 

 

 

 

 

 

 

 

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 465/545

Пример 44: взаимодействие конкурирующих транзакций

Потерянное обновление MySQL может быть исследовано выполнением в двух отдельных сессиях следующих блоков кода:

MySQL I Решение 6.2.2.a (код для исследования аномалии потерянного обновления) |

1

-- Транзакция A:

 

 

-- Транзакция B:

2

SELECT CONCAT('Tr A ID =

',

 

SELECT CONCAT('Tr B ID = ',

3

 

CONNECTION_ID());

 

 

CONNECTION_ID());

4

SET autocommit = 0;

 

 

SET autocommit = 0;

5

SET SESSION TRANSACTION

 

 

SET SESSION TRANSACTION

 

ISOLATION LEVEL {УРОВЕНЬ};

 

ISOLATION LEVEL {УРОВЕНЬ};

 

START TRANSACTION;

 

 

START TRANSACTION;

8

SELECT CONCAT('Tr A START: ',

 

SELECT CONCAT('Tr B START: ',

9

 

CURTIME(), ' in ');

 

CURTIME(), ' in ');

10

SELECT

'VARIABLE_VALUE'

 

 

SELECT 'VARIABLE_VALUE'

11

FROM

'information_schema'

 

FROM

'information_schema'

12

 

'session_variables'

 

 

'session_variables'

13

WHERE

'VARIABLE_NAME' =

 

 

WHERE

'VARIABLE_NAME' =

14

 

'tx isolation';

 

 

 

'tx isolation';

 

SELECT CONCAT('Tr A, SELECT: ',

 

 

 

 

16

 

CURTIME () ) ;

 

 

 

 

17

SELECT

'sb_is_active'

 

 

SELECT SLEEP(5 ;

18

FROM

'subscriptions'

 

 

 

 

 

 

WHERE

'sb id' = 2;

 

 

 

 

 

20

 

 

 

 

SELECT CONCAT('Tr B, SELECT: ',

21

 

 

 

 

 

CURTIME());

22

SELECT SLEEP(10 ;

 

 

SELECT 'sb_is_active'

23

 

 

 

 

FROM

'subscriptions'

24

 

 

 

 

WHERE

'sb id' = 2;

 

 

 

 

 

 

 

SELECT CONCAT('Tr A UPDATE: ',

 

 

 

 

26

 

CURTIME () ) ;

 

 

 

 

27

UPDATE

'subscriptions'

 

 

 

 

 

28

SET

'sb_is_active'

=

'Y'

SELECT SLEEPi10>;

29

WHERE

'sb_id' = 2;

 

 

 

 

 

 

 

30

SELECT CONCAT('Tr A COMMIT: ',

 

 

 

 

31

 

CURTIME());

 

 

 

 

 

 

COMMIT;

 

 

 

 

 

33

 

 

 

 

SELECT CONCAT('Tr B UPDATE: ',

34

 

 

 

 

 

CURTIME());

35

 

 

 

 

UPDATE 'subscriptions'

36

SELECT SLEEP(10 ;

 

 

SET

'sb_is_active' = 'N'

37

 

 

 

 

WHERE

'sb_id' = 2;

38

 

 

 

 

SELECT CONCAT('Tr B COMMIT: ' ,

39

 

 

 

 

 

CURTIME());

40

 

 

 

 

COMMIT;

 

 

 

 

 

 

 

SELECT CONCAT('After A, SELECT: ',

SELECT CONCAT('After B, SELECT: ',

 

42

 

CURTIME());

 

 

 

CURTIME());

 

43

SELECT

'sb_is_active'

 

 

SELECT 'sb_is_active'

 

44

FROM

'subscriptions'

 

 

FROM

'subscriptions'

 

45

WHERE

'sb id' = 2;

 

 

WHERE

'sb id' = 2;

 

Приведём пример журнала выполнения этого кода для ситуации, когда транзакция A выполняется на уровне изолированности READ COMMITTED и конкурирует с

транзакцией B, последовательно выполняемой во всех поддерживаемых MySQL уровнях изолированности.

A: READ COMMITTED

B: READ UNCOMMITTED

Tr A ID = 77

Tr B ID = 76

Tr A START:19:12:19 in READ-COMMITTED

Tr B START: 19:12:19 in READ-UNCOMMITTED

Tr A, SELECT: 19:12:19

Tr B, SELECT: 19:12:24

sb is active = N

sb is active = N

Tr A UPDATE: 19:12:29

Tr B UPDATE: 19:12:34

Tr A COMMIT: 19:12:29

Tr B COMMIT: 19:12:34

After A, SELECT: 19:12:39

After B, SELECT: 19:12:34

sb is active = N

sb_is_active = N

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 466/545

Пример 44: взаимодействие конкурирующих транзакций

A: READ COMMITTED

 

B: READ COMMITTED

Tr A ID = 80

 

Tr B ID = 79

Tr A START:19:14:43 in READ-COMMITTED

Tr B START: 19:14:43 in READ-COMMITTED

Tr A, SELECT: 19:14:43

 

Tr B, SELECT: 19:14:48

sb is active = N

 

sb is active = N

' UPDATE: 19:14:53

 

B UPDATE: 19:14:58

Tr A COMMIT: 19:14:53

 

Tr B COMMIT: 19:14:58

After A, SELECT: 19:15:03 sb

is active

After B, SELECT: 19:14:58

= N

 

sb_is_active = N

A: READ COMMITTED

B: REPEATABLE READ

Tr A ID = 83

Tr B ID =

82

Tr ASTART: 19:17:00 in READ-COMMITTED

Tr BSTART: 19:17:00 in REPEATABLE-READ

Tr A, SELECT:19:17:00

Tr B, SELECT: 19:17:05

sb is active = N

sb is active =

N

Tr A UPDATE: 19:17:10

Tr B UPDATE: 19:17:15

Tr A COMMIT: 19:17:10

Tr B COMMIT: 19:17:15

After A, SELECT: 19:17:20

After B, SELECT: 19:17:15

sb is active = N

sb is active = N

 

 

 

 

A: READ COMMITTED

 

 

 

B: SERIALIZABLE

Tr A ID = 86

 

 

 

Tr B ID =

85

 

 

Tr ASTART: 19:19:07

in READ-COMMITTED

 

Tr BSTART: 19:19:06 in SERIALIZABLE

Tr A, SELECT:19:19:07

 

 

 

Tr B, SELECT: 19:19:11

 

sb is active = N

 

 

 

sb is active = N

 

Tr A UPDATE: 19:19:17

 

 

 

Tr B UPDATE: 19:19:21

 

Tr A COMMIT: 19:19:17

 

 

 

Tr B COMMIT: 19:19:21

 

After A, SELECT: 19:19:27

 

After B, SELECT: 19:19:21

 

sb is active = N

 

 

 

sb is active = N

 

 

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

 

 

 

 

 

 

 

 

 

 

 

 

Уровень изолированности транзакции B

 

 

READ

 

 

 

 

REPEATABLE

 

SERIALIZABLE

 

 

UNCOMMITTED

READ COMMITTED

 

READ

 

 

 

 

 

 

A

 

Обновление

 

Обновление

 

Обновление

 

 

транзакции

READ

 

 

 

Обновление транзакции

транзакции A

 

транзакции A

 

транзакции A

 

UNCOMMITTED

 

 

 

A утеряно

 

 

 

 

 

утеряно

 

утеряно

 

утеряно

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

изолированности

READ

Обновление

 

Обновление

 

Обновление

 

Обновление транзакции

утеряно

 

утеряно

 

утеряно

 

 

COMMITTED

транзакции A

 

транзакции A

 

транзакции A

 

A утеряно

 

утеряно

 

утеряно

 

утеряно

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

REPEATABLE

Обновление

 

Обновление

 

Обновление

 

Обновление транзакции

 

транзакции A

 

транзакции A

 

транзакции A

 

 

READ

 

 

 

A утеряно

 

 

 

 

 

 

 

 

Уровень

 

 

 

 

 

 

 

 

 

 

Обновление

 

Обновление

 

Обновление

 

Возможна взаимная

 

 

 

 

 

 

SERIALIZABLE

транзакции A

 

транзакции A

 

транзакции A

 

блокировка с отменой

 

 

утеряно

 

утеряно

 

утеряно

 

транзакции B

 

 

 

 

 

 

 

 

 

 

Если в данном эксперименте убрать чтение информации перед её обновлением (строки 15-19 для транзакции A, и строки 20-24 для транзакции B), то при любой комбинации уровней изолированности результат будет одним и тем же: изменения, выполненные транзакцией A, будут утеряны.

Чтобы получить другой вариант поведения СУБД, необходимо явно блокиро-

вать читаемые записи (SELECT ... LOCK IN SHARE MODE или SELECT ... FOR UPDATE)36 в первой операции чтения. В данном случае это не было сделано, чтобы

продемонстрировать наиболее типичное поведение MySQL. Но если добавить указанные блокировки, поведение MySQL изменится и примет следующий вид.

36 http://dev.mysql.com/doc/refman/5.6/en/innodb-locking-reads.html

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 467/545

Пример 44: взаимодействие конкурирующих транзакций

Итоговые результаты взаимодействия транзакций при использовании LOCK IN SHARE MODE для первой операции чтения. Важно отметить, что в некоторых случаях

взаимная блокировка нарушает работу обеих транзакций, но в большинстве случаев СУБД отменяет транзакцию B, позволяя транзакции A успешно выполниться.

 

 

 

Уровень изолированности транзакции B

 

 

 

READ

 

REPEATABLE

SERIALIZABLE

 

 

UNCOMMITTED

READ COMMITTED

READ

 

 

 

A

 

 

 

 

 

транзакции

READ

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

UNCOMMITTED

ровка транзакций

ровка транзакций

ровка транзакций

ровка транзакций

 

 

 

 

 

 

 

изолированности

READ

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

COMMITTED

ровка транзакций

ровка транзакций

ровка транзакций

ровка транзакций

 

 

 

 

 

 

 

 

REPEATABLE

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

 

READ

ровка транзакций

ровка транзакций

ровка транзакций

ровка транзакций

Уровень

 

 

 

 

 

SERIALIZABLE

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

Взаимная блоки-

 

 

ровка транзакций

ровка транзакций

ровка транзакций

ровка транзакций

 

 

 

 

 

 

 

 

Итоговые результаты взаимодействия транзакций при использовании FOR UPDATE для первой операции чтения.

 

 

 

Уровень изолированности транзакции B

 

 

 

READ

READ COMMITTED

REPEATABLE

SERIALIZABLE

 

 

UNCOMMITTED

READ

 

 

 

 

 

 

 

 

 

 

 

 

Обновление тран-

Обновление тран-

Обновление тран-

Обновление тран-

A

READ

закции A утеряно,

закции A утеряно,

закции A утеряно,

закции A утеряно,

транзакции

UNCOMMITTED

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

 

 

 

завершения A

завершения A

завершения A

завершения A

 

 

 

 

 

 

 

 

Обновление тран-

Обновление тран-

Обновление тран-

Обновление тран-

изолированности

READ

закции A утеряно,

закции A утеряно,

закции A утеряно,

закции A утеряно,

COMMITTED

завершения A

завершения A

завершения A

завершения A

 

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

 

 

завершения A

завершения A

завершения A

завершения A

 

 

Обновление тран-

Обновление тран-

Обновление тран-

Обновление тран-

 

REPEATABLE

закции A утеряно,

закции A утеряно,

закции A утеряно,

закции A утеряно,

 

READ

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

Уровень

 

 

 

 

 

 

Обновление тран-

Обновление тран-

Обновление тран-

Обновление тран-

 

 

 

SERIALIZABLE

закции A утеряно,

закции A утеряно,

закции A утеряно,

закции A утеряно,

 

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

транзакция B ждёт

 

 

 

 

завершения A

завершения A

завершения A

завершения A

 

 

 

 

 

 

Очевидно, что использованием различных комбинаций способов выполнения (или вовсе невыполнение) первой операции чтения в начале каждой транзакции можно получить ещё больше вариантов поведения СУБД.

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 468/545

Пример 44: взаимодействие конкурирующих транзакций

Неповторяющееся чтение в MySQL может быть исследовано выполнением в двух отдельных сессиях следующих блоков кода:

MySQL I Решение 6.2.2.a (код для исследования аномалии неповторяющегося чтения) |

1

-- Транзакция A:

 

-- Транзакция B:

2

SELECT CONCAT('Tr A ID =

',

SELECT CONCAT('Tr B ID = ',

3

 

CONNECTION ID()) ;

 

CONNECTION ID());

4

SET autocommit = 0;

 

SET autocommit = 0;

5

SET SESSION TRANSACTION

 

SET SESSION TRANSACTION

 

ISOLATION LEVEL {УРОВЕНЬ};

ISOLATION LEVEL {УРОВЕНЬ};

 

 

 

 

 

 

START TRANSACTION;

 

START TRANSACTION;

7

SELECT CONCAT('Tr A START: ',

SELECT CONCAT('Tr B START: ',

8

 

CURTIME(), ' in ');

 

CURTIME(), ' in ');

9

SELECT 'VARIABLE VALUE'

 

SELECT 'VARIABLE VALUE'

10

FROM

'information schema'

FROM

'information schema'

11

 

'session variables'

 

'session variables'

12

WHERE

'VARIABLE NAME' =

 

WHERE

'VARIABLE NAME' =

13

 

'tx isolation';

 

 

'tx isolation';

14

 

 

 

SELECT CONCAT('Tr A SELECT-1: ',

15

 

 

 

 

CURTIME());

16

SELECT SLEEP(5);

 

 

 

17

 

 

 

SELECT 'sb is active'

18

 

 

 

FROM

'subscriptions'

19

 

 

 

WHERE

'sb id' = 2;

 

SELECT CONCAT('Tr A UPDATE: ',

 

 

21

 

CURTIME());

 

 

22

UPDATE 'subscriptions'

 

 

 

23

SET

'sb is active' =

 

 

 

24

CASE

 

 

SELECT SLEEPi10>;

25

WHEN 'sb is active' =

'Y' THEN 'N'

 

 

26

WHEN 'sb is active' =

'N' THEN 'Y'

 

 

 

 

 

 

 

 

27

END

 

 

 

 

28

WHERE

'sb id' = 2;

 

 

 

29

SELECT CONCAT('Tr A COMMIT: ',

 

 

30

 

CURTIME());

 

 

 

COMMIT;

 

 

 

 

 

 

 

 

32

 

 

 

SELECT CONCAT('Tr A SELECT-2: ',

33

 

 

 

 

CURTIME());

34

 

 

 

 

 

35

 

 

 

SELECT 'sb is active'

36

 

 

 

FROM

'subscriptions'

37

 

 

 

WHERE

'sb id' = 2;

38

 

 

 

SELECT CONCAT('Tr B COMMIT: ',

39

 

 

 

 

CURTIME());

40

 

 

 

COMMIT;

 

 

 

 

 

 

 

Приведём пример журнала выполнения этого кода для ситуации, когда транзакция A выполняется на уровне изолированности REPEATABLE READ и конкурирует

с транзакцией B, последовательно выполняемой во всех поддерживаемых MySQL уровнях изолированности.

 

A: REPEATABLE READ

B: READ UNCOMMITTED

Tr

A ID = 151

Tr B ID = 152

Tr

A START:20:24:12 in REPEATABLE-READ

Tr B START: 20:24:12 in READ-UNCOMMITTED

Tr

A UPDATE:20:24:17

Tr B SELECT-1: 20:24:12

Tr

A COMMIT:20:24:17

sb_is_active = N

 

 

Tr B SELECT-2: 20:24:22

 

 

sb_is_active = Y

 

 

Tr B COMMIT: 20:24:22

 

 

 

Работа с MySQL, MS SQL Server и Oracle в примерах © EPAM Systems, RD Dep, 2016-2018 Стр: 469/545