پرش به محتوای اصلی
پرش به محتوای مقاله

Postgres در برابر RabbitMQ؛ بهینه‌سازی قفل‌ها جایگزین ابزارهای مجزا شد

·۸ مرداد ۱۴۰۵۵ دقیقه مطالعه
راهنما
صف‌های Postgres در عمل مقیاس‌پذیرند
صف‌های Postgres در عمل مقیاس‌پذیرند
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

اثبات عملی مقیاس‌پذیری Postgres تا ۳۰ هزار اجرا در ثانیه از طریق ترکیب SKIP LOCKED، ایزولاسیون شرطی و ایندکس‌های جزئی؛ در حالی که پیش از این، سقف عملیاتی این معماری بسیار پایین‌تر تصور می‌شد.

اگر امروز برای مدیریت صف‌های پردازشی در مقیاس بالا از ابزارهای پیچیده استفاده می‌کنید، شاید زمان آن رسیده که معماری خود را بازنگری کنید. باور رایج به این نکته که برای صف‌بندی در مقیاس بالا حتماً به RabbitMQ یا Redis نیاز دارید، اشتباه است. طبق اعلام DBOS، یک پایگاه‌داده Postgres که به‌درستی تنظیم شده باشد، می‌تواند ۳۰,۰۰۰ اجرای گردش‌کار (Workflow) را در ثانیه در هزاران سرور مدیریت کند.

صف‌های Postgres در واقع مقیاس‌پذیرند

بیشتر مهندسان از به‌کارگیری Postgres برای صف‌ها دوری می‌کنند چون با مشکل «رقابت بر سر منابع» (Contention) مواجه می‌شوند. وقتی هزاران Worker به‌طور هم‌زمان یک جدول را چک می‌کنند تا تسک‌های جدید را بردارند، آن‌ها اغلب سعی می‌کنند همان تسک‌های یکسانی را بگیرند و این امر تداخلی ایجاد می‌کند که معمولاً توان عملیاتی (Throughput) را به حدود ۱۰۰ اجرا در ثانیه محدود می‌کند. این اصطکاک معماری معمولاً تیم‌ها را مجبور می‌کند تا پیچیدگی‌های مربوط به ابزارهایی مثل Celery یا BullMQ را به استک تکنولوژی خود اضافه کنند.

مکانیزم رقابت در صف

در یک صف ساده و ابتدایی مبتنی بر Postgres، کلاینت‌ها با افزودن گردش‌کارها به یک جدول صف (queues table)، تسک‌ها را ارسال می‌کنند. سپس Workerها سعی می‌کنند قدیمی‌ترین گردش‌کارهای ارسال شده را استخراج و پردازش کنند تا ترتیب FIFO (اولین ورودی، اولین خروجی) حفظ شود.

وقتی چندین Worker به‌طور هم‌زمان کوئری می‌زنند تا N تسک قدیمی را پیدا کنند، همگی ردیف‌های یکسانی را می‌بینند. چون هر گردش‌کار فقط باید توسط یک Worker پردازش شود، اکثر Workerها در تصاحب آن تسک شکست می‌خورند و مجبور می‌شوند دوباره تلاش کنند. در مقیاس بالا، این چرخه تکراری (Churn) باعث ایجاد یک گلوگاه عظیم در سیستم می‌شود.

صف‌های پیام در پستگرس واقعاً مقیاس‌پذیرند: نمودار عملکرد صف در مقایسه با سیستم‌های صف اختصاصی

بر اساس تحلیل فنی DBOS، اولین گام برای شکستن این سقف، بهره‌گیری از عبارت FOR UPDATE SKIP LOCKED است. این دستور ابتدایی به Workerها اجازه می‌دهد ردیف‌ها را بدون اینکه مانع راه دیگران شوند، قفل کنند.

عملکرد SKIP LOCKED

انتخاب ردیف‌ها با این عبارت دو اقدام حیاتی انجام می‌دهد:

  • ردیف‌ها را قفل می‌کند تا Workerهای دیگر نتوانند آن‌ها را انتخاب کنند.
  • هر ردیفی که پیش از این قفل شده است را نادیده می‌گیرد (Skip می‌کند) و N تسک قدیمی بعدی را که در حال حاضر توسط هیچ Worker دیگری نگه داشته نشده‌اند، انتخاب می‌کند.

صف‌های Postgres در عمل مقیاس‌پذیرند: تحلیل عملکرد و راهکارهای بهینه‌سازی

صف‌های Postgres در عمل مقیاس‌پذیرند

