چگونه OpenAI با PostgreSQL به ۸۰۰ میلیون کاربر رسید؟
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 20 دقیقه

چگونه OpenAI با PostgreSQL به ۸۰۰ میلیون کاربر رسید؟

 

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

اما OpenAI مسیر متفاوتی را انتخاب کرد. به‌جای تقسیم داده‌ها بین چندین دیتابیس، تیم مهندسی این شرکت تصمیم گرفت ببیند PostgreSQL را تا چه اندازه می‌توان بهینه و مقیاس‌پذیر کرد. نتیجه این تصمیم، زیرساختی بود که توانست با تکیه بر یک سرور اصلی برای عملیات نوشتن (Primary Writer) و چندین Read Replica، به بیش از ۸۰۰ میلیون کاربر ChatGPT خدمات ارائه دهد.

البته رسیدن به این نقطه ساده نبود. در طول این مسیر، حجم بار دیتابیس بیش از ده برابر افزایش یافت و تیم OpenAI با چالش‌هایی مانند افزایش ناگهانی درخواست‌های خواندن، کوئری‌های پرهزینه، فشار شدید عملیات نوشتن و مشکلات مربوط به کش روبه‌رو شد. با این حال، آن‌ها با بهینه‌سازی مداوم بخش‌های مختلف زیرساخت خود، توانستند عملکرد و پایداری PostgreSQL را در مقیاسی حفظ کنند که بسیاری آن را غیرممکن می‌دانستند.

در ادامه این مطلب، بررسی می‌کنیم OpenAI برای رسیدن به این سطح از مقیاس‌پذیری با چه چالش‌هایی مواجه شد و چگونه توانست PostgreSQL را به یکی از ستون‌های اصلی زیرساخت ChatGPT تبدیل کند.

معماری Single-Primary چیست؟

در معماری Single-Primary، تنها یک سرور دیتابیس مسئول تمام عملیات نوشتن (Write) است، در حالی که یک یا چند Read Replica درخواست های خواندن (Read) را پردازش می کنند.

مقیاس‌دهی دیتابیس

این معماری در نگاه اول یک محدودیت مهم دارد. چون تمام عملیات نوشتن باید از یک سرور عبور کند، امکان توزیع بار نوشتن بین چندین سرور وجود ندارد. به همین دلیل، بسیاری تصور می کنند چنین ساختاری برای سیستم‌هایی با میلیون ها کاربر مناسب نیست.

اما واقعیت این است که همه سیستم ها حجم یکسانی از عملیات خواندن و نوشتن ندارند. برای مثال، در ChatGPT بیشتر کاربران در حال خواندن اطلاعات، مشاهده تاریخچه گفتگوها یا دریافت پاسخ هستند و نسبت به آن، تعداد عملیات نوشتن بسیار کمتر است. به همین دلیل، اگر درخواست‌های خواندن به خوبی بین Read Replicaها توزیع شوند، این معماری همچنان می‌تواند در مقیاس بسیار بزرگ عملکرد مناسبی داشته باشد.

یکی از دلایلی که OpenAI به سراغ Sharding نرفت، هزینه بالای انجام این مهاجرت بود. تقسیم دیتابیس به چند بخش مستقل، نیازمند تغییر صدها بخش از برنامه و صرف زمان زیادی برای توسعه، آزمایش و استقرار است. از آنجا که بهینه سازی‌های انجام شده هنوز ظرفیت مورد نیاز را تامین می‌کردند، تیم مهندسی OpenAI تصمیم گرفت Sharding را به آینده موکول کند و تا جای ممکن از معماری فعلی بهره ببرد.

اما سوال اصلی اینجاست که اگر قرار نبود ساختار دیتابیس تغییر کند، OpenAI چگونه توانست همین معماری را تا این اندازه مقیاس دهد؟

پاسخ در سه راهکار اصلی نهفته است که تیم مهندسی OpenAI برای افزایش ظرفیت و کارایی Read Replicaها به کار گرفت.

