Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Основное / Письменные лекции по дисциплине «Базы данных»

.pdf
Скачиваний:
98
Добавлен:
29.01.2021
Размер:
939.56 Кб
Скачать

Вы можете установить или отключить режим AUTOCOMMIT для

текущего соединения, задав значение переменной:

mysql> SHOW VARIABLES LIKE 'autocommit';

+

---------------

+-------

+

| Variable_name | Value

|

+---------------

 

+-------

+

| autocommit

| ON

|

+---------------

 

+-------

+

1

row in set (0.00 sec)

 

mysql> SET AUTOCOМMIT =

1;

Значения 1 и ON эквивалентны, так же как 0 и OFF.

Транзакция начинается со специального запроса «START TRANSACTION», либо «BEGIN». Чтобы закончить транзакцию, нужно либо зафиксировать изменения (запрос COMMIT), либо откатить их (запрос

ROLLBACK).

Пример с COMMIT:

set autocommit=0; // Отключаем autocommit

start transaction; (также, можно написать BEGIN) …какие-то действий с БД (insert, update,delete…) commit; // Фиксация действий, запись их в физическую БД

Пример с ROLLBACK:

set autocommit=0; // Отключаем autocommit start transaction;

…какие-то действия с БД (insert, update,delete…)

rollback; // Отменяем серию действий, не производим запись в физическую БД

В MySQL не существует механизма вложенных транзакций. Одно соединение с БД — одна транзакция. Новая транзакция в пределах одного соединения может начаться только после завершения предыдущей.

4.2. Операторы транзакции

Для некоторых операторов нельзя выполнить откат с помощью ROLLBACK. Это операторы языка определения данных (Data Definition Language — DDL). Сюда входят запросы CREATE, ALTER, DROP, TRUNCATE,

COMMENT, RENAME.

Следующие операторы неявно завершают транзакцию (как если бы перед их выполнением был выдан COMMIT):

ALTER TABLE

DROP DATABASE

LOAD MASTER DATA

SET AUTOCOMMIT = 1

BEGIN

DROP INDEX

LOCK TABLES

START TRANSACTION

CREATE INDEX

DROP TABLE

RENAME TABLE

TRUNCATE TABLE

Обратите внимание, что в случае SQL ошибки, транзакция сама по себе не откатится. Обычно ошибки обрабатываются уже с помощью sql wrapper'ов в самом приложении, таких как PHP PDO например. Если вы захотите откатывать изменения в случае ошибки прямо в MySQL, можно создать специальную процедуру и уже в ней выполнять ROLLBACK в

обработчике. Однако так делать крайне не рекомендуется, так как в основном ошибки базы данных обрабатываются с помощью SQL оберток на стороне приложения, таких как PHP PDO например, чтобы оттуда полностью управлять транзакциями.

Рассмотрим практический пример: есть 2 таблицы, пользователи — users и информация о пользователях — user_info. Представим, что нам

нужно либо выполнить 3 запроса к базе данных, либо не выполнять их вообще, так как иначе это приведет к сбоям в работе приложения.

start transaction;

INSERT INTO user (id, nik) VALUES (1, 'nikola');

INSERT

INTO

user_info

(id,

id_user,

item_name,

item_value) VALUES

(1,

1,

'Имя', 'Николай');

 

INSERT

INTO

user_info

(id,

id_user,

item_name,

item_value) VALUES

(2,

1,

'Возраст', '24');

 

commit;

 

 

 

 

 

 

 

 

В целом я думаю принцип работы транзакции понятен. Но все не так просто. Существуют проблемы параллельных транзакций. Рассмотрим пример. Представим, что во время выполнения этой транзакции, другой пользователь создал вторую параллельную транзакцию и сделал запрос SELECT * FROM user после того, как в нашей транзакции был выполнен первый запрос «INSERT INTO user (id, nik) VALUES (1, 'nikola')».

