Как избежать проблемы с блокировкой таблиц БД заданий при использовании очередей Laravel?

Я использую Laravel 5.1.

Очереди используются для выборки / синхронизации данных между несколькими системами.

Я использую драйвер базы данных, все время работают 3 процесса "artisan queue: work --daemon".

Задания отправляются как пользователями системы, так и планировщиком (cron). Для определения приоритета заданий используются три очереди.

Кажется, все работает нормально - таблица заданий заполняется записями, система заботится о них и удаляет уже выполненные.

Однако через некоторое время начинают мешать проблемы с блокировкой:

SQLSTATE [40001]: Ошибка сериализации: 1213 Обнаружена тупиковая ситуация при попытке получить блокировку; попробуйте перезапустить транзакцию

а также

«RuntimeException» с сообщением «Невозможно поменять местами экземпляр PDO во время транзакции».

а также

SQLSTATE [HY000]: Общая ошибка: 1205 Превышено время ожидания блокировки; попробуйте перезапустить транзакцию

Я еще не пробовал использовать другой драйвер очереди. Я бы действительно хотел остаться с базой данных. Движок - InnoDB, таблица заданий имеет структуру и индексы по умолчанию.

Есть ли способ решить эту проблему? Что ты думаешь?

Возможно, стоит упомянуть, что я вызываю DB::reconnect() внутри своих классов заданий, поскольку работники очереди работают как демоны.

Как и следовало ожидать, задания отправляются с использованием признака DispatchesJobs. Другим способом я не вмешиваюсь в алгоритм очередей.


person MaGnetas    schedule 09.09.2015    source источник
comment
Любое решение для этого?   -  person Michel Ayres    schedule 10.09.2015
comment
Неа. На данный момент перенесен на beanstalkd. Хотя мне больше понравился драйвер БД, потому что я мог отслеживать задания и неудачные задания, просто просматривая таблицы БД ...   -  person MaGnetas    schedule 10.09.2015
comment
Изучите, как Laravel обрабатывает транзакции MySQL. Похоже, вы начали транзакцию, потом долго сидели. COMMIT транзакции как можно скорее. Проверьте использование autocommit.   -  person Rick James    schedule 12.09.2015
comment
Правильное индексирование и использование транзакций MySQL может помочь вам выполнить обратную трассировку каждого модуля, связанного с заблокированной таблицей, чтобы вы могли точно определить, какая функция или часть кода «делает» блокировку. Ваше здоровье   -  person Avidos    schedule 03.11.2015
comment
Что ж ... проблема в том, что я использую встроенную подсистему очередей laravel и не хочу оставаться в стороне от всего, что стоит за отправкой и дескриптором. Я не вставляю задания в БД самостоятельно, не обновляю и не удаляю их.   -  person MaGnetas    schedule 03.11.2015
comment
У меня точно такая же проблема. Какие-нибудь решения?   -  person jaaksarv    schedule 09.11.2015
comment
Нет, для этого проекта используется beanstalkd. Хотя я бы предпочел очереди БД   -  person MaGnetas    schedule 09.11.2015


Ответы (3)


Это может быть не ответ, а некоторая информация.

При использовании операторов SELECT ... FOR UPDATE вы можете наблюдать конфликт блокировок (мертвые блокировки и т. Д.).

select … for update where x <= y

это сканирование диапазона с ‹= база данных блокирует все строки‹ = y, включая любые пробелы, поэтому, если у вас есть строки с y, например: 1, 3, 5, он блокирует даже пустое пространство между 1 и 3 в индексе, который называется пробелом запирание

можете увидеть проблему с помощью этой команды:

SHOW ENGINE INNODB STATUS;

---TRANSACTION 72C, ACTIVE 755 sec
4 lock struct(s), heap size 1248, 3 row lock(s), undo log entries 1
MySQL thread id 3, OS thread handle 0x7f84a78ba700, query id 163 localhost msandbox
TABLE LOCK table test.t trx id 72C lock mode IX
RECORD LOCKS space id 19 page no 4 n bits 80 index age of table test.t trx id 72C lock_mode X
RECORD LOCKS space id 19 page no 3 n bits 80 index GEN_CLUST_INDEX of table test.t trx id 72C lock_mode X locks rec but not gap
RECORD LOCKS space id 19 page no 4 n bits 80 index age of table test.t trx id 72C lock_mode X locks gap before rec

Последняя линия

Если у вас много пропусков в ваших транзакциях, влияющих на параллелизм и производительность, вы можете отключить их двумя разными способами:

1- Change the ISOLATION level to READ COMMITTED. In this isolation level, it is normal and expected that query results can change during a transaction, so there is no need to create locks to prevent that from happening.

2- innodb_locks_unsafe_for_binlog = 1. Disables the gap locks except for foreign-key constraint checking or duplicate-key checking.

https://www.percona.com/blog/2012/03/27/innodbs-gap-locks/

person guy_fawkes    schedule 13.01.2016
comment
все это может быть полезно, спасибо. Но мне очень не хотелось бы связываться с ядром laravel. Я считаю, что это нужно как-то решать из коробки. Хотя я мог бы внести некоторые полезные изменения в свой код или рабочий процесс, чтобы избежать этой проблемы. - person MaGnetas; 19.01.2016

Я пишу систему управления очередями на Laravel, у меня есть несколько заданий, у которых есть несколько пользователей, которым я должен отправлять электронные письма. У меня много рабочих с супервизором, и, чтобы избежать отправки нескольких писем одному и тому же пользователю, я написал этот код. Надеюсь, это поможет кому-нибудь с этой проблемой

DB::transaction(function () use ($job) {

            if (!count($job->jobUsers()->sharedLock()->get())) { // to share reading ability btw multiple workers

                Log::info('There is no user in this job');
                $job->status = Job::STATUS_FINISHED;
                $job->save();
                return;

            }

            foreach ($job->jobUsers as $jobUser) {
                Log::info($jobUser->user_id);

                JobUser::where('job_id', $jobUser->job_id)
                    ->where('user_id', $jobUser->user_id)
                    ->lockForUpdate()  // exclusive lock
                    ->update(['status' => JobUser::STATUS_SENT]);
            }

        });
person Mikayel Margaryan    schedule 12.03.2019
comment
Спасибо. Это большое решение того, что я ищу. Не могли бы вы поподробнее. Что такое пользователи вакансий и их отношение к вакансиям. Спасибо - person uncle-tee; 18.04.2020
comment
Я разместил это год назад, поскольку, как я помню, под «пользователями вакансий» имел в виду всех пользователей, упомянутых в вакансии, как пользователей, ожидающих письма. Также позже я понял, что такого рода задачи следует выполнять не только с помощью Laravel, но и с помощью других сервисов. Попробуйте использовать RabbitMQ и выдвигать каждое задание отправки электронной почты для пользователя как одну задачу в очереди, а затем прослушивать эту очередь с помощью команды Laravel, которую можно запускать как несколько рабочих. С такой структурой вы можете избежать проблем с блокировкой. - person Mikayel Margaryan; 20.04.2020
comment
Спасибо за это. Это будет иметь большое значение. Как каждый раз мне придется использовать метод lockForUpdate (), когда я обновляю поле в базе данных. - person uncle-tee; 20.04.2020

Вы можете переключиться на MyISAM, и это устранит ошибки транзакции; но в то же время вы потеряете много отличной функциональности и надежности, которые поставляются с innoDB.

* Это не вариант, если таблицы имеют внешние ключи, и вы полагаетесь на такие вещи, как каскадные удаления и обновления, которые можно найти только в innoDB.

person Douglas.Sesar    schedule 25.11.2015
comment
Я полагаю, что внешние ключи в этом случае не проблема. Таблица не имеет отношения. И миграция, создающая таблицу вакансий, не указывает движок. Это могло бы быть решением. Однако отсутствие транзакций может означать столкновения, которые трудно отследить ... - person MaGnetas; 25.11.2015