راهکار اول: کاهش بار روی سرور اصلی دیتابیس

در معماری Single-Primary، مهم ترین بخش سیستم همان Primary Database است. از آنجا که تمام عملیات نوشتن از این سرور عبور می کند، هرگونه افزایش فشار می تواند عملکرد کل سیستم را تحت تاثیر قرار دهد. به همین دلیل، یکی از مهم ترین اهداف OpenAI این بود که تا حد امکان بار واردشده به Primary را کاهش دهد.

برای رسیدن به این هدف، تیم مهندسی OpenAI از چند راهکار مختلف استفاده کرد.

انتقال درخواست های خواندن به Read Replicaها

اولین و مهم ترین اقدام، ارسال بیشتر درخواست های خواندن به Read Replicaها بود. این کار باعث می شود سرور اصلی تنها روی عملیات نوشتن تمرکز کند.

البته همه درخواست های خواندن را نمی توان به Replicaها فرستاد. برخی از آن‌ها درون یک تراکنش نوشتن اجرا می‌شوند و برای حفظ یکپارچگی داده ها باید روی Primary انجام شوند. به همین دلیل، OpenAI این دسته از کوئری ها را تا حد امکان بهینه کرد تا اجرای آن‌ها باعث کند شدن سرور اصلی نشود.

انتقال سرویس های پرنویس به دیتابیس های دیگر

همه داده ها لزوما نباید در PostgreSQL ذخیره شوند. OpenAI بخشی از سرویس‌هایی را که حجم زیادی از عملیات نوشتن داشتند و امکان تقسیم آن‌ها بین چندین سرور وجود داشت، به سیستم هایی مانند Azure Cosmos DB منتقل کرد.

این کار باعث شد بخشی از بار نوشتن از PostgreSQL حذف شود و ظرفیت بیشتری برای پردازش سایر درخواست ها در اختیار Primary قرار بگیرد.

کاهش تعداد عملیات نوشتن در سطح برنامه

گاهی مشکل از خود دیتابیس نیست، بلکه برنامه بیش از حد لازم داده‌ها را ثبت یا به روزرسانی می‌کند. تیم OpenAI تعدادی از باگ‌های نرم افزار را که باعث ثبت اطلاعات تکراری می‌شدند برطرف کرد و در برخی بخش ها نیز از Lazy Write استفاده کرد.

در این روش، به جای اینکه تمام تغییرات بلافاصله در دیتابیس نوشته شوند، عملیات نوشتن در زمان مناسب و به صورت تدریجی انجام می‌شوند. این کار از ایجاد موج‌های ناگهانی درخواست‌های نوشتن جلوگیری می کند و فشار واردشده به سرور اصلی را کاهش می‌دهد.

حتی زمانی که لازم بود داده‌های جدید به جدول‌های بزرگ اضافه شوند، OpenAI این کار را با محدودیت شدید در سرعت اجرا انجام می‌داد. هرچند این فرایند ممکن بود بیش از یک هفته طول بکشد، اما در عوض از ایجاد فشار ناگهانی روی دیتابیس و اختلال در سرویس جلوگیری می‌کرد.

راهکار دوم: بهینه‌سازی کوئری‌ها و اتصال به دیتابیس

پس از کاهش بار روی سرور اصلی، OpenAI تمرکز خود را روی بهینه‌سازی کوئری‌ها و نحوه اتصال برنامه به دیتابیس گذاشت. این بخش تاثیر زیادی در افزایش کارایی سیستم و کاهش تاخیر داشت.

حذف کوئری‌های پرهزینه

تیم مهندسی OpenAI متوجه شد که برخی کوئری‌ها مقدار بسیار زیادی از منابع پردازنده را مصرف می‌کنند. یکی از این کوئری‌ها به طور همزمان ۱۲ جدول را به یکدیگر Join می‌کرد و هر بار که تعداد اجرای آن افزایش پیدا می‌کرد، باعث بروز اختلال‌های جدی در سیستم می شد.

