Ваш процесс вставки выглядит примерно так:
INSERT INTO products (this, that, something, else)
VALUES (?,?,?,?);
SELECT id FROM products WHERE this=? AND that=?;
INSERT INTO product_variants ( product_id, col,...)
VALUES (<<that id you just SELECTED>>, ...);
Другими словами, у вас есть последовательность INSERT, а затем SELECT. Это промышленный рецепт для взаимоблокировок в загруженной СУБД.
Старайтесь избегать SELECT, используя MySQL s LAST_INSERT_ID()
function . Он возвращает вам значение идентификатора автоинкремента, обычно первичного ключа, после любой вставки. Сделайте что-то вроде этого:
INSERT INTO products (this, that, something, else)
VALUES (?,?,?,?);
SET @product_id := LAST_INSERT_ID();
INSERT INTO product_variants ( product_id, col,...)
VALUES (@product_id, ...);
SET @product_variant_id := LAST_INSERT_ID();
Тогда вам не нужно будет чередовать INSERT с SELECT только для того, чтобы получить значение id. Операции SET в моем примере позволяют вам повторно использовать значение id для операции INSERT; Следующий INSERT перезаписывает значение LAST_INSERT_ID, поэтому вам нужно сохранить это значение, если оно используется в качестве внешнего ключа в последующих INSERT.
Также попробуйте заключить последовательности INSERT в BEGIN TRANSACTION;
и COMMIT;
.
Также попробуйте уменьшить количество процессов, потребляющих данные из вашей очереди. Как это ни парадоксально, слишком много одновременных операций в СУБД может снизить производительность. И параллелизм определенно увеличивает вероятность взаимоблокировок.
Редактировать Конечно, все ваши связанные вставки должны быть сделаны в одном соединении с СУБД, чтобы LAST_INSERT_ID был полезен.
Другая возможная возможность подход, чтобы получить правильное значение для внешнего ключа и затем использовать его для вставки.
BEGIN TRANSACTION;
SELECT id INTO @fk
FROM master_table
WHERE whatever
AND whatever
FOR UPDATE;
INSERT INTO detail_table (master_id, col, col, col)
VALUES (@fk, ?, ?);
COMMIT;
Этот SELECT ... FOR UPDATE устанавливает блокировку транзакции в строке главной таблицы, с которой вы работаете , Если вы будете тщательно следовать этому шаблону, это приведет к тому, что каждая операция получит необходимые блокировки в том же порядке. Какой промышленный рецепт для предотвращения тупиков. Если вам нужно получить значения FK из нескольких таблиц для выполнения операции, всегда упоминайте таблицы в одном и том же порядке в инструкциях SELECT ... FOR UPDATE
Но, тем не менее, LAST_INSERT_ID, безусловно, самый эффективный и Самый надежный способ сделать эту работу. Вы должны рассмотреть возможность рефакторинга вашего кода, чтобы использовать его везде, где это возможно.