Что увидит пользователь второй транзакции? Сможет ли он увидеть вставленную запись даже тогда, когда результаты первой транзакции еще не зафиксировались (не произошел COMMIT)? Или он сможет увидеть изменения только после того, как результаты первой транзакции будут зафиксированы? Оказывается имеют место быть оба варианта. Все зависит от уровня изоляции транзакции.

4.3. Уровни изоляции (изолированности) транзакций

0 — Чтение неподтверждённых данных (грязное чтение) (Read Uncommitted, Dirty Read) — самый низкий уровень изоляции. При этом уровне возможно чтение незафиксированных изменений параллельных транзакций. Как раз в этом случае второй пользователь увидит вставленную запись из первой незафиксированной транзакции. Нет гарантии, что незафиксированная транзакция будет в любой момент откачена, поэтому такое чтение является потенциальным источником ошибок.

1 — Чтение подтверждённых данных (Read Committed) — здесь возможно чтение данных только зафиксированных транзакций. Но на этом уровне существуют две проблемы. В этом режиме строки, которые участвуют в выборке в рамках транзакции, для других параллельных транзакций не блокируются, из этого вытекает проблема №1 — «Неповторяемое чтение» (non-repeatable read) — это ситуация, когда в рамках транзакции происходит несколько выборок (SELECT) по одним и тем же критериям, и между этими выборками совершается параллельная транзакция, которая изменяет данные, участвующие в этих выборках. Так как параллельная транзакция изменила данные, результат при следующей выборке по тем же критериям в первой транзакции будет другой. Проблема №2 — «Фантомное чтение» — этот случай рассмотрен ниже.

2 — Повторяемое чтение (Repeatable Read, Snapshot) — на этом уровне изоляции так же возможно чтение данных только зафиксированных транзакций. Также на этом уровне отсутствует проблема «Неповторяемого чтения», то есть строки, которые участвуют в выборке в рамках транзакции, блокируются и не могут быть изменены другими параллельными транзакциями. Но таблицы целиком не блокируются. Из-за этого остается проблема «фантомного чтения». «Фантомное чтение»

— это когда за время выполнения одной транзакции результат одних и тех же выборок может меняться по причине того, что блокируется не вся таблица, а только те строки, которые участвуют в выборке. Это означает, что параллельные транзакции могут вставлять строки в таблицу, в которой совершается выборка, поэтому два запроса SELECT * FROM table могут дать разный результат в разное время при вставке данных параллельными транзакциями.

3 — Сериализуемый (Serializable) — сериализуемые транзакции. Самый надежный уровень изоляции транзакций, но и при этом самый медленный. На этом уровне вообще отсутствуют какие-либо проблемы

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

Уровень изоляции

Возможность

Возможность

Возможность

Блокировка

чернового

неповторяющ

фантомного

чтения

 

чтения

егося чтения

чтения

 

 

 

 

 

 

 

0. READ

Да

Да

Да

Нет

UNCOMMITTED

 

 

 

 

1. READ

Нет

Да

Да

Нет

COMMITTED

 

 

 

 

2. REPEATABLE

Нет

Нет

Да

Нет

READ

 

 

 

 

 

 

 

 

 

3. SERIALIZABLE

Нет

Нет

Нет

Да

 

 

 

 

 

По умолчанию в MySQL установлен

уровень изоляции №2

(Repeatable Read). Разработчики MySQL не зря сделали по умолчанию именно этот уровень, так как он наиболее удачный для большинства случаев. С первого раза может показаться, что самый лучший вариант №3

— он самый надежный, но на практике вы можете испытать большие неудобства из-за очень медленной работы вашего приложения. Помните, что многое зависит не от того, насколько хорош уровень изоляции транзакций в БД, а от того, как спроектировано ваше приложение. При грамотном программировании, можно даже использовать самый низкий уровень изоляции транзакций — все зависит от особенностей структуры и грамотности разработки вашего приложения. Но не нужно стремиться к самому низкому уровню изоляции — нет, просто если вы используйте не самый защищенный режим, следует помнить о проблемах параллельных транзакций, в этом случае вы не потеряетесь и все сделаете правильно.