این تجربه به OpenAI نشان داد که در سیستم های OLTP بهتر است تا حد امکان از Joinهای پیچیده اجتناب شود. در مواردی که استفاده از Join اجتناب ناپذیر است، آن‌ها کوئری‌های بزرگ را به چند کوئری ساده‌تر تقسیم می‌کنند و بخشی از منطق ترکیب داده‌ها را به لایه برنامه منتقل می‌کنند. این کار باعث می‌شود پردازش بین چندین سرور برنامه توزیع شود و فشار کمتری به دیتابیس وارد شود.

بررسی دقیق کوئری‌های تولیدشده توسط ORM

بسیاری از پروژه‌ها برای ساده‌تر شدن توسعه، از ORM استفاده می‌کنند. این ابزارها بر اساس کد برنامه، دستورات SQL را به صورت خودکار تولید می‌کنند. اگرچه این موضوع سرعت توسعه را افزایش می‌دهد، اما همیشه به معنی تولید کوئری‌های بهینه نیست.

به همین دلیل، OpenAI تمام کوئری‌های تولیدشده توسط ORM را بررسی و تحلیل می‌کند تا مطمئن شود عملکرد مناسبی دارند. همچنین تنظیماتی مانند idle_in_transaction_session_timeout را فعال کرده است تا از باقی ماندن تراکنش‌های بدون فعالیت جلوگیری کند. این موضوع مانع از اختلال در فرایند Autovacuum پستگرس می‌شود، فرایندی که مسئول پاک‌سازی و نگهداری داده های داخلی دیتابیس است.

مدیریت هوشمند اتصال ها با PgBouncer

چالش دیگری که OpenAI با آن روبه‌رو شد، تعداد اتصال‌های همزمان به دیتابیس بود. هر نمونه از Azure PostgreSQL حداکثر از ۵۰۰۰ اتصال همزمان پشتیبانی می‌کند. در گذشته، افزایش ناگهانی درخواست‌ها باعث می‌شد تمام این اتصال‌ها اشغال شوند و سرویس از دسترس خارج شود.

راه حل این مشکل استفاده از Connection Pooling بود. در این روش، به جای اینکه برای هر درخواست یک اتصال جدید ایجاد شود، اتصال‌های موجود بین درخواست‌های مختلف به اشتراک گذاشته می‌شوند. این کار شبیه استفاده چند نفر از یک خودرو برای رفتن به محل کار است، به جای ایجاد ترافیک بیشتر، از منابع موجود به شکل بهینه استفاده می شود.

OpenAI برای پیاده سازی این راهکار از PgBouncer استفاده کرد. PgBouncer به عنوان یک لایه واسط بین برنامه و دیتابیس قرار می‌گیرد و اتصال‌ها را مدیریت و دوباره استفاده می‌کند. نتیجه این تغییر چشمگیر بود، میانگین زمان برقراری اتصال از ۵۰ میلی ثانیه به حدود ۵ میلی ثانیه کاهش پیدا کرد.

برای افزایش کارایی، هر Read Replica در کلاستر کوبرنتیز چندین نمونه از PgBouncer را اجرا می‌کند. این نمونه‌ها پشت یک Kubernetes Service قرار دارند تا ترافیک به صورت متوازن بین آن‌ها توزیع شود. همچنین OpenAI سرورهای برنامه، PgBouncer و Read Replicaها را در یک منطقه جغرافیایی مستقر می کند تا تاخیر شبکه و هزینه برقراری اتصال به حداقل برسد.

به دیاگرام زیر توجه کنید:

راهکار سوم: جلوگیری از خرابی‌های زنجیره‌ای

یکی از مهم‌ترین درس‌هایی که OpenAI در مسیر مقیاس‌پذیر کردن PostgreSQL آموخت، این بود که بسیاری از اختلال‌ها از خود دیتابیس شروع نمی‌شوند، بلکه از بخش‌های دیگر سیستم به دیتابیس منتقل می‌شوند.

