جاري التحميل...

لماذا تحتاج إلى البرمجة التنافسية والرياضيات في المؤسسات الكبرى؟

بواسطة احمد الحسين
آخر تحديث 21 يوليو 2025
لماذا تحتاج إلى البرمجة التنافسية والرياضيات في المؤسسات الكبرى؟
ProgrammingMathematicsSystem Design

ربما سمعت عبارات مثل: "الرياضيات ليست مهمة لمهندسي برمجة المخدمات (backend)" أو حتى ما هو أسوأ: "البرمجة التنافسية ليس لها تطبيقات في العالم الحقيقي."

هذه الافتراضات ليست خاطئة تماماً. في الواقع، قد تكون صحيحة في الشركات الناشئة في مراحلها الأولى، حيث يكون عدد المستخدمين قليلاً، ويُفضّل تسليم الميزات بسرعة على حساب قابلية التوسّع أو الصيانة طويلة الأمد. ولكن ما إن تبدأ الشركة بالنمو وتقديم خدماتها لعدد كبير من المستخدمين، تنهار هذه الافتراضات. في تلك المرحلة، تصبح كل سطر برمجي غير فعّال ديناً تقنياً — وستندم على عدم التفكير بالأداء وقابلية التوسع منذ البداية.

لكن دعنا نواجه السؤال مباشرة: لماذا من المهم أن يكون لديك خلفية قوية في الرياضيات والبرمجة التنافسية في المؤسسات الكبيرة (أو الشركات الناشئة سريعة النمو)؟

(سأستخدم كلمتي "رياضي" و"مبرمج تنافسي" بشكل متبادل هنا، لأن هاتين المهارتين غالباً ما تتقاطعان بشكل كبير.)

مثال حقيقي من شركة تكنولوجيا كبيرة

دعونا نستعرض مشروعاً حقيقياً عملتُ عليه في إحدى الشركات التقنية الكبرى — وهو منصة مراقبة (Observability Platform) تُستخدم لرصد أداء التطبيقات عبر جمع التتبعات (Traces)، والسجلات (Logs)، والقياسات (Metrics). سنركّز هنا على القياسات.

ما هي القياسات (metrics)؟

هي قيم معنونة (labeled values) تُمثّل حالة النظام. على سبيل المثال:

cpu_usage { env="production", host="example.com", group="developers-group" }

داخلياً، يتم تحويل هذه القيم إلى صيغة تشبه JSON وتخزينها في VictoriaMetrics، وهي قاعدة بيانات زمنية (time-series database) قابلة للتوسع. منصتنا تجمع وتعالج وتخزن عدة تيرابايتات من البيانات في الدقيقة الواحدة من آلاف المخدِّمات (instances).

تبدو العملية بسيطة؟ في الحقيقة.... هي ليست كذلك.

للتعامل مع هذا الحجم، احتجنا إلى بنية موزعة — أكثر من 200 مخدِّم يعمل بالتوازي. ولم يكن بالإمكان مشاركة قاعدة بيانات مركزية أو منسّق (coordinator) مركزي، لأن ذلك سيمثل نقطة فشل واحدة (single point of failure) — وهو أمر غير مقبول لمنصة مراقبة تملك مستوى خدمة يصل إلى 99.9% ضمن عقود خدمتها (SLA).

المشكلة: الانفجار في التعددية (Cardinality Explosion)

أحد التحديات الكبرى كان ما يُعرف بـ الانفجار في التعددية (cardinality explosion) — أي عندما يقوم أحد العملاء بإرسال عدد كبير جداً من السلاسل الزمنية الفريدة (unique time series) خلال فترة قصيرة. يمكن أن يحدث هذا بسبب خطأ في الإعدادات أو مشكلة داخلية، وهو أمر يضر بأداء VictoriaMetrics بشكل كبير.

لذلك، كان لا بد من فرض حدود — تحديداً، تقييد عدد السلاسل الزمنية الفريدة المرسلة من كل عميل، وكل تطبيق، وكل بيئة.

لماذا السلاسل الزمنية الفريدة؟ لأن VictoriaMetrics تتعامل مع البيانات المكررة بكفاءة، بل ونقوم أحياناً بتكرار الجلب عمداً للمخدِّمات ذات الأولوية العالية. المشكلة ليست في الحجم، بل في التفرّد.

ما هي التعددية (Cardinality)؟ > Cardinality هي عدد القيم الفريدة في عمود جدول مقارنة بعدد الصفوف الكلي.

إذاً، المشكلة كانت:

لدينا N من العملاء، كل منهم يملك M من التطبيقات، ولدينا تدفّق من سلاسل metrics معنونة (بعضها مكرر). كيف نحسب بكفاءة عدد السلاسل الزمنية الفريدة لكل زوج عميل-تطبيق؟

النهج الساذج ومشاكله

