Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
В случае очереди на БД, если 2 инстанса одной...
// лучше иметь отдельный коннект для очереди
$db->query('SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE');
$db->beginTransaction();
// время, когда считаем lock устаревшим
$locktimeout = '2001-01-01 00:00:00';
try{
$row = $db->getRow('SELECT * from `queue` WHERE `lock` IS NULL OR `lock` < "'.$locktimeout.'" LIMIT 1');
$db->update('queue' , ['lock' => date('Y-m-d H:i:s')], 'where `id`='.$row['id']);
$db->commit();
}catch (Exception $e){
$db->rollBack();
// deadlock, try restart transaction, как ожидаемое поведение MySQL
if($e->getCode() = 1213){
// принимаем меры
}
}
Безусловно, RabbitMQ (и даже Redis) — удобнее и быстрее. Но и с БД тоже можно поработать до определенных нагрузок.
Если в качестве хранилища использовать MySQL, то можно воспользоваться блокировками через GET_LOCK и RELEASE_LOCK.
-- Ждем как освободится блокировка и ставим ее
SELECT GET_LOCK('queue_pop', -1);
-- Читаем задачу из очереди
SELECT * FROM queue WHERE started_at IS NULL ORDER BY id LIMIT 1;
-- Если задача найдена, переводим ее в статус обработки
UPDATE queue SET started_at = NOW() WHERE id = :id;
-- Снимаем блокировку
SELECT RELEASE_LOCK('queue_pop');
-- Обрабатываем задачу если она полученаТак между воркерами синхронизируется только момент получения задачи из очереди, а сама обработка останется асинхронной.
Подобные блокировки еще есть в Postgres и Oracle, а в Yii2 есть обертки для них:
Простая система демонов для Yii2