برای کاهش تعداد درخواست‌های خواندن، OpenAI از یک لایه کش (Cache) استفاده می‌کند. در حالت عادی، بیشتر درخواست‌ها مستقیما از کش پاسخ داده می‌شوند و نیازی به مراجعه به دیتابیس نیست. این کار فشار روی دیتابیس را به شکل قابل توجهی کاهش می‌دهد.

اما اگر به هر دلیلی نرخ موفقیت کش کاهش پیدا کند، شرایط کاملا تغییر می‌کند. در این حالت، تعداد زیادی از درخواست‌ها نمی‌توانند پاسخ خود را از کش دریافت کنند و همگی به سمت دیتابیس ارسال می‌شوند. این اتفاق ممکن است در اثر خرابی سرویس کش، اجرای کوئری‌های بسیار سنگین یا حتی انتشار یک قابلیت جدید که حجم زیادی از عملیات نوشتن ایجاد می‌کند رخ دهد.

با افزایش ناگهانی بار، استفاده از منابع سرور بیشتر می‌شود، زمان اجرای کوئری‌ها افزایش پیدا می‌کند و برخی درخواست‌ها با خطای Timeout مواجه می‌شوند. در ادامه، برنامه برای جبران این خطاها دوباره همان درخواست‌ها را ارسال می‌کند و همین موضوع فشار بیشتری به دیتابیس وارد می‌کند. این چرخه می‌تواند به یک خرابی زنجیره‌ای (Cascading Failure) تبدیل شود و کل سرویس را تحت تاثیر قرار دهد.

برای جلوگیری از این مشکل، تیم مهندسی OpenAI از مکانیزمی به نام Cache Locking و Cache Leasing استفاده کرد.

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

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

به دیاگرام زیر توجه کنید:

OpenAI برای افزایش پایداری سیستم، اقدامات پیشگیرانه دیگری نیز انجام داد. یکی از این اقدامات، پیاده‌سازی Rate Limiting در لایه‌های مختلف معماری، از جمله برنامه، Connection Pooler، پروکسی و لایه کوئری بود.

هدف از این محدودیت‌ها جلوگیری از این است که افزایش ناگهانی ترافیک، نمونه‌های دیتابیس را بیش از ظرفیتشان تحت فشار قرار دهد و باعث ایجاد خرابی‌های زنجیره‌ای شود. همچنین OpenAI مراقب فاصله زمانی بین تلاش‌های مجدد (Retry Interval) بود. فاصله‌های بسیار کوتاه می‌توانند باعث ایجاد پدیده‌ای به نام Retry Storm شوند، حالتی که درخواست‌های ناموفق به‌صورت مداوم تکرار می‌شوند و تعداد آن‌ها به شکل تصاعدی افزایش پیدا می‌کند.

تیم مهندسی OpenAI لایه ORM خود را نیز توسعه داد تا از Rate Limiting پشتیبانی کند. به این ترتیب، در صورت نیاز می‌توانند الگوهای خاصی از کوئری‌ها را به‌طور کامل مسدود کنند. این قابلیت به آن‌ها کمک می‌کند در زمان افزایش ناگهانی کوئری‌های سنگین، بخشی از بار سیستم را حذف کرده و سریع‌تر به وضعیت پایدار بازگردند.

با وجود تمام این راهکارها، OpenAI همچنان با شرایطی مواجه شد که برخی درخواست‌ها منابع بسیار بیشتری از حد معمول مصرف می‌کردند. این مشکل که به Noisy Neighbor Effect معروف است، در سیستم‌های بزرگ زمانی رخ می‌دهد که یک بخش از سیستم منابع زیادی را مصرف کند و روی عملکرد بخش‌های دیگر تاثیر بگذارد.

برای مثال، انتشار یک قابلیت جدید ممکن است همراه با کوئری‌های بهینه‌نشده باشد که مصرف CPU بالایی دارند. در چنین شرایطی، حتی قابلیت‌های مهم دیگر نیز ممکن است به دلیل کمبود منابع کند شوند.

