Взаимные блокировки на MySQL, удаление строк
У нас есть (в настоящее время InnoDB) таблица, которая содержит примерно 500000 строк. Это представляет очередь задач для запуска. Он хранится в базе данных MySQL.
На постоянной основе, по крайней мере, один раз в секунду, но иногда чаще, мы выбираем данные из них и впоследствии обновляем некоторые строки. Раз в день мы убираем старые строки из таблицы.
Мы начали зацикливаться на столе, и это привело к остановке обработки наших задач. Эти тупики были вызваны во время ночной пробежки. Комбинация DELETE, SELECT и UPDATE означала, что по сути ничего продуктивного не могло произойти. У меня, к сожалению, нет выхода из SHOW ENGINE INNODB STATUS.
Я хотел бы знать лучший вариант для решения этой проблемы. Обратите внимание, что наш код обнаруживает взаимоблокировки и перезапускает запрос. Кроме того, мы давно обнаружили, что одновременное удаление всех совпадающих строк слишком обременительно для таблицы базы данных, в которой наблюдается большая активность, поэтому мы ОГРАНИЧИВАЕМ наши удаления до 10 000 строк за раз и продолжаем повторный запрос до тех пор, пока все необходимые строки не будут обрезка.
Я вижу следующие варианты и хотел бы узнать, какие из них являются лучшими, или предложения по другим вариантам:
- УДАЛИТЬ меньше строк одновременно
- Используйте экспоненциальный откат в наших DELETE, хотя я обеспокоен тем, что это не поможет, учитывая нашу конкретную рабочую нагрузку
- ЗАБЛОКИРОВАТЬ СТОЛЫ согласно документации MySQL. Возможно, мы могли бы принять блокировку операторов SELECT и UPDATE на время удаления.
- Переключитесь на тип таблицы MyISAM. Мы пошли с InnoDB, потому что мы первоначально использовали транзакции для этой таблицы. Это больше не так. Я недостаточно знаком со спецификой, чтобы знать, является ли это жизнеспособным решением.
- Возможно, используйте UPDATE LOW_PRIORITY. Возможно, что DELETE не влияют на SELECT, а только на UPDATE, и этого может быть достаточно.
2 ответа
При выполнении DML
операции, InnoDB
блокирует все отсканированные строки, но не совпадает.
Рассмотрим эту схему таблицы:
DROP TABLE t_tran;
CREATE TABLE t_tran (id INT NOT NULL PRIMARY KEY, data INT NOT NULL, KEY ix_tran_data (data)) Engine=InnoDB;
DROP TABLE t_tran;
CREATE TABLE t_tran (id INT NOT NULL PRIMARY KEY, data INT NOT NULL, KEY ix_tran_data (data)) Engine=InnoDB;
INSERT
INTO t_tran
VALUES
(1, 1),
(2, 2),
(3, 3),
(4, 4),
(5, 5),
(6, 6),
(7, 7),
(8, 8);
START TRANSACTION;
DELETE
FROM t_tran
WHERE data = 2
AND id <= 5;
В этом случае, MySQL
выбирает RANGE
путь доступа на id
, который он считает дешевле, чем REF
на data
,
В параллельной транзакции вы сможете удалять или обновлять строки 6
, 7
, 8
но не ряды 1
в 5
так как они заблокированы (несмотря на то, что только строка 2
был затронут).
Если вы удалите id <= 5
из условия выше, вы сможете удалить любую строку, кроме строки 3
,
К сожалению, вы не можете контролировать MySQL
пути доступа в DML
операции.
Лучшее, что вы можете сделать, это правильно проиндексировать ваши условия и надеяться, что MySQL
выберет эти индексы.
Убедитесь, что ваша изоляция транзакции помечена как прочитанная и не повторяемая. Чтение зафиксировано должно быть по умолчанию, но мы увидели, что на нашем сервере по умолчанию innodb повторяется чтение.
Вы можете проверить, запустив следующее:
SHOW VARIABLES LIKE 'tx%';
Чтобы установить это, в своем файле my.cnf введите следующую строку:
tx_isolation=READ-COMMITTED