مقابلة تصميم الأنظمة: صمّم Uber
«صمّم Uber» هو أحد أكثر أسئلة تصميم الأنظمة شيوعًا لأنه يلمس الفهرسة الجغرافية المكانية، وبث الموقع في الوقت الفعلي، والمطابقة، وإنتاجية الكتابة المرتفعة، كل ذلك في آنٍ واحد. القائم بالمقابلة لا يبحث عن البنية الحقيقية لـ Uber — بل يريد أن يراك تحدّد نطاق المشكلة، وتتخذ خيارات يمكن الدفاع عنها، وتفكّر بصوت مرتفع في مسألة التوسّع.
تعامل معها بترتيب مدروس: وضّح المتطلبات، قدّر الحجم، اسكتش الكيانات وواجهات البرمجة الأساسية، صمّم البيانات ومسار المطابقة، ثم ناقش نقاط الاختناق والمقايضات. يتبع هذا الشرح أدناه ذلك الترتيب لتتمرّن على البنية، لا على الإجابة فقط.
1. توضيح المتطلبات
افصل بين المتطلبات الوظيفية وغير الوظيفية، وحدّد النطاق قبل التصميم. يطلب الركاب رحلة ويرون السائق يقترب في الوقت الفعلي؛ يتصل السائقون بالإنترنت ويستقبلون طلبات قريبة؛ يقوم النظام بمطابقتهم وتتبّع الرحلة حتى اكتمالها.
- وظيفي: طلب رحلة، المطابقة مع سائق قريب، تتبّع الرحلة مباشرةً، حساب التعرفة، سجل الرحلات.
- غير وظيفي: مطابقة منخفضة الكمون (ثوانٍ)، توافر عالٍ، تحديثات موقع فورية، اتساق يضمن تخصيص السائق لراكب واحد فقط بالضبط.
- خارج النطاق (صرّح بذلك): التفاصيل الداخلية لمعالجة الدفعات، التقييمات، تعلّم الآلة لتعرفة الذروة — اذكرها ثم أجّلها.
2. تقدير الحجم
الأرقام التقريبية تبرر القرارات اللاحقة. افترض نحو 10 ملايين سائق نشط يوميًا يرسلون نبضة موقع كل 4 ثوانٍ — وهذا يعني نحو 2.5 مليون عملية كتابة في الثانية لبيانات الموقع، وهو عبء يهيمن عليه الكتابة ويستبعد فورًا استخدام جدول علائقي بسيط كمسار ساخن.
3. المكونات الأساسية وواجهات البرمجة
اسكتش الخدمات وتدفّق الطلبات. حافظ على استقلالية مسار المطابقة عن المخزن الدائم للرحلات لكي يتوسع كل منهما وفق خصائصه الخاصة.
- خدمة الموقع: تستوعب نبضات السائقين وتحافظ على المواقع الحالية في فهرس جغرافي مكاني داخل الذاكرة.
- خدمة المطابقة: عند طلب رحلة، تستعلم عن السائقين المتصلين القريبين وتخصص واحدًا منهم.
- خدمة الرحلات: سجل دائم لدورة حياة الرحلة (مطلوبة ← متطابقة ← جارية ← مكتملة).
- الإشعارات/البث: يرسل موقع السائق المباشر إلى الراكب (WebSocket/الاستقصاء الطويل).
4. المطابقة الجغرافية المكانية — جوهر المسألة
هذا ما يريد القائم بالمقابلة سماعه أكثر من أي شيء آخر. تخزين خطوط الطول والعرض الخام ومسحها هو O(n)؛ تحتاج إلى فهرس مكاني. اشرح الشجرات الرباعية (quadtrees) أو تجزيء الموقع الجغرافي (geohashing): تقسيم الخريطة إلى خلايا بحيث يصبح «البحث عن سائقين قريبين من هذه النقطة» عملية بحث في عدد قليل من الخلايا لا مسحًا كاملًا.
يشفّر تجزيء الموقع الجغرافي منطقة ما في سلسلة نصية قصيرة تعني فيها البادئات المشتركة قربًا مكانيًا، مما يحوّل «القريب» إلى استعلام بادئة. تعيش الخلايا الحالية للسائقين في مخزن داخل الذاكرة (مثل Redis) لقراءات سريعة وتحديثات متكررة؛ وحجم الخلية هو مقايضة قابلة للضبط بين الدقة وتفرّع الاستعلام.
5. التوسّع ونقاط الاختناق
حدّد نقاط الضغط وكيف ستخفّف منها. تدفّق 2.5 مليون عملية كتابة موقع في الثانية هو النقطة الأكثر هيمنة.
- قسّم فهرس الموقع جغرافيًا (sharding) — يُعالَج سائقو وركاب مدينة واحدة بواسطة نفس الجزء (shard)، مما يحافظ على المطابقة محليّة.
- استوعب ارتفاع الكتابة بمخزن داخل الذاكرة وكتابات دائمة غير متزامنة؛ لست بحاجة إلى تسجيل كل نبضة في قاعدة بيانات دائمة.
- اضمن سائقًا واحدًا لراكب واحد بتخصيص ذرّي (قفل أو تحديث شرطي) لتجنّب الحجز المزدوج.
- استخدم قائمة انتظار رسائل (Kafka) بين استيعاب الموقع والمستهلكين اللاحقين لفصل الارتفاعات المفاجئة.
6. مقايضات يجب التصريح بها
اختم بذكر التوترات التي يقدّرها القائم بالمقابلة: الاتساق القوي مقابل الاتساق النهائي (تخصيص السائق يجب أن يكون قويًا؛ عرض الموقع يمكن أن يكون نهائيًا)، سرعة الذاكرة مقابل الديمومة، ودقة حجم الخلية مقابل تفرّع الاستعلام. التصريح بهذه الأمور بوضوح هو الفارق بين إجابة ناجحة وإجابة قوية.
تدرّب على تصميم نظام Uber بمساعدة فورية
يمكن لـمساعد مقابلات تصميم الأنظمة من Natively أن يحافظ على تنظيمك خلال جولة مباشرة — يقترح البُعد التالي الذي يجب تغطيته ويعرض المقايضات في الوقت الفعلي، كل ذلك على جهازك الخاص.
