Лучший способ реализовать параллельную таблицу на основе очереди

11

У меня есть таблица в MySQL, которая представляет очередь ссылок для обработки. Ссылки обрабатываются внешним приложением, одно за другим, и в конце удаляются. Это очередь большого объема, и у меня есть несколько экземпляров приложения обработки, которые распределены по нескольким серверам.

Как я могу гарантировать, что каждая запись выбирается только одним приложением? Есть ли способ пометить / заблокировать запись?

Прямо сейчас, чтобы избежать двух или более перехватов одной и той же ссылки, я разрешаю каждому экземпляру собирать только определенный набор записей (основанный на MOD их идентификатора), но это не прозрачный способ увеличить обработку очереди. скорость, просто добавляя новые экземпляры.

Мигель Е
источник
Моя мантра: «Не ставь в очередь, просто делай это». То есть вместо того, чтобы бросать задачу в очередь, запустите процесс для выполнения задачи.
Рик Джеймс

Ответы:

8

Во-первых: MySQL является одним из худших компонентов программного обеспечения для реализации, особенно если он очень динамичный. Причина в том, что механизмы, такие как MEMORY и MyISAM, имеют только блокировки полной таблицы, в то время как более подходящие механизмы, такие как InnoDB, имеют более высокий штраф на запись (для предоставления свойств ACID) и оптимизированы для доступа к записям, которые пространственно и временно закрыты (те, которые установлены в памяти). ). Также нет хорошей системы уведомлений об изменениях для MySQL - она ​​должна быть реализована как опрос. Существуют десятки программ, более оптимизированных для этой задачи .

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

SELECT FOR UPDATEэто то, что вы ищете - читайте сериализацию. Хотя UPDATE / DELETE всегда блокирует строку во время выполняющейся транзакции MYSQL, вы можете избежать большой транзакции во время процесса, поэтому:

START TRANSACTION;
SELECT * FROM your_table WHERE state != 'PROCESSING' 
  ORDER BY date_added ASC LIMIT 1 FOR UPDATE;
if (rows_selected = 0) { //finished processing the queue, abort}
else {
UPDATE your_table WHERE id = $row.id SET state = 'PROCESSING'
COMMIT;

// row is processed here, outside of the transaction, and it can take as much time as we want

// once we finish:
DELETE FROM your_table WHERE id = $row.id and state = 'PROCESSING' LIMIT 1;
}

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

jynus
источник
Спасибо. Как вы думаете, производительность может выиграть от большей блокировки (изменив LIMIT на 10)?
Мигель E
@MiguelE В общем, да, чем больше времени вы тратите на обработку и чем меньше вероятность столкновения с другими транзакциями, тем лучше. Но в некоторых случаях это может зависеть - это также может привести к обратному эффекту (блокируется больше транзакций). Всегда проверяйте это сначала. Также важно правильно индексировать таблицу, иначе в некоторых режимах изоляции вы можете получить полную блокировку таблицы.
января
1
И, вероятно, было бы неплохо отслеживать дату начала обработки строки, на случай, если процесс зависнет и вы захотите реализовать механизм тайм-аута.
Джулиан
3

Как я объяснил в этой статье , в MySQL 8 появилась поддержка SKIP LOCKED и NO WAIT.

SKIP LOCKED полезен для реализации очередей заданий (так называемых пакетных очередей), так что вы можете пропустить блокировки, которые уже заблокированы другими параллельными транзакциями.

NO WAIT полезен для избежания ожидания, пока параллельная транзакция не снимет блокировки, которые мы также заинтересованы в блокировке. Без НИКАКОГО ПОДОЖДЕНИЯ мы должны либо дождаться снятия блокировок (во время фиксации или разблокирования транзакцией, которая в данный момент удерживает блокировки), либо истечения времени ожидания получения блокировок. Следовательно, NO WAIT действует как тайм-аут блокировки со значением 0.

Для более подробной информации о SKIP LOCK и NO WAIT, ознакомьтесь с этой статьей .

Влад Михалча
источник
0

Я сделал что-то подобное с автономными проверками DBCC (два сервера делают резервные копии, а затем проверяют DBCC). Один сервер собирает все резервные копии 31 сервера вчера и помещает их в очередь, а затем этот сервер, а другой получает из этой очереди. Хотя не так много серверов, метод должен оставаться прежним: сервер приложений должен выполнить запрос обновления для очереди, обновляющей поле даты / времени и поле «сервер приложений» с именем этого сервера приложений или, что еще лучше, с числовым идентификатором. Это приведет к блокировке или, если блокировка уже получена от другого сервера, получающего следующую строку, она будет заблокирована и будет ждать, пока другое приложение завершит получение следующей строки. Затем вы захотите, чтобы приложение извлекало самую последнюю запись из очереди для своего поля приложения и получало из нее любую информацию, которую вы хотите. Используя MySQL '

Крис Вудс
источник