برای جلوگیری از این مشکل، OpenAI بارهای کاری مختلف را روی نمونه‌های جداگانه دیتابیس قرار داد. آن‌ها درخواست‌ها را به دو سطح Low Priority و High Priority تقسیم کردند و هر دسته را به نمونه‌های دیتابیس مستقل هدایت کردند.

این جداسازی باعث می‌شود افزایش ناگهانی بار در درخواست‌های کم‌اهمیت، عملکرد درخواست‌های حیاتی را کاهش ندهد. همین رویکرد در محصولات و سرویس‌های مختلف OpenAI نیز استفاده می‌شود تا هر بخش بتواند سطح مناسبی از پایداری و عملکرد را حفظ کند.

مقابله با محدودیت‌های معماری PostgreSQL

اگرچه PostgreSQL یک دیتابیس قدرتمند و قابل‌اعتماد است، اما مانند هر فناوری دیگری محدودیت‌های معماری خاص خود را دارد. یکی از مهم‌ترین این محدودیت‌ها به نحوه مدیریت تراکنش‌های هم‌زمان در PostgreSQL مربوط می‌شود که با استفاده از مکانیزم MVCC (Multi-Version Concurrency Control) انجام می‌شود.

در MVCC، وقتی یک کوئری بخشی از یک رکورد (Row) یا حتی فقط یک فیلد را تغییر می‌دهد، PostgreSQL نسخه جدیدی از کل آن Row ایجاد می‌کند. این طراحی باعث می‌شود چندین تراکنش بتوانند هم‌زمان به نسخه‌های مختلف یک داده دسترسی داشته باشند، بدون اینکه یکدیگر را مسدود کنند.

اما این روش برای سیستم‌هایی که حجم زیادی عملیات نوشتن دارند، چالش‌هایی ایجاد می‌کند. یکی از این مشکلات Write Amplification است. یعنی حتی اگر فقط یک فیلد تغییر کند، PostgreSQL مجبور است کل Row را دوباره ذخیره کند.

از طرف دیگر، MVCC باعث ایجاد Read Amplification نیز می‌شود. چون برای پیدا کردن آخرین نسخه معتبر یک داده، دیتابیس باید نسخه‌های قدیمی‌تر Rowها که به آن‌ها Dead Tuple گفته می‌شود را نیز بررسی کند. افزایش این نسخه‌های قدیمی می‌تواند باعث بزرگ شدن حجم جدول‌ها (Table Bloat)، افزایش حجم ایندکس‌ها (Index Bloat) و نیاز بیشتر به نگهداری و تنظیم دقیق فرایند Autovacuum شود.

راهکار اصلی OpenAI برای مقابله با محدودیت‌های MVCC این بود که بارهای کاری سنگین از نظر نوشتن را به سیستم‌های دیگری منتقل کند و در سطح برنامه نیز عملیات نوشتن غیرضروری را کاهش دهد. همچنین آن‌ها تغییرات Schema را به عملیات سبک محدود کردند تا از بازنویسی کامل جدول‌ها جلوگیری شود.

مدیریت محدودیت‌های تغییر Schema

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

در زمان این بازنویسی جدولی، پستگرس یک نسخه جدید از کل جدول ایجاد می‌کند و تغییرات را روی آن اعمال می‌کند. برای جدول‌های بسیار بزرگ، این فرایند ممکن است ساعت‌ها طول بکشد و دسترسی به داده‌ها را با مشکل مواجه کند.