SET TRANSACTION — этот оператор устанавливает уровень изоляции следующей транзакции, глобально либо только для текущего сеанса.

SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL

{ READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ |

SERIALIZABLE }

Существующие соединения не затрагиваются. Для выполнения этого оператора нужно иметь привилегию SUPER. Применение ключевого слова SESSION устанавливает уровень изоляции по умолчанию всех будущих транзакций только для текущего сеанса.

Вы можете также установить начальный глобальный уровень изоляции для сервера mysqld, запустив его с опцией

--transaction-isolation.

Банковское приложение является классическим примером, демонстрирующим необходимость транзакций. Представьте банковскую базу данных с двумя таблицами: checking (текущие счета) и savings (сберегательные счета). Чтобы перевести 200 долларов с текущего счета Джейн на ее сберегательный счет, вам нужно сделать по меньшей мере три шага.

1.Убедиться, что остаток на ее текущем счете больше 200 долларов.

2.Вычесть 200 долларов из остатка текущего счета.

3.Добавить 200 долларов к остатку сберегательного счета.

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

Вы начинаете транзакцию командой START TRANSACTION, а затем либо сохраняете изменения командой COMMIT, либо отменяете их командой ROLLBACK. Код SQL для транзакции может выглядеть следующим образом:

START TRANSACTION;

SELECT balance FROM checking WHERE customer_id =

10233276;

UPDATE checking SET balance = balance - 200.00 WHERE

customer_id = 10233276;

UPDATE savings SET balance = balance + 200.00 WHERE

customer_id = 10233276;

COMMIT;

Но сами по себе транзакции — это еще не все. Что произойдет в случае сбоя сервера базы данных во время выполнения четвертой строки? Кто знает... Клиент, вероятно, потеряет 200 долларов. А если другой процесс вклинится между выполнением строк 3 и 4 и снимет весь остаток с текущего счета? Банк предоставит клиенту кредит 200 долларов, даже не зная об этом.

4.4. Тест ACID

Транзакций недостаточно, пока система не прошла тест ACID.

Аббревиатура ACID расшифровывается как atomicity, consistency, isolation

и durability (атомарность, согласованность, изолированность и долговечность). Это тесно связанные критерии, которым должна соответствовать правильно функционирующая система обработки транзакций.

Атомарность. Транзакция должна функционировать как единая неделимая рабочая единица таким образом, чтобы вся она была либо выполнена, либо отменена. Для атомарных транзакций не существует такого понятия, как частичное выполнение: все или ничего.

Согласованность. База данных всегда должна переходить из одного согласованного состояния в другое. В нашем примере согласованность гарантирует, что сбой между строками 3 и 4 не приведет к исчезновению с текущего счета 200 долларов. Поскольку транзакция не будет подтверждена, ни одно из изменений не отразится в базе данных.

Изолированность. Результаты транзакции обычно невидимы другим транзакциям, пока она не подтверждена. В нашем примере это гарантирует, что, если программа суммирования остатков на банковских

счетах будет запущена после третьей строки перед четвертой, она по-прежнему увидит 200 долларов на текущем счете.

Долговечность (устойчивость). После подтверждения внесенные в ходе транзакции изменения становятся постоянными. Это значит, что они должны быть записаны так, чтобы данные не потерялись при сбое системы. Долговечность, однако, является несколько расплывчатой концепцией, поскольку у нее довольно много уровней. Некоторые стратегии обеспечения долговечности дают более высокие гарантии безопасности, чем другие, и ни одна из них не является надежной на 100% (если база данных долговечна сама по себе, то каким образом резервное копирование повышает долговечность?).