الحل البديهي هو استخدام hash sets لكل زوج عميل-تطبيق، ودمجها باستخدام أدوات مثل Kafka أو RabbitMQ. قد ينجح هذا في شركة ناشئة صغيرة لديها بضع مئات من العملاء.

ولكن على نطاقنا:

  • K = أكثر من 200 مخدِّم
  • N = آلاف العملاء
  • M = آلاف التطبيقات
  • Q = حجم ضخم من السلاسل الزمنية

تعقيد الزمن لهذا الحل يقترب من O(K × N × M × Q). والأسوأ من ذلك، أنه في حال حدوث ارتفاع مفاجئ في التعددية (cardinality spike)، تزداد الحمولة بشكل كبير.

إضافة منسّق مركزي (orchestrator) قد يخفف المشكلة — لكنه سيمثل عنق زجاجة (bottleneck) ونقطة فشل محتملة، وهو أمر لا يمكننا تحمّله.

حل رياضي (وأذكى)

بعد أسابيع من البحث، أدركنا أننا لا نحتاج إلى العدد الدقيق للسلاسل الفريدة — بل مجرد تقدير يساعدنا على اكتشاف حالات الحمل الزائد (overload).

إليك كيف تعاملنا مع الأمر.

افترض أن لدينا مجموعة من الأعداد الصحيحة الموزعة عشوائياً (أو سلاسل مشفّرة). دعونا نلاحظ التمثيل الثنائي لهذه الأعداد. إذا اخترت رقماً عشوائياً:

  • احتمال أن يكون أول بت (bit) هو 0 = $1/2$
  • احتمال أن يكون أول بتين هما 00 = $1/4$
  • وهكذا...

إذا قمنا بتسجيل أكبر عدد من الأصفار المتتالية في البداية (p)، فإن عدد العناصر المميزة يُقدَّر بـ $2^p$ تقريباً.

هذه هي الفكرة الأساسية وراء خوارزمية احتمالية تُسمى HyperLogLog، والتي تُقدّر التعددية من خلال أخذ عينات إحصائية، مما يتيح تتبع البيانات الفريدة باستخدام حد أدنى من الذاكرة.

لتحسين الدقة، نقسم البيانات إلى دلاء (buckets)، مثلاً $2^{16}$ دلواً (باستخدام أول 16 بت من قيمة التشفير). كل دلو يقدّر التعددية لمجموعته الفرعية، ثم ندمج النتائج.

لكن هذا يعمل فقط إذا كانت البيانات موزعة بشكل متجانس (uniformly distributed).

لكن بياناتنا هي سلاسل نصية، وليست أعداداً عشوائية.

لحل هذه المشكلة، استخدمنا دوال تشفير غير متعددة الحدود مثل xxHash وHashStateMurmur. لأن دوال التشفير متعددة الحدود تميل إلى ربط البتات مع الأحرف — مما يؤدي إلى تحيّز وأخطاء في التقدير.

تحسين عملي: تقنية المؤشرين (Two-Pointer Technique)

بعد نشر نظام التقدير الخاص بنا، واجهنا عنق زجاجة جديد: استهلاك عالٍ للذاكرة.

عندما يتجاوز أحد العملاء الحد المسموح له، كنا نحذف السلاسل الزمنية الفائضة. في النسخة الأولى من التنفيذ، كنا ننشئ مصفوفة جديدة وننسخ العناصر التي يجب الاحتفاظ بها — مما يؤدي إلى هدر في الذاكرة وزيادة في ضغط جامع النفايات (GC).

لتحسين هذا، استخدمنا تقنية المؤشرين — وهي تقنية شائعة في البرمجة التنافسية:

  • المرور من اليسار إلى اليمين
  • تتبع "آخر مؤشر آمن"
  • تبديل العناصر القابلة للحذف للأمام
  • تقليص المصفوفة داخل الذاكرة (in-place)

هذا وفّر غيغابايتات من الذاكرة، وحسّن الأداء بشكل كبير.

أفكار ختامية

هناك عدد لا يُحصى من المشاكل في الأنظمة واسعة النطاق تحتاج إلى تفكير خوارزمي ونمذجة رياضية. فريقنا، الذي يضم عدداً من المتأهلين لنهائيات ICPC، يطبّق تقنيات البرمجة التنافسية بشكل منتظم لتقليل زمن الاستجابة، واستهلاك الذاكرة، والتكاليف.

من دون هذه الذهنية، كنا سنعلق في مشاكل غير قابلة للحل — غير قادرين على تنفيذ آلية حد معدل فعالة وقابلة للتوسّع.

الاستنتاج: البرمجة التنافسية والرياضيات ليستا تمارين أكاديمية فقط — بل أدوات جوهرية في صندوق أدوات المهندس، وخاصة عند بناء أنظمة ضخمة.

المراجع

روابط إضافية ومفيدة