برای جلوگیری از این مشکلات، OpenAI قوانین سخت‌گیرانه‌ای برای تغییر Schema تعریف کرده است:

  • فقط تغییرات سبک Schema مجاز هستند، مانند اضافه یا حذف برخی ستون‌ها که باعث بازنویسی کامل جدول نمی‌شوند.
  • تمام تغییرات Schema باید حداکثر در مدت ۵ ثانیه انجام شوند.
  • ایجاد و حذف ایندکس‌ها باید به‌صورت Concurrent انجام شود تا باعث قفل شدن دیتابیس نشود.
  • تغییرات Schema فقط روی جدول‌های موجود انجام می‌شوند.
  • قابلیت‌های جدیدی که به جدول‌های جدید نیاز دارند، باید از سیستم‌های Sharded مانند Azure Cosmos DB استفاده کنند.
  • هنگام پر کردن مقادیر جدید در یک ستون موجود (Backfill)، OpenAI محدودیت سرعت اعمال می‌کند، حتی اگر این فرایند بیش از یک هفته طول بکشد. این کار باعث می‌شود فشار ناگهانی به دیتابیس وارد نشود و پایداری سیستم حفظ شود.

این رویکرد نشان می‌دهد که در مقیاس بسیار بزرگ، فقط انتخاب یک فناوری مناسب کافی نیست، بلکه باید محدودیت‌های همان فناوری را شناخت و معماری سیستم را بر اساس آن طراحی کرد.

دسترس‌پذیری بالا و بازیابی پس از بحران

استفاده از معماری Single-Primary یک ریسک مهم دارد، اگر سرور اصلی دیتابیس دچار مشکل شود، کل سیستم تحت تاثیر قرار می‌گیرد. به همین دلیل، OpenAI برای کاهش این خطر از چندین راهکار مرتبط با High Availability و Disaster Recovery استفاده کرده است.

کاهش وابستگی به Primary

اولین اقدام OpenAI این بود که بیشتر درخواست‌های خواندن مهم را از Primary به Read Replicaها منتقل کند. به این ترتیب، اگر سرور اصلی دچار مشکل شود، بخش زیادی از عملیات خواندن همچنان می‌تواند ادامه پیدا کند.

البته عملیات نوشتن در زمان خرابی Primary همچنان با مشکل مواجه خواهد شد، اما تاثیر کلی اختلال بسیار کمتر می‌شود، زیرا تمام قابلیت‌های سرویس به یک نقطه وابسته نیستند.

استفاده از Hot Standby برای Primary

دومین راهکار، اجرای Primary در حالت High Availability همراه با یک Hot Standby است.

Hot Standby یک نسخه از دیتابیس است که به‌صورت مداوم با Primary هماهنگ می‌شود و در صورت نیاز آماده جایگزینی است. اگر سرور اصلی دچار خرابی شود یا برای انجام عملیات نگهداری نیاز به توقف داشته باشد، OpenAI می‌تواند به‌سرعت Standby را به Primary جدید تبدیل کند تا زمان قطعی سرویس به حداقل برسد.

تیم Azure PostgreSQL نیز روی بهبود فرایند Failover کار زیادی انجام داده است تا این جابه‌جایی حتی در شرایط بار بالا، با امنیت و پایداری مناسب انجام شود.

مدیریت خرابی Read Replicaها

برای Read Replicaها نیز OpenAI از چندین نمونه در هر منطقه جغرافیایی استفاده می‌کند. این Replicaها علاوه بر داشتن ظرفیت کافی، فضای اطمینان (Headroom) مناسبی دارند.

به همین دلیل، از کار افتادن یک Replica باعث قطعی کامل سرویس در آن منطقه نمی‌شود، زیرا ترافیک به‌صورت خودکار به Replicaهای دیگر منتقل می‌شود.

چالش افزایش تعداد Replicaها

همه تغییرات دیتابیس ابتدا در Primary ثبت می‌شوند و سپس اطلاعات تغییرات از طریق WAL (Write Ahead Log) به Read Replicaها ارسال می‌شود. WAL شامل تمام تغییرات دیتابیس است و Replicaها با اجرای دوباره این تغییرات، خود را با Primary هماهنگ نگه می‌دارند.

اما با افزایش تعداد Replicaها، فشار بیشتری به Primary وارد می‌شود. زیرا Primary باید اطلاعات WAL را برای تعداد بیشتری از سرورها ارسال کند. این موضوع باعث افزایش مصرف CPU و پهنای باند شبکه می‌شود و در نهایت می‌تواند باعث افزایش و ناپایداری Replica Lag شود.