این روش اجازه می‌دهد Workerهای زیادی به‌طور هم‌زمان و بدون رقابت، گردش‌کارهای جدید را استخراج کنند. در این حالت، Worker اول N تسک نخست را قفل می‌کند، Worker دوم N تسک بعدی را می‌گیرد و این روند ادامه می‌یابد. بدون این «ترفند قدیمی Postgres»، مقیاس‌پذیری فراتر از ۱۰۰ اجرا در ثانیه تقریباً غیرممکن است.

پس از حل مشکل قفل‌گذاری، تیم با سد دوم یعنی خطاهای «شکست سریال‌سازی» (Serialization Failure) روبرو شد. این اتفاق زمانی رخ می‌داد که سیستم در سطح ایزولاسیون REPEATABLE READ اجرا می‌شد؛ سطحی که تضمین می‌کند یک نمای ثابت (Consistent Snapshot) از دیتابیس در طول تراکنش وجود داشته باشد.

گلوگاه سطح ایزولاسیون

هنگام استخراج بیش از ۱,۰۰۰ گردش‌کار در ثانیه، اکثریت عملیات با این شکست‌ها مواجه می‌شدند. سطح REPEATABLE READ در ابتدا برای پشتیبانی از محدودیت‌های جهانی صف استفاده می‌شد؛ برای مثال، پیاده‌سازی قانونی مانند «حداکثر N گردش‌کار به‌طور هم‌زمان در تمام Workerها اجرا شود». این کار مستلزم آن است که Workerها یک نمای جهانی و سازگار از وضعیت سیستم داشته باشند.

اما این رویکرد در همزمانی بالا بسیار هزینه‌بر می‌شود. اگر چندین Worker ردیف‌های هم‌پوشان را تغییر دهند، Postgres یکی از آن‌ها را متوقف (Abort) می‌کند. در مقیاس بالا، Workerها زمان بیشتری را صرف تلاش مجدد برای تراکنش‌ها می‌کردند تا پردازش واقعی گردش‌کارها.

صف‌های Postgres در عمل مقیاس‌پذیرند: تحلیل عملکرد و راهکارهای بهینه‌سازی queue در پایگاه داده PostgreSQL

برای رفع این مشکل، DBOS سطوح ایزولاسیون شرطی را پیاده کرد. آن‌ها متوجه شدند که صف‌های بزرگ به‌ندرت به کنترل جریان جهانی نیاز دارند و ترجیح می‌دهند از محدودیت‌های محلی استفاده کنند (مثلاً «حداکثر ۱۰ گردش‌کار برای هر Worker») که نیازی به هماهنگی بین Workerهای مختلف ندارد.

  • صف‌هایی که به محدودیت‌های جهانی نیاز دارند، در سطح REPEATABLE READ باقی ماندند.
  • صف‌های بدون نیاز به محدودیت جهانی به سطح READ COMMITTED منتقل شدند.

این تغییر، خطاهای سریال‌سازی را به‌طور کامل حذف کرد و توان عملیاتی را به‌طور چشمگیری افزایش داد. با این حال، عبور از ۸,۰۰۰ اجرا در ثانیه باعث جهش مصرف CPU شد. مقصران اصلی، ایندکس‌های ثانویه ناکارآمد و سربار فرآیند auto-vacuum در Postgres بودند. هر بار که وضعیت یک گردش‌کار تغییر می‌کرد، دیتابیس مجبور بود چندین ایندکس را به‌روزرسانی کند که مقدار زیادی از توان پردازشی را می‌بلعید.

هزینه نگهداری ایندکس‌ها

تیم کشف کرد که نگهداری ایندکس‌ها و عملیات vacuum بخش قابل‌توجهی از CPU دیتابیس را مصرف می‌کند. این مشکل از دو ناحیه اصلی نشأت می‌گرفت:

  • کوئری استخراج: ایندکس اصلی روی queue_name و status به یافتن تسک‌ها کمک می‌کرد اما آن‌ها را با ترتیب زمانی برنمی‌گرداند. این امر Postgres را مجبور می‌کرد تا یک مرتب‌سازی (Sort) گران‌قیمت بر اساس برچسب زمانی انجام دهد.
  • ایندکس‌های نظارتی: ایندکس‌هایی مانند آن‌هایی که روی ID گردش‌کار والد (Parent Workflow ID) بودند، برای تمام گردش‌کارها نگهداری می‌شدند، فارغ از اینکه آیا واقعاً به آن‌ها نیاز است یا خیر.

صف‌های Postgres در عمل مقیاس‌پذیرند: تحلیل عملکرد و راهکارهای بهینه‌سازی queue در پایگاه داده PostgreSQL

صف‌های Postgres در عمل مقیاس‌پذیرند: تحلیل عملکرد و راهکارهای بهینه‌سازی

