- PVSM.RU - https://www.pvsm.ru -
Здравствуй!
Предлагаю всем желающим вспомнить или познать суть блокировок движка InnoDB в MySQL.
КДПВ: deadlock в исполнении тропической фауны
Все, думаю, уже знают, что InnoDB использует блокировки на уровне строк. В зависимости от уровня изоляции транзакции могут блокироваться как строки, попавшие в результирующую выборку, так и все строки, что были просмотрены при поиске. Например, в REPEATABLE READ блокирующий запрос без использования индекса потребует перебора всей таблицы, а следовательно и блокировки всех записей. Посему помни, %username%, правильный выбор индексов напрямую влияет на скорость работы блокировок.
Есть два базовых типа блокировок:
Если копнуть глубже, то выяснится, что есть еще 2 типа блокировок, назовем их блокировками «о намерениях». Нельзя просто так взять и заблокировать запись в InnoDB. Блокировки intention shared и intention exclusive являются блокировками на уровне таблицы и блокируют только создание других блокировок и операции на всей таблице типа LOCK TABLE. Наложение такой блокировки транзакцией лишь сообщает о намерении данной транзакции получить соответствующую совместную или исключительную блокировку строки.
Если наложенная на строку блокировка не позволяет выполнить операцию, то транзакция просто ждет снятия мешающей блокировки. В случае перекрестных блокировок ждать можно долго, это deadlock. В документации MySQL есть пара советов [1] о том, как избежать взаимных блокировок.
InnoDB накладывает блокировки не на сами строки с данными, а на записи индексов. Та или иная блокировка может накладываться на:
Блокировка промежутков нужна для того, чтобы избежать появления фантомных записей, когда, например, между двумя одинаковыми чтениями диапазона соседняя транзакция успевает вставить запись в этот диапазон.
Все описанное выше определяется InnoDB неявно, вам нужно лишь представлять, что происходит «под капотом».
О том какие именно запросы накладывают блокировки, можно посмотреть опять же в документации [2].
Ну и перед тем как перейти к уровням изоляции, рассмотрим понятие согласованного чтения (consistent read).
В момент первого запроса в транзакции создается снэпшот данных БД (т.н. read view), на который не влияют изменения в параллельных транзакциях, но влияют изменения в текущей. Чтение из такого снэпшота называют неблокирующим согласованным чтением. Неблокирующим — потому что для создания такого снэпшота не требуется навешивание блокировок, согласованным — потому что никакие катаклизмы во внешним мире (кроме DROP TABLE и ALTER TABLE) не повлияют на уютный мирок снэпшота. InnoDB можно попросить сделать снэпшот и до первого запроса в транзакции, для этого нужно упомянуть об этом во время старта транзакции — START TRANSACTION WITH CONSISTENT SNAPSHOT.
Уровень изоляции можно изменить запросом SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL.
И напоследок пара упомянутых в тексте штук, про которые следует знать.
SELECT… LOCK IN SHARE MODE — блокирует считываемые строки на запись.
Другие сессии могут читать, но ждут окончания транзакции для изменения затронутых строк. Если же в момент такого SELECT'а строка уже изменена другой транзакцией, но еще не зафиксирована, то запрос ждет окончания транзакции и затем читает свежие данные. Данная конструкция нужна, как правило, для того чтобы получить свежайшие данные (независимо от времени жизни транзакции) и заодно убедиться в том, что их никто не изменит.
SELECT… FOR UPDATE — блокирует считываемые строки на чтение. Точно такую же блокировку ставит обычный UPDATE, когда считывает данные для обновления.
Обратите внимание, что такие запросы читают уже не из снэпшота, как простой SELECT, т.е. они увидят изменения зафиксированные другой транзакцией после начала текущей. Так происходит потому, что InnoDB может заблокировать только последнюю версию строки, а в снэпшоте она не обязательно будет последней.
Например, в сценарии считать → изменить → записать обратно между считать и записать параллельная транзакция может изменить данные, но это изменение будет тут же затерто текущей транзакцией при записи обратно. LOCK IN SHARE MODE в данном примере не даст вклиниться соседней транзакции, ей придется подождать. Заметьте, что в данном случае блокировка будет ставиться дважды, сначала совместная блокировка при чтении, затем исключительная при записи. Так как блокировок две, то есть теоретический шанс проскочить третьей между ними и вызвать deadlock.
Отличие FOR UPDATE в том, что он сразу ставит исключительную блокировку, такую же, как и обычный UPDATE. Таким образом для сценария считать → изменить → записать обратно блокировка будет ставиться только один раз в момент считывания. Такой вариант снизит вероятность возникновения взаимных блокировок.
Автор: MastaEx
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/mysql/70557
Ссылки в тексте:
[1] пара советов: http://dev.mysql.com/doc/refman/5.7/en/innodb-deadlocks.html
[2] документации: http://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html
[3] Источник: http://habrahabr.ru/post/238513/
Нажмите здесь для печати.