У меня есть таблица базы данных, которая в основном представляет собой очередь в порядке поступления. Строки просто вставляются в таблицу другими частями системы и забываются. Каждые 5 минут запускается задание для обработки элементов из очереди. Поле состояния каждой строки, подлежащей обработке, изменено с ожидающего значения на обрабатываемое. Последующие дубликаты в очереди сопоставляются и помечаются как дубликат ранее поставленного в очередь элемента, который обрабатывается. Задание процессора очереди — единственное, что делает с таблицей что-либо, за исключением тех частей системы, которые просто вслепую вставляют строки.
Это именно то, что процессор делает с очередью:
START TRANSACTION;
SELECT id
FROM api_queue
WHERE status=:status_processing
-- Application checks this result set is empty, then...
UPDATE api_queue qs
INNER JOIN api_queue qdupes ON qdupes.products_id=qs.products_id AND qdupes.action=qs.action
SET qdupes.status = IF(qs.id=qdupes.id, :status_processing, :status_processing_duplicate)
WHERE qs.id IN (:queue_ids) ;
COMMIT;
-- Each queue item is processed
-- Once processing is complete, we purge the queue
START TRANSACTION;
SELECT COUNT(*) AS total FROM api_queue WHERE status = :status_processing ;
-- Application sanity checks the number of processing items it's about to delete against how many it's processed, and then...
DELETE FROM api_queue WHERE status IN (:status_processing, :status_processing_duplicate) ;
COMMIT;
Обычно за 5 минут очередь формирует невыполненную работу примерно из 100 элементов, хотя иногда она может исчисляться тысячами, если в каталоге произошло много изменений.
Первая транзакция, как правило, выполняется довольно быстро, если не заходит в тупик (0,1–0,2 секунды на завершение), но кажется, что тупики возникают примерно в 10 % случаев.
Почему он так часто попадает в тупики? Даже если транзакция блокирует все строки, находящиеся в настоящее время в таблице, следует ли ожидать, что это вызовет конкуренцию при добавлении новых строк в таблицу? Если да, то почему?
Я также заметил, что иногда первая вышеприведенная транзакция (содержащая запрос UPDATE
) на самом деле вообще не применяется, хотя я думаю, что это вполне может быть несвязанной ошибкой.
Моя таблица очереди выглядит так:
CREATE TABLE IF NOT EXISTS `api_queue` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`products_id` int(11) NOT NULL,
`action` tinyint(3) NOT NULL,
`triggered_by` tinyint(3) NOT NULL,
`status` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;