Транзакции InnoDB
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 использует три способа блокировки - строка, диапазон и следующая строка на случай вставки (глубже я вчитываться не стал)
Вся эта теория конечно полезна, но по настоящему они используются реальными запросами.
- Чтение с уровенем REPEATABLE READ (блокировка на запись). Ждёт если над данными кто-то работает.
SELECT... LOCK IN SHARE MODE
- Чтение в режиме SERIALIZEABLE (блокировка на чтение и запись)
SELECT... FOR UPDATE
При этих запросах на время исполнении транзакции она переходит в новый режим.
Травматизм deadlock'ов
Deadlock'и, т.е. тупиковая ситуация одновременных процессов (потоков) которые нуждаются в одних и тех же или зависимых друг от друга данных часто возникают в программировании. InnoDB не исключение. Например если идут две транзакции и каждая хочет изменить ресурсы (строки/диапазон строк) которые сейчас заблокированы. Получается что ни одна транзакция не может закончится.
В таких ситуациях InnoDB вынуждена откатить одну из транзакций и выдать ошибку
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
Такие проблемы возникают при большой параллельной вставке/изменении/удалении рядов несколькими процессами. MySQL советуют все транзакции снабжать повторным запуском транзакций.
По теме..
- Сравнение нагрузок на MyIsam и InnoDB в 2007 году
- Ссылочная целостность
- XtraBackup для создания резервных копий InnoDB