Транзакции ACID гарантируют, что банк не потеряет ваши деньги. Вообще очень сложно, а то и невозможно сделать это с помощью логики приложения. Сервер базы данных, поддерживающий ACID, должен выполнить множество сложных операций, о которых вы, возможно, даже не подозреваете, чтобы обеспечить гарантии ACID.

Как и в случае увеличения детализации блокировок, оборотной стороной усиленной безопасности является увеличение объема работы сервера базы. Сервер базы данных с транзакциями ACID также требует больших мощности процессора, объема памяти и дискового пространства, чем сервер без них. Как мы уже отмечали, это тот самый случай, когда архитектура подсистем хранения данных MySQL является вашим союзником. Вы сами можете решить, требует ли приложение использования транзакций. Если они не нужны, вы можете добиться большей производительности, выбрав для некоторых типов запросов нетранзакционную подсистему хранения данных. С помощью команды LOCK TABLES можно установить нужный уровень защиты без использования транзакций. Все в ваших руках.

4.5. Механизм блокировок

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

Для решения проблем при параллельной работе используется механизм блокировок: одна или набор записей блокируются на момент выполнения транзакции. Поэтому желательно делать транзакции покороче, чтобы обеспечить равномерную работу пользователей в сети.

На практике используются 2 вида блокировок:

без взаимного доступа (монопольная, exclusive locks);

с взаимным доступом (разделяемая, shared locks).

Транзакция, предназначенная для чтения, извлечения записи, должна накладывать s-locks на читаемые записи. Транзакция, предназначенная для изменения данных, должна накладывать х-locks на эти записи. Если этой же транзакцией до этого была наложена s-locks, то эта блокировка поменяется на х-locks. Это приводит к тому, что в первом случае, только при чтении, другие пользователи могут одновременно читать ту же запись, а при изменении данных другим пользователям запрещено видеть неподтвержденные данные, пока они не будут зафиксированы.

Блокировки, как правило, задаются неявно:

запрос на извлечение данных (select) по умолчанию считается запросом с s-locks;

любой запрос на обновление (insert, delete, update) по умолчанию х-locks.

Некоторые СУБД поддерживают режим сосуществования s-locks, накладываемых разными клиентами на один и тот же ресурс. Т. к. если несколько человек одновременно читают одни и те же данные, то при

отказе наложившего s-locks формально получается, что данные уже можно изменять, хотя другие пользователи продолжают чтение.

В подсистеме хранения InnoDB применяется двухфазный протокол блокировки. Она может устанавливать блокировки в любой момент транзакции, но не снимает их до выполнения команд COMMIT или ROLLBACK. Все блокировки снимаются одновременно. Ранее описанные механизмы блокировки являются неявными. InnoDB обрабатывает блокировки автоматически в соответствии с вашим уровнем изоляции.

4.6. Взаимоблокировки

Взаимоблокировка возникает тогда, когда две и более транзакции взаимно удерживают и запрашивают блокировку одних и тех же ресурсов, создавая циклическую зависимость. Такие состояния наблюдаются и в том случае, если транзакции пытаются заблокировать ресурсы в разном порядке. Они могут возникнуть, когда несколько транзакций блокируют одни и те же ресурсы. Для примера рассмотрим две транзакции, обращающиеся к таблице StockPrice:

Транзакция №1

START TRANSACTION;

 

 

 

 

 

 

 

 

UPDATE StockPrice

SET

close

=

45.50

WHERE

stock_id

=

4

and date = '2002-05-01';

 

 

 

 

 

 

 

UPDATE StockPrice

SET

close

=

19.80

WHERE

stock_id

=

3

and date = '2002-05-02';

 

 

 

 

 

 

 

COMMIT;

 

 

 

 

 

 

 

 

Транзакция №2

START TRANSACTION;

UPDATE StockPrice SET high = 20.12 WHERE stock_id = 3 and date = '2002-05-02';

UPDATE StockPrice SET high = 47.20 WHERE stock_id = 4 and date = '2002-05-01';

COMMIT;