Как да избегнете проблема със заключването на таблицата на работните места при използване на опашки на 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 за сега. Въпреки че драйверът на DB ми хареса повече, защото можех да проследя заданията и неуспешните задания, като просто гледах таблиците на DB...   -  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, и бих искал да не стоя далеч от всичко зад disptatch и манипулатора. Не вмъквам сам заданията в DB, ​​нито ги актуализирам или премахвам.   -  person MaGnetas    schedule 03.11.2015
comment
Имам същия проблем. Някакви решения?   -  person jaaksarv    schedule 09.11.2015
comment
Не, използвайки beanstalkd за този проект. Въпреки че бих предпочел DB опашки   -  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(), когато актуализирам полето в db. - person uncle-tee; 20.04.2020

Можете да преминете към MyISAM и това ще премахне грешките при транзакциите; но в същото време бихте загубили много страхотна функционалност и надеждност, които идват с innoDB.

*Това не е опция, ако таблиците имат външни ключове и разчитате на неща като каскадни изтривания и актуализации, които се намират само в innoDB

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