مشروع: تقدير ورسم تصميم نظام كامل
مشروع: تقدير ورسم تصميم نظام كامل
قضيت الدروس التسعة الأولى من هذه السلسلة في تعلّم مفردات وتقنيات تصميم الأنظمة منفردةً: جمع المتطلبات، والتقدير السريع، وأهداف الكمون والإنتاجية، وأنماط التوسع، والمقايضات، والمخططات الأولى. هذا الدرس الأخير هو مشروع تتويجي — ستطبّق كل هذه المهارات معاً على مشكلة واقعية واحدة: تصميم خدمة مشاركة الصور الاجتماعية على نطاق مشابه لإنستغرام في مرحلة النمو المبكر.
اعمل على كل قسم بنفسك قبل قراءة التحليل. عادة الالتزام بإجابة قبل مراجعة الحل المرجعي هي التي تبني الحدس الحقيقي في التصميم.
بيان المشكلة
يُطلب منك تصميم جوهر تطبيق لمشاركة الصور. يستطيع المستخدمون:
- رفع الصور (JPEG/PNG، حتى 10 ميغابايت لكل صورة).
- عرض خلاصة شخصية من صور الحسابات التي يتابعونها.
- الإعجاب بالصور والتعليق عليها.
- متابعة المستخدمين الآخرين وإلغاء متابعتهم.
الأهداف غير الوظيفية التي حددها المحاور:
- 50 مليون مستخدم نشط يومياً (DAU).
- كل مستخدم نشط يرفع في المتوسط 0.1 صور يومياً ويشاهد 50 صورة يومياً.
- يجب أن تُحمَّل الخلاصة خلال 200 مللي ثانية p95 على اتصال جوال.
- رفع الصور يتحمل كموناً حتى ثانية واحدة.
- يستهدف النظام توافر 99.9% (أقل من 9 ساعات توقف سنوياً).
- الاحتفاظ بالبيانات: الصور تُحفظ بشكل دائم؛ أحداث الخلاصة تُحفظ لـ90 يوماً.
الخطوة 1: التقدير السريع
قبل رسم أي مربع، التزم بأرقام محددة. الحدوس الغامضة ("الطلبات كثيرة") غير قابلة للتنفيذ؛ التقديرات الدقيقة هي ما يُبنى عليه.
إنتاجية الرفع
50 مليون مستخدم × 0.1 صورة/يوم = 5 ملايين رفع يومياً.
بالتوزيع المتساوي: 5 م ÷ 86,400 ث ≈ 58 رفعاً/ثانية في المتوسط.
الذروة (افترض 3× المتوسط): ≈ 175 رفعاً/ثانية.
إنتاجية القراءة والخلاصة
50 مليون × 50 صورة/يوم = 2.5 مليار مشاهدة صورة يومياً.
متوسط: 2.5 م ÷ 86,400 ≈ 28,900 طلب قراءة/ثانية.
الذروة: ≈ 87,000 طلب/ثانية.
التخزين
5 م رفع/يوم × 3 ميغابايت متوسطاً (بعد الضغط) = 15 تيرابايت تخزين جديد يومياً.
خلال 5 سنوات: 15 تيرابايت × 365 × 5 ≈ 27 بيتابايت لتخزين الصور.
البيانات الوصفية (URL والمالك والطابع الزمني وعدد الإعجابات): ~200 بايت لكل صورة.
5 م × 365 × 5 × 200 بايت ≈ 1.8 تيرابايت بيانات وصفية خلال 5 سنوات — ضئيلة مقارنة بتخزين الملفات.
النطاق الترددي
الرفع: 175 رفع/ثانية × 3 ميغابايت ≈ 525 ميغابايت/ثانية واردة.
قراءة الخلاصة (صورة مصغرة ~100 كيلوبايت): 87,000 طلب/ثانية × 100 كيلوبايت ≈ 8.7 جيجابايت/ثانية صادرة.
الخطوة 2: الملاحظات الجوهرية التي تُشكّل المعمارية
تكشف التقديرات عن أربع رؤى حاسمة:
- نسبة القراءة إلى الكتابة ~500:1 (87,000 قراءة مقابل 175 كتابة/ثانية في الذروة). النظام كثيف القراءات بشكل هائل. حسّن مسار القراءة أولاً.
- تخزين الصور ضخم للغاية (~27 بيتابايت خلال 5 سنوات). لا تستطيع قاعدة بيانات علائقية تخزين الملفات الثنائية بهذا الحجم بكفاءة. مخزن الكائنات (مثل S3) ضرورة لا خيار.
- 8.7 جيجابايت/ثانية صادرة لا يمكن أن تأتي من خادم أصلي واحد. CDN عالمي يجب أن يستوعب حركة مرور توصيل الصور.
- توليد الخلاصة بـ28,900 طلب/ثانية وميزانية 200 مللي ثانية يعني أنك لا تستطيع تحمّل تكلفة الربط عبر كل علاقات المتابعة في وقت الطلب. يجب الحوسبة المسبقة للخلاصات (fan-out on write) أو تخزينها مؤقتاً بكثافة.
الخطوة 3: تحديد الخدمات
جمّع المسؤوليات في خدمات ذات حدود واضحة، لكل منها مهمة واحدة:
- خدمة الرفع — تستقبل الملف الثنائي من العميل، وتتحقق منه، وتخزّنه في مخزن الكائنات، وتكتب البيانات الوصفية في قاعدة البيانات، وتنشر حدثاً في خط أنابيب الخلاصة.
- خدمة الخلاصة — تعيد خلاصة المستخدم الشخصية، من كاش محوسب مسبقاً حيثما أمكن.
- خدمة الوسائط — تولّد صوراً مصغرة بدقات متعددة وتوفر روابط CDN موقّعة.
- خدمة الرسم الاجتماعي — تدير علاقات المتابعة/إلغاء المتابعة؛ يستعلم عنها خط أنابيب الخلاصة.
- خدمة التفاعل — تعالج الإعجابات والتعليقات؛ هذه الطلبات كثيفة لكنها تتحمل كموناً أعلى قليلاً من الخلاصة.
الخطوة 4: مخطط المعمارية رفيعة المستوى
الخطوة 5: التحقق من التصميم في مواجهة كل متطلب
التصميم الذي لا يمكن تتبّعه حتى متطلبات محددة هو تصميم ناقص. استعرض كل متطلب غير وظيفي:
نسبة القراءة/الكتابة 500:1 — محلولة
كاش الخلاصة (Redis، مجموعة مرتّبة واحدة لكل مستخدم) يستوعب غالبية حركة القراءة. الـCDN يستوعب توصيل ملفات الصور. فقط حالات الإخفاق وأحداث تسخين الكاش تصل لقاعدة البيانات أو مخزن الكائنات.
كمون الخلاصة 200 مللي ثانية p95 — محلول
بحث في Redis بمجموعة مرتّبة أقل من مللي ثانية. إعادة قائمة من 20 رابط صورة من خلاصة محوسبة مسبقاً يستغرق أقل من 5 مللي ثانية في الخدمة، مما يُبقي ميزانية الشبكة والعرض للعميل في حدود هدف 200 مللي ثانية. الـCDN يضمن تحميل الصور من أقرب نقطة حضور جغرافياً.
تخزين 27 بيتابايت — محلول
مخازن الكائنات (AWS S3 وGoogle Cloud Storage وAzure Blob) مصمّمة للتخزين الثنائي على نطاق الإكسابايت مع النسخ الجغرافي المدمج. هذا بالضبط عبء العمل الذي صُمّمت له.
8.7 جيجابايت/ثانية صادرة — محلول
لا يستطيع خادم أصلي واحد تقديم 8.7 جيجابايت/ثانية باقتصادية أو بكمون منخفض عالمياً. الـCDN يوزع هذا الحمل عبر مئات نقاط الحضور حول العالم. مخزن الكائنات الأصلي يتلقى فقط طلبات الإخفاق من الـCDN — نسبة ضئيلة من الحركة الكلية.
توافر 99.9% — معالج جزئياً
كل طبقة خدمة يجب أن تشغّل نسختين على الأقل خلف موازن الحمل. قاعدة البيانات الوصفية تحتاج إعداد أساسي-نسخة مع تبديل تلقائي. Redis يجب أن يعمل في وضع Cluster أو Sentinel. الـCDN يوفر تكراراً ذاتياً. سجّل هذه المتطلبات كتعليقات توضيحية على المخطط.
الخطوة 6: تحديد المقايضات والتعبير عنها
التصميم القوي ليس فقط تصميماً يعمل — بل هو تصميم يستطيع مصممه تسمية كل مقايضة رئيسية وشرح سبب اختياره لها.
Fan-out on write مقابل fan-out on read لتوليد الخلاصة
يختار المخطط fan-out on write: عند رفع المستخدم لصورة، يدفع Feed Worker فوراً مرجعاً لها في كاش خلاصة كل متابع. هذا يجعل القراءات فورية لكن الكتابات مكلفة — مشهور بـ10 ملايين متابع يُطلق 10 ملايين كتابة في الكاش لكل رفع. لمنتج في مرحلة نمو مبكر حيث معظم الحسابات لديها أقل من 1,000 متابع، fan-out on write هو الخيار الافتراضي الصحيح. عند حجم المشاهير، يصبح النهج الهجين (fan-out on read للحسابات فوق عتبة معينة) هو التطور المعياري. اذكر هذه المقايضة صراحةً في المقابلة.
SQL مجزأة مقابل NoSQL للبيانات الوصفية
يُظهر المخطط مخزن SQL مجزأاً (مثل MySQL مجزأ بـuser_id). البديل هو مخزن عمودي واسع NoSQL (Cassandra أو DynamoDB). SQL يوفر مرونة استعلامات أغنى؛ Cassandra يوفر قابلية توسع كتابة خطية أسهل. عند 175 رفع/ثانية، كتلة MySQL مجزأة ومُضبطة جيداً كافية تماماً. يتغير الاختيار إذا نمت إنتاجية الكتابة إلى مئات الآلاف في الثانية — لكن هذه ليست مشكلة اليوم.
إعادة التوجيه 301 مقابل 302 لروابط CDN
روابط الصور المُعادة في الخلاصة يجب أن تكون روابط CDN. استخدام روابط موقّعة قصيرة المدة (تنتهي بعد ساعة) يمنع استخدام الروابط خارج السياق وانتهاكات التحكم في الوصول، لكنه يتطلب من الـCDN التحقق من الرموز عند كل طلب — هذا يضيف جزءاً صغيراً من الكمون، مقبول نظراً لفائدة الأمان. سجّل هذا القرار كتعليق على مربع CDN.
الخطوة 7: إغلاق الحلقة — ماذا تقول بعد تقديم المخطط
بعد تقديم أي مخطط أول، أشر فوراً إلى ما لم يُغطَّ بعد وما سيكون الخطوة التالية:
- "لم أعالج بعد كشف الإساءة وتحديد معدل الطلبات — ستقعان في طبقة API Gateway."
- "خدمة الرسم الاجتماعي (علاقات المتابعة) مُضمَّنة لكنها غير مفصّلة. عند 50 مليون مستخدم بمتوسط 300 متابعة لكل مستخدم، هذا 15 مليار حافة — قاعدة بيانات رسومية أو جدول قائمة مجاورة في Cassandra يستحق النقاش."
- "توصيل الإشعارات (إشعارات الإعجابات الجديدة والمتابعات) يتطلب طابور غير متزامن وبوابة push notifications — غير مُظهَرة هنا."
- "النشر النشط-النشط متعدد المناطق لهدف التوافر 99.9% عبر الجغرافيا هو المستوى التالي من التصميم."
الإشارة الاستباقية للعناصر المفتوحة تُثبت أنك تفهم النطاق الكامل للمشكلة وتختار ما تؤجله، لا أنك غير مدرك له.
تمارين تطبيقية
طبّق نفس العملية من سبع خطوات على الأنظمة التالية بنفسك. حدّد لنفسك 20 دقيقة لكل مشكلة:
- محرر مستندات تعاوني في الوقت الفعلي (مثل Google Docs) — ركّز على حل النزاعات والتحويل التشغيلي.
- نظام إيفاد طلبات مشاركة الركوب — ركّز على الفهرسة الجيومكانية وتحديثات موقع السائق.
- خدمة إتمام مشتريات للتجارة الإلكترونية العالمية — ركّز على اتساق المخزون وضمان عدم تكرار عمليات الدفع.