MySQL - обновить строки таблицы без блокировки строк

У меня есть требование, когда нам нужно обновить строку, не удерживая блокировку во время обновления.

Вот подробности требований. Мы будем запускать пакетную обработку таблицы каждые 5 минут update blogs set is_visible=1 where some conditions этот запрос, чтобы обрабатывать миллионы записей, поэтому мы не хотим блокировать все строки для записи во время обновлений.

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


person Lohith MV    schedule 19.04.2019    source источник


Ответы (2)


Прежде всего, если вы по умолчанию используете механизм хранения InnoDB MySQL, вы не сможете обновлять данные без блокировок строк, за исключением установки уровня изоляции транзакции на READ UNCOMMITTED, запустив

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

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

Чтобы дополнить ответ от @Tim, действительно неплохо иметь уникальный индекс для столбца, используемого в предложении where. Однако обратите внимание, что нет абсолютной гарантии, что оптимизатор в конечном итоге выберет такой план выполнения, используя созданный индекс. Это может работать или не работать, в зависимости от случая.

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

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

P.S. Блокировка IX находится не в самой записи, а прикреплена к объекту таблицы с более высокой степенью детализации. И даже с уровнем изоляции транзакции REPEATABLE READ блокировка пропусков отсутствует, когда запрос использует уникальный индекс.

person flyingice    schedule 19.04.2019

Лучшая практика - всегда получать определенную блокировку, когда есть вероятность, что обновление может происходить одновременно с другими транзакциями. Если вашим механизмом хранения является MyISAM, тогда MySQL заблокирует всю таблицу во время обновления, и вы мало что можете с этим поделать. Если механизмом хранения будет InnoDB, то возможно, что MySQL установит эксклюзивную блокировку IX только на записи, на которые направлено обновление, но в этом случае есть предостережения. Первое, что вам нужно сделать, чтобы добиться этого, - это SELECT ... FOR UPDATE:

SELECT * FROM blogs WHERE <some conditions> FOR UPDATE;

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

CREATE UNIQUE INDEX idx ON blogs (id);

Даже с таким индексом InnoDB может по-прежнему применять блокировку пробелов к записям между значениями индекса, чтобы гарантировать соблюдение REPEATABLE READ контракта.

Итак, вы можете добавить индекс в столбцы, включенные в ваше предложение WHERE, чтобы оптимизировать обновление в InnoDB.

person Tim Biegeleisen    schedule 19.04.2019
comment
Я не уверен, правильно ли вы поняли мой вопрос? Я не хочу, чтобы во время обновления происходили блокировки строк, поэтому использование `select ... For update` заблокирует таблицы. Мой вопрос: есть ли способ обновить строку без блокировки строки? - person Lohith MV; 19.04.2019
comment
Это верно только в случае MyISAM. В InnoDB FOR UPDATE не блокирует всю таблицу, если у вас есть правильная настройка уникальных индексов. - person Tim Biegeleisen; 19.04.2019