برای حل این موضوع، تیم به سمت استفاده از «ایندکس‌های جزئی» (Partial Indexes) حرکت کرد. آن‌ها ایندکس اصلی استخراج را به‌روز کردند تا شامل اولویت (Priority) و برچسب زمانی (Timestamp) باشد و آن را به‌گونه‌ای پیکربندی کردند که فقط زمانی ورودی‌ها را نگهداری کند که وضعیت گردش‌کار برابر با ENQUEUED باشد.

بهینه‌سازی با ایندکس‌های جزئی

این تغییرات دو برد اساسی در عملکرد ایجاد کرد:

  • حذف مراحل مرتب‌سازی گران‌قیمت: اکنون کوئری استخراج، گردش‌کارها را فوراً با ترتیب صحیح دریافت می‌کند.
  • کاهش عملیات vacuum: به محض اینکه یک تسک استخراج (Dequeue) شود، Postgres به‌جای نگهداری آن در ایندکس برای باقی عمر گردش‌کار، ورودی ایندکس را به‌سادگی حذف می‌کند.

صف‌های Postgres در عمل مقیاس‌پذیرند: تحلیل عملکرد و راهکارهای بهینه‌سازی

آن‌ها همین اصل را در بخش نظارت (Observability) نیز به کار بردند. برای مثال، ایندکس روی ID گردش‌کار والد اکنون فقط برای گردش‌کارهایی نگهداری می‌شود که واقعاً دارای والد هستند. مجموع این بهینه‌سازی‌ها باعث شد سیستم بتواند به رکورد ۸۰ میلیارد گردش‌کار در ماه دست یابد.

برای توسعه‌دهندگان، این نتایج به این معناست که «بدهی فنی» مربوط به مدیریت یک خوشه (Cluster) مجزا برای صف‌بندی ممکن است غیرضروری باشد. اکنون می‌توانید با نگه داشتن صف و وضعیت (State) در یک دیتابیس واحد، سازگاری قوی‌تری را تجربه کنید بدون اینکه کارایی سیستم را فدا کنید. این رویکرد بهینه‌سازی زیرساخت برای مدیریت جریان‌های پیچیده، مشابه تغییر رویکرد در نحوه مدیریت عامل‌های AI از طریق شواهد فنی است که در آن به جای تکیه بر قدرت خام محاسباتی، بر ساختارهای مدیریتی بهینه تمرکز می‌شود.

اگر شما در حال مدیریت یک سیستم توزیع‌شده هستید، باید ارزیابی کنید که آیا سطوح ایزولاسیون فعلی شما در حال محدود کردن توان عملیاتی‌تان است یا خیر. برای مشاهده اینکه موتور اجرای بادوام (Durable Execution Engine) شرکت DBOS چگونه این الگوها را در محیط عملیاتی پیاده کرده است، به گیت‌هاب یا مستندات آن‌ها مراجعه کنید.

گام بعدی شما

  • اگر از Postgres برای صف استفاده می‌کنید، عبارت SKIP LOCKED را در کوئری‌های خود بررسی کنید.
  • سطح ایزولاسیون تراکنش‌های خود را ارزیابی کنید؛ اگر به محدودیت‌های جهانی نیاز ندارید، از READ COMMITTED استفاده کنید.
  • برای کاهش فشار CPU، ایندکس‌های خود را به «ایندکس‌های جزئی» تبدیل کنید تا فقط داده‌های فعال را پوشش دهند.

اما داستان سخت‌افزاری این تحول حتی شگفت‌انگیزتر است — به تحلیل ما درباره‌ی تراشه‌های Blackwell مراجعه کنید.

چرا این موضوع مهم است؟

این رویکرد نیاز به زیرساخت‌های costly و پیچیده مثل RabbitMQ را در بسیاری از سناریوها حذف می‌کند. تکیه بر تجربه عملی DBOS، استانداردهای جدیدی برای استقرار سیستم‌های توزیع‌شده با سازگاری بالا ایجاد می‌کند.

تأثیر برای ایران

توسعه‌دهندگان ایرانی می‌توانند با جایگزینی ابزارهای پیچیده صف با Postgres، هزینه‌های مدیریت سرور و پیچیدگی استقرار (Deployment) را به‌شدت کاهش دهند.

·نگاه ما
تحریریه دات‌هوش

این دستاورد نشان می‌دهد که بسیاری از گلوگاه‌های مقیاس‌پذیری در دیتابیس‌های رابطه‌ای، ناشی از رفتارهای پیش‌فرض است و نه محدودیت‌های ذاتی معماری. جابه‌جایی از ابزارهای تخصصی صف به سمت دیتابیس‌های همه‌منظوره، پیچیدگی عملیاتی را کاهش می‌دهد و مدل «یک منبع برای همه کارها» را در مقیاس‌های میلیونی ممکن می‌کند.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.