Queue-SQL؛ حذف و آپدیت انبوه با جابهای موازی لاراول
خلاصهٔ کاملتر
تو Laravel News اومده که اجرای یه دستور UPDATE یا DELETE روی میلیونها ردیف در عمل دردسر بزرگیه: قفل دیتابیس دقیقهها باقی میمونه، رپلیکاها عقب میافتن و ممکنه وسط دیپلوی تایماوت بخوری. Queue-SQL پکیجی از Kamran Atayev برای لاراوله که همین نوشتنهای سنگین رو بین جابهای صف موازی پخش میکنه؛ کمینه و بیشینهٔ کلید اصلی کوئری رو حساب میکنه، بازهٔ ID رو به برشهای محدود میشکنه و یه Illuminate\Bus\Batch میفرسته تا هر برش مستقل پردازش بشه.
پکیج یه متد queue() روی Query Builder و Eloquent سوار میکنه که update، delete، insert و upsert رو پوشش میده. کافیه قبل از متد نوشتن زنجیرش کنی و آخرش dispatch() رو صدا بزنی — تا قبل از dispatch() هیچی صف نمیشه:
Order::where('status', 'complete')
->queue(chunk: 25000, tries: 2, onQueue: 'maintenance', throttle: 4)
->update(['status' => 'completed'])
->then(fn (Batch $batch) => Log::info("Backfill finished in {$batch->totalJobs} jobs"))
->dispatch();آرگومان throttle مخصوصاً برای بکفیلهای بزرگ به کار میاد؛ محدود کردن بچ به چهار جاب در ثانیه نمیذاره کانکشنهای دیتابیس اشباع بشن یا رپلیکیشن لگ بگیره. چون هر جاب آپدیت یا حذف یه بازهٔ ثابت از کلیدها رو هدف میگیره، اجرای دوبارهٔ همون جاب دقیقاً روی همون ردیفها میافته و retry ایمنه. ولی این خاصیت برای insert انبوه صادق نیست و مستندات پیشنهاد میده اونجا از ایندکس یکتا یا upsert() استفاده کنی.
اگه بخوای قبل از صف کردن نقشهٔ کار رو ببینی، جای dispatch() از dryRun() استفاده میکنی که فقط تعداد جابها، بازهها و تخمین ردیفها رو برمیگردونه:
Order::where('status', 'complete')
->queue(chunk: 25000)
->update(['status' => 'completed'])
->dryRun();
// ['operation' => 'update', 'table' => 'orders', 'jobs' => 412, 'estimatedRows' => 9842311]بهجای chunk میشه maxJobs داد تا سقف کل جابها ثابت بمونه؛ این دوتا با هم قابل استفاده نیستن و همزمان دادنشون InvalidArgumentException میده. نویسنده یادآوری میکنه که برشها بر اساس کمینه و بیشینهٔ کلید حساب میشن نه تعداد دقیق ردیفها، پس روی جدولهایی با گپ زیاد در ID بار جابها نامتوازن میشه.
خروجی dispatch() یه نمونهٔ Illuminate\Bus\Batch هست و دستورهای آرتیسان queue-sql:status و queue-sql:cancel (با فلگ --watch برای نمای زنده و خروجی JSON) وضعیت بچها رو نشون میدن. Queue-SQL روی PHP 8.1+ و لاراول ۱۰ تا ۱۳ کار میکنه، از SQLite و MySQL و PostgreSQL پشتیبانی میکنه و به جدول job_batches نیاز داره. تو بنچمارک حذف ۲۰ هزار ردیف روی SQLite، یه DELETE تکمرحلهای ۳.۶ برابر بیشتر از طولانیترین قفل Queue-SQL دیتابیس رو قفل نگه داشته.
نکات کلیدی:
- نوشتنهای انبوه به جابهای موازی با بازهٔ کلید مشخص شکسته میشن و نتیجه یه Batch استاندارد لاراوله.
- throttle، tries و onQueue جلوی فشار بیش از حد روی دیتابیس رو میگیرن.
- retry برای update و delete ایمنه، ولی insert انبوه میتونه ردیف تکراری بسازه.
- جدول بدون کلید اصلی عددیِ افزایشی به یه جاب تکی برمیگرده.
- مرزهای کلید موقع dispatch() قفل میشن، پس ردیفهای جدیدتر تو همون بچ نمیافتن.