در حال حاضر، OpenAI نزدیک به ۵۰ Read Replica را در مناطق جغرافیایی مختلف مدیریت می‌کند. این معماری با استفاده از نمونه‌های قدرتمند و شبکه پرسرعت به‌خوبی کار می‌کند، اما نمی‌توان تعداد Replicaها را بدون محدودیت افزایش داد، زیرا در نهایت فشار ارسال WAL می‌تواند خود Primary را به نقطه محدودیت برساند.

Replication زنجیره‌ای برای آینده

برای حل این محدودیت، OpenAI همراه با تیم Azure PostgreSQL روی معماری Cascading Replication کار می‌کند.

در این معماری، Primary دیگر مستقیما WAL را برای تمام Replicaها ارسال نمی‌کند. به‌جای آن، چند Replica میانی این اطلاعات را دریافت کرده و به Replicaهای پایین‌دست منتقل می‌کنند.

این ساختار شبیه یک درخت است و باعث می‌شود تعداد Replicaها بتواند بدون وارد کردن فشار بیش از حد به Primary افزایش پیدا کند. این روش حتی می‌تواند امکان پشتیبانی از بیش از ۱۰۰ Replica را فراهم کند.

البته این معماری پیچیدگی‌های عملیاتی جدیدی ایجاد می‌کند، مخصوصا در زمینه مدیریت Failover. به همین دلیل، OpenAI تا زمانی که از عملکرد امن و قابل‌اعتماد آن مطمئن نشود، همچنان در حال آزمایش این قابلیت است.

به دیاگرام زیر دقت کنید:

جمع‌بندی

بهینه‌سازی‌های انجام‌شده توسط OpenAI نتایج قابل توجهی به همراه داشته است.

این معماری توانسته میلیون‌ها کوئری در ثانیه را پردازش کند و در عین حال Replication Lag را تقریبا نزدیک به صفر نگه دارد. سیستم به تاخیر کمتر از چند ده میلی‌ثانیه در سطح p99 رسیده است، یعنی ۹۹ درصد درخواست‌ها در کمتر از حدود ۵۰ میلی‌ثانیه پاسخ داده می‌شوند. همچنین این زیرساخت توانسته به سطح Five-Nines Availability برسد که معادل ۹۹.۹۹۹ درصد دسترس‌پذیری است.

در ۱۲ ماه گذشته، OpenAI تنها یک رخداد SEV-0 مرتبط با PostgreSQL تجربه کرد. این اتفاق در زمان انتشار گسترده قابلیت ChatGPT ImageGen رخ داد، زمانی که بیش از ۱۰۰ میلیون کاربر جدید در کمتر از یک هفته ثبت‌نام کردند و حجم عملیات نوشتن ناگهان بیش از ۱۰ برابر افزایش یافت.

در آینده، OpenAI همچنان در حال انتقال بخش‌هایی از بارهای کاری سنگین از نظر نوشتن به سیستم‌های Sharded است. همچنین تیم مهندسی این شرکت با Azure برای توسعه Cascading Replication همکاری می‌کند تا امکان افزایش امن تعداد Read Replicaها فراهم شود. علاوه بر این، آن‌ها بررسی راهکارهای دیگری مانند PostgreSQL Sharding یا استفاده از سیستم‌های توزیع‌شده جایگزین را نیز ادامه خواهند داد.

تجربه OpenAI نشان می‌دهد PostgreSQL می‌تواند برای بارهای کاری بزرگ و به‌خصوص سیستم‌هایی که بیشتر عملیات آن‌ها خواندن است، بسیار فراتر از چیزی که معمولا تصور می‌شود مقیاس‌پذیر باشد.

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

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

/@arastoo
ارسطو عباسی
کارشناس تست نرم‌افزار و مستندات

...

دیدگاه و پرسش
برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

ارسطو عباسی

کارشناس تست نرم‌افزار و مستندات