Как стать автором
Обновить

Транзакции InnoDB

Время на прочтение4 мин
Количество просмотров4.5K
InnoDB это транзакционный, реляционный движок работающий на основе MySQL сервера. Начиная с 2001 года он поставляется в стандартной сборке, а с версии 5.1 может устанавливаться в качестве плагина (без необходимости перекомпилировать ядро сервера). Синтаксис очень простой.
START TRANSACTION;
...
COMMIT; -- или же ROLLBACK; если что-то пошло в логике не так

Про определение


Определение транзакционности и реляционности значат во-первых значат полноценную связанность таблиц через FK и как следствие — целостность данных при удалении рядов. С MyIsam как известно приходилось вручную удалять связанные данные в нескольких таблицах, в InnoDB — каскадное удаление одним запросом. Во-вторых поскольку для БД немыслимы параллельные версии данных как в SVN и некому эти версии объединять в одну ветку, но при этом необходима параллельная работа нескольких процессов (пользователей) с одними данными, то в качестве решения становится транзакции.
Очередь из запросов-автомобилей теперь пополняется атомарной транзакцией-автобусом. Естественно это плохо, поскольку чем длиней и дольше выполняется транзакция тем больше параллельных процессов будут ждать его. Для ускорения работы создаются остановки — типы и уровни блокировки данных. Для InnoDB по умолчанию это блокирование на уровне строки (по PK), тогда как в MyIsam атомарная операция блокирует всю таблицу.


Тразнактивность = блокировка


Два движка поэтому нельзя сравнивать — InnoDB из-за транзактивности приходится спускаться на уровень строк, поскольку вероятность очереди к одной и той же строке у двух процессов ниже, следовательно быстрей будет работа. Но как результат — на каждую строку приходится делать флаги блокировки, значит чуть больше памяти. Из-за разницы в уровнях блокировки данных, сравнивать InnoDB с MyIsam по производительности в зависимости от числа процессов достаточно трудно.
Есть несколько типов блокировок
  • READ (пока я читаю — никто не запишет) — по умолчанию на SELECT ставится
  • WRITE (пока я пишу — никто не прочтёт и не запиет) — по умолчанию на UPDATE ставится
  • LOW_PRIORITY WRITE (дам быстро прочитать если кто-то ждёт)

В качестве ликбеза — блокировать можно вручную целую таблицу (но не нужно ибо для InnoDB это убого тормозит все процессы). Повторное блокирование снимает предыдущие блокировки. Блокировать можно и виртуальные таблицы (view)
LOCK TABLES user WRITE, company READ;
UNLOCK TABLES;


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


В случаях когда два процесса одновременно и частично затрагивают общие данные то не обязательно все данные будут полностью блокированы. Существуют послабления, когда параллельные транзакции получают доступ к незавершённым транзакциям.
Текущий уровень можно получить из настроек, можно прописать в настройки или исполнить запросом — как на время транзакции так и на время всего соединения.
SELECT @@global.tx_isolation;
SET TRANSACTION ISOLATION LEVEL READ COMMITED;

По степени точности (строгости блокировки) по убыванию согласно стандарту SQL92 выделяют:
  • SERIALIZEABLE — полная независимость транзакций, в т.ч. своё чтение
  • REPEATABLE READ (повторяющееся чтение) — значение для InnoDB по умолчанию. Чтение общих строк в транзакциях разрешается, но не их изменение.
  • READ COMMITED (чтение фиксированного) — блокировка записи, но общее чтение. Есть проблема повторяющегося чтения, т.е. в первой транзакции несколь раз по разному читаются общие данные, потому что вторая транзакция их меняет.
  • READ UNCOMMITED («грязное» чтение незафиксированного) — никакой блокировки на чтение и запись. При двух одновременных UPDATE поле получит значение последнего изменения в обоих транзакциях. Возможны множество проблем, особенно если до ROLLBACK одной транзакции изменения читает другая.

В REPEATABLE READ существует проблема фантомной вставки. Поскольку блокируются только ряды на UPDATE, но не на INSERT, то параллельно с транзакцией повторяющегося чтения можно сделать вставку, из-за чего возникнет фантомный ряд. Что-бы этого избежать InnoDB использует три способа блокировки — строка, диапазон и следующая строка на случай вставки (глубже я вчитываться не стал)
Вся эта теория конечно полезна, но по настоящему они используются реальными запросами.
  1. Чтение с уровенем REPEATABLE READ (блокировка на запись). Ждёт если над данными кто-то работает.
    SELECT... LOCK IN SHARE MODE
  2. Чтение в режиме SERIALIZEABLE (блокировка на чтение и запись)
    SELECT... FOR UPDATE

При этих запросах на время исполнении транзакции она переходит в новый режим.

Травматизм deadlock'ов


Deadlock'и, т.е. тупиковая ситуация одновременных процессов (потоков) которые нуждаются в одних и тех же или зависимых друг от друга данных часто возникают в программировании. InnoDB не исключение. Например если идут две транзакции и каждая хочет изменить ресурсы (строки/диапазон строк) которые сейчас заблокированы. Получается что ни одна транзакция не может закончится.
В таких ситуациях InnoDB вынуждена откатить одну из транзакций и выдать ошибку
ERROR 1213 (40001): Deadlock found when trying to get lock; try
restarting transaction

Такие проблемы возникают при большой параллельной вставке/изменении/удалении рядов несколькими процессами. MySQL советуют все транзакции снабжать повторным запуском транзакций.
По теме..


Оригинал статьи
Теги:
Хабы:
Всего голосов 13: ↑12 и ↓1+11
Комментарии2

Публикации

Истории

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань