Каждый, кто использует таблицы Innodb, вероятно, привык к тому, что Innodb
таблицы выполняют неблокирующее чтение, то есть, если вы не используете некоторые
модификаторы, такие как LOCK IN SHARE MODE или FOR UPDATE, операторы SELECT
не будет блокировать строки во время работы.
Как правило, это правильно, однако есть заметное исключение - INSERT INTO table1 SELECT * FROM table2. Этот оператор выполнит блокировку чтения (разделяемые блокировки) для таблицы table2. Это также относится к аналогичным таблицам с предложением where и объединениями. Это важно для таблиц, которые читаются как Innodb - даже если записи выполняются в таблице MyISAM.
Итак, почему это было сделано, будучи довольно плохим для производительности MySQL и
параллелизм?
Причина - репликация. В MySQL до 5.1 репликация основана на операторах, что означает, что операторы, отвечающие на ведущее устройство, должны вызывать тот же эффект, что и на ведомом устройстве. Если бы Innodb не блокировал строки в исходной таблице, другая транзакция могла бы изменить строку и зафиксировать ее перед транзакцией, выполняющей инструкцию INSERT .. SELECT. Это приведет к тому, что эта транзакция будет применена к ведомому устройству перед оператором INSERT… SELECT и, возможно, приведет к получению данных, отличных от данных на ведущем устройстве. Блокировка строк в исходной таблице при их чтении защищает от этого эффекта, так как другие транзакции изменяют строки до того, как INSERT… SELECT сможет получить к ней доступ, также будут изменены в том же порядке на ведомом устройстве. Если транзакция пытается изменить строку после того, как к ней был получен доступ и она заблокирована INSERT… SELECT, транзакция должна будет дождаться завершения инструкции, чтобы убедиться, что она будет выполнена на ведомом устройстве в правильном порядке. Становится довольно сложным? Ну, все, что вам нужно знать, это нужно было сделать перед репликацией, чтобы работать в MySQL до 5.1.
В MySQL 5.1 это, а также некоторые другие проблемы должны решаться путем репликации на основе строк. Тем не менее, я еще не дал ему настоящие стресс-тесты, чтобы увидеть, насколько хорошо он работает:)
Еще одна вещь, которую следует учитывать - INSERT ... SELECT фактически выполняет чтение в режиме блокировки и, таким образом, частично обходит управление версиями и извлекает последнюю принятую строку. Таким образом, даже если вы работаете в режиме REPEATABLE-READ, эта операция будет выполняться в режиме READ-COMMITTED.
режим, потенциально дающий другой результат по сравнению с тем, что даст чистый SELECT. Это, кстати, относится и к SELECT .. LOCK IN SHARE MODE и к SELECT ... FOR UPDATE.
Один мой вопрос: а что, если я не использую репликацию и мой двоичный журнал отключен? Если репликация не используется, вы можете включить опцию innodb_locks_unsafe_for_binlog, которая ослабит блокировки, которые Innodb устанавливает при выполнении оператора, что обычно обеспечивает лучший параллелизм. Однако, как следует из названия, блокировка небезопасна для репликации и восстановления во времени, поэтому используйте параметр innodb_locks_unsafe_for_binlog с осторожностью.
Обратите внимание, что отключение двоичных журналов недостаточно для запуска ослабленных блокировок. Вы
также необходимо установить innodb_locks_unsafe_for_binlog = 1. Это сделано так
включение двоичного журнала не вызывает неожиданных изменений в блокировке
проблемы поведения и производительности. Вы также можете использовать эту опцию с
репликации иногда, если вы действительно знаете, что делаете. я мог бы
не рекомендую, если это действительно не нужно, так как вы можете не знать
какие другие блокировки будут ослаблены в будущих версиях и как это будет
влияет на вашу репликацию.