قواعد بياناتنا كانت تحتضر: كيف أنقذتنا ‘مرشحات بلوم’ من جحيم الاستعلامات المكلفة؟

يا جماعة الخير، السلام عليكم ورحمة الله. معكم أخوكم أبو عمر.

بتذكر قبل كم سنة، كنا شغالين على نظام ضخم فيه تسجيل مستخدمين بالملايين. وكان من أهم شروط التسجيل، طبعًا، إنه اسم المستخدم والإيميل يكونوا فريدين من نوعهم. في البداية، الأمور كانت “عال العال”. المستخدم بيكتب اسم المستخدم اللي بده ياه، واحنا بنبعت طلب صغير للسيرفر، السيرفر بيسأل قاعدة البيانات: “يا معلمة، هاد الاسم موجود عندك؟”. وبترجعله جواب “آه” أو “لأ”.

لكن لما كبر النظام وصار عنا ملايين المستخدمين، والآلاف بيحاولوا يسجلوا بنفس الدقيقة، “ولّعت الدنيا”. قاعدة البيانات صارت زي واحد حامل حمل ثقيل وبيمشي بصعوبة. كل عملية كتابة حرف في خانة اسم المستخدم كانت تعني استعلام جديد لقاعدة البيانات. السيرفرات صارت تصرخ، وقاعدة البيانات يا حرام كانت على وشك تلفظ أنفاسها الأخيرة. قضينا أيام وليالي نحاول نعمل “Optimization” ونضيف “Indexes” ونكبر حجم السيرفرات، بس المشكلة كانت أعمق من هيك.

في ليلة من الليالي، وأنا قاعد بقلّب في الأوراق وبشرب فنجان القهوة السادة تبعي، خطرتلي فكرة. لحظة شوي… أغلب الأسماء اللي الناس بتجربها هي أسماء جديدة! يعني 99% من استعلاماتنا لقاعدة البيانات بترجع بنتيجة “لأ، مش موجود”. إحنا بنستهلك أغلى مورد عنا (قاعدة البيانات) عشان نسمع جواب سلبي! يا زلمة، لازم يكون في حل أذكى. وهنا، لمعت في بالي بطلة قصتنا: مرشحات بلوم (Bloom Filters).

ما هي مرشحات بلوم؟ الساحر الذي ينقذ الموقف

ببساطة شديدة، مرشح بلوم هو عبارة عن “هيكل بيانات احتمالي” (Probabilistic Data Structure). انسوا الاسم المعقد شوي، وركزوا معي. وظيفته يجاوب على سؤال واحد فقط: “هل هذا العنصر محتمل أن يكون ضمن مجموعة؟”.

لاحظوا إني حكيت “محتمل”. وهنا سر قوته وضعفه بنفس الوقت.

  • إذا سألت مرشح بلوم عن عنصر معين (مثلاً اسم مستخدم) وأجابك: “قطعاً لا، هذا العنصر غير موجود“، فجوابه صحيح 100%. مستحيل يغلط.
  • أما إذا أجابك: “نعم، محتمل أن يكون هذا العنصر موجوداً“، فهنا يوجد احتمال صغير جداً إنه يكون غلطان. وهذا ما نسميه “الإيجابية الكاذبة” (False Positive).

تخيل معي إنه عندك حارس أمن (بواب) على باب بناية ضخمة. بدل ما نعطيه قائمة بأسماء كل سكان البناية (قاعدة البيانات)، بنعطيه “طبعة إصبع” مشوشة لكل ساكن. لما يجي زائر جديد، الحارس بياخذ طبعة إصبعه وبقارنها مع الطبعات المشوشة اللي عنده. إذا ما طابقت ولا أي وحدة، فهو متأكد 100% إنه هاد الشخص مش من السكان. أما إذا طابقت، بيقوله: “تفضل، ممكن تكون من السكان، روح اسأل الاستقبال للتأكيد”. الاستقبال هنا هو قاعدة البيانات. الحارس (مرشح بلوم) منع 99% من الزوار الغرباء من إزعاج الاستقبال!

كيف تعمل هذه الساحرة الصغيرة؟

الآلية خلف مرشحات بلوم عبقرية في بساطتها. تتكون من مكونين أساسيين:

1. مصفوفة البتات (Bit Array)

تخيلها كشريط طويل جداً من الخانات، وكل خانة فيها إما 0 أو 1. في البداية، كل الخانات بتكون 0.


[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

2. دوال التجزئة (Hash Functions)

مجموعة من الدوال (مثلاً 3 أو 5 دوال) وظيفتها تاخذ أي مدخل (زي اسم المستخدم “abu_omar”) وتحوله لرقم. كل دالة بتعطيك رقم مختلف لنفس المدخل.

عملية الإضافة (لما نسجل مستخدم جديد)

لنفترض أننا نريد إضافة اسم المستخدم “Sami” إلى مرشح بلوم. نقوم بالخطوات التالية:

  1. نمرر “Sami” على دوال التجزئة الثلاثة.
  2. لنفترض أن النتائج كانت:
    • الدالة 1 أعطت الرقم: 2
    • الدالة 2 أعطت الرقم: 8
    • الدالة 3 أعطت الرقم: 14
  3. نذهب إلى مصفوفة البتات ونغير قيمة الخانات رقم 2 و 8 و 14 إلى 1.

المصفوفة الآن تبدو هكذا:


[0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]

عملية التحقق (لما مستخدم جديد يجرب اسم)

الآن، لنفترض أن مستخدماً جديداً يحاول التسجيل باسم “Omar”.

  1. نمرر “Omar” على نفس دوال التجزئة.
  2. لنفترض أن النتائج كانت: 2، 5، 11.
  3. نذهب للمصفوفة ونتحقق من الخانات 2، 5، و 11.
  4. نجد أن الخانة 2 قيمتها 1، ولكن الخانة 5 قيمتها 0.
  5. بما أننا وجدنا خانة واحدة على الأقل قيمتها 0، فإننا متأكدون 100% أن “Omar” لم يتم إضافته من قبل. ونقول للمستخدم “الاسم متاح” بدون لمس قاعدة البيانات.

ماذا لو جرب مستخدم آخر التسجيل بنفس الاسم “Sami”؟

  1. نمرر “Sami” على الدوال، وستعطينا نفس الأرقام: 2، 8، 14.
  2. نتحقق من الخانات 2، 8، و 14 في المصفوفة.
  3. سنجد أن كل الخانات الثلاثة قيمتها 1.
  4. هنا، مرشح بلوم سيقول: “محتمل أن يكون هذا الاسم موجوداً“. في هذه الحالة فقط، نسمح للطلب بالوصول إلى قاعدة البيانات للتأكد النهائي.

لعنة الإيجابيات الكاذبة (False Positives): قد يحدث أن يتم إضافة عنصرين مختلفين (مثلاً “Sami” و “Nidal”) ويصادف أن مجموع “بصماتهم” الرقمية تُفعِّل نفس الخانات التي ينتجها عنصر ثالث لم يتم إضافته من قبل (مثلاً “Jamal”). عندها، عند التحقق من “Jamal”، سنجد كل الخانات المطلوبة تساوي 1 بالصدفة، وسيعطينا المرشح “إيجابية كاذبة”. لكن هذا الاحتمال يمكن التحكم به وتقليله بشكل كبير.

هيا بنا نُشَمِّر عن سواعدنا: مثال عملي بلغة Python

الحديث النظري جميل، لكن “الشغل بزبط الحكي”. خلينا نشوف كيف طبقنا هذا الحل عملياً. لن نكتب الخوارزمية من الصفر، بل سنستخدم مكتبة ممتازة اسمها pybloom_live.

أولاً، قم بتثبيت المكتبة:

pip install pybloom_live

والآن، لنكتب الكود الذي أنقذ سيرفراتنا.


from pybloom_live import BloomFilter
import time

# الخطوة 1: تهيئة مرشح بلوم وملؤه من قاعدة البيانات
# هذا يتم مرة واحدة عند بدء تشغيل التطبيق أو بشكل دوري

# لنفترض أن هذه هي أسماء المستخدمين الموجودة لدينا في قاعدة البيانات
existing_usernames = ["abu_omar", "sami", "ahmad", "falasteen", "user123", "developer99"]

# نقوم بإنشاء مرشح بلوم
# capacity: عدد العناصر المتوقع تخزينها
# error_rate: نسبة الخطأ (الإيجابيات الكاذبة) التي نتقبلها
# كلما قلت نسبة الخطأ، زاد حجم المرشح في الذاكرة
username_filter = BloomFilter(capacity=1000000, error_rate=0.001)

print("بدء ملء مرشح بلوم من قاعدة البيانات...")
for username in existing_usernames:
    username_filter.add(username)
print("... انتهى الملء. المرشح جاهز للاستخدام.")

# --------------------------------------------------------------------

# الخطوة 2: دالة التحقق التي يستخدمها التطبيق في كل مرة
def is_username_available(username):
    print(f"n--- التحقق من اسم المستخدم: '{username}' ---")
    
    # هنا السحر يحدث!
    if username in username_filter:
        # المرشح يقول: "الاسم قد يكون موجوداً"
        # هذه هي الحالة الوحيدة التي نتصل فيها بقاعدة البيانات
        print(f"'{username}' قد يكون موجوداً (حسب المرشح). سنتحقق من قاعدة البيانات للتأكيد...")
        
        # في تطبيق حقيقي، هنا يتم استدعاء قاعدة البيانات
        # time.sleep(0.5) # محاكاة لتأخير قاعدة البيانات
        if username in existing_usernames:
            print(f"تأكيد قاعدة البيانات: '{username}' موجود بالفعل. ❌")
            return False
        else:
            # هذه هي حالة الإيجابية الكاذبة (False Positive)
            print(f"تأكيد قاعدة البيانات: '{username}' غير موجود. كانت إيجابية كاذبة. ✅")
            return True
    else:
        # المرشح يقول: "الاسم غير موجود قطعاً"
        # لا حاجة للاتصال بقاعدة البيانات على الإطلاق!
        print(f"'{username}' غير موجود قطعاً (حسب المرشح). الاسم متاح! ✅")
        return True

# --- تجربة عملية ---
is_username_available("sami")      # موجود بالفعل
is_username_available("new_user")  # جديد تماماً
is_username_available("ali")       # قد يسبب إيجابية كاذبة بالصدفة

النتيجة كانت مذهلة. خففنا الحمل على قاعدة البيانات بنسبة تزيد عن 95% لأن معظم محاولات التسجيل كانت لأسماء جديدة. تحولت قاعدة البيانات من عنق الزجاجة إلى جزء مستقر وموثوق في نظامنا.

نصائح أبو عمر الذهبية 📝

من خلال تجربتي مع مرشحات بلوم وغيرها من الخوارزميات، تعلمت بعض الدروس التي أحب أن أشارككم إياها:

  • اعرف متى تستخدمها: مرشحات بلوم مثالية لعمليات التحقق من الوجود (Existence checks) على مجموعات بيانات ضخمة جداً لا تسع في الذاكرة. أمثلة أخرى: التحقق من أن رابط URL ليس في قائمة الروابط الخبيثة، أو التحقق من أن المستخدم لم ير هذا المقال من قبل لتجنب عرضه مرة أخرى.
  • اعرف متى لا تستخدمها: لا يمكنك حذف عنصر من مرشح بلوم القياسي! إذا حاولت تغيير خانة من 1 إلى 0، قد تتسبب في “إتلاف” بصمة عنصر آخر. (توجد أنواع متقدمة مثل Counting Bloom Filters تسمح بالحذف ولكنها أكثر تعقيداً واستهلاكاً للذاكرة). أيضاً، لا تستخدمها إذا كانت نسبة الخطأ 0% هي مطلب أساسي ولا يمكنك تحمل أي إيجابيات كاذبة.
  • الحجم هو المفتاح: قبل استخدام مرشح بلوم في بيئة الإنتاج، يجب أن تحدد حجمه (m) وعدد دوال التجزئة (k) بعناية. استخدم حاسبات مرشح بلوم المتاحة على الإنترنت. كل ما عليك هو إدخال عدد العناصر المتوقعة (n) ونسبة الخطأ المقبولة (p)، وستعطيك الأرقام المثالية.
  • تذكر الحكمة القديمة: “درهم وقاية خير من قنطار علاج”. مرشح بلوم هو “درهم الوقاية” الذي يمنع “قنطار العلاج” المتمثل في ترقية السيرفرات وقواعد البيانات بشكل مكلف ومستمر.

الخلاصة: أداة بسيطة بتأثير هائل

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

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

فيا صديقي المبرمج، في المرة القادمة التي تواجه فيها مشكلة أداء بسبب الاستعلامات المتكررة، تذكر قصة أبو عمر، وفكر: هل يمكن لمرشح بلوم أن يكون بطلك القادم؟ 🚀

أبو عمر

سجل دخولك لعمل نقاش تفاعلي

كافة المحادثات خاصة ولا يتم عرضها على الموقع نهائياً

آراء من النقاشات

لا توجد آراء منشورة بعد. كن أول من يشارك رأيه!

آخر المدونات

برمجة وقواعد بيانات

تحديثات قاعدة البيانات بدون توقف: كيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من جحيم التوقفات المجدولة؟

هل سئمت من إيقاف الخدمة مع كل تحديث لهيكلة قاعدة البيانات؟ أشارككم قصة حقيقية وكيف أنقذنا نمط التوسيع والتعاقد (Expand/Contract) من ليالي النشر الطويلة والمُجهدة،...

4 يونيو، 2026 قراءة المزيد
الشبكات والـ APIs

كانت إعادة المحاولة كارثة: كيف أنقذتنا مفاتيح عدم تكرار العمليات (Idempotency Keys) من جحيم الفواتير المزدوجة؟

أشارككم قصة حقيقية من الخنادق البرمجية، يوم كاد خطأ بسيط في إعادة محاولة طلبات الدفع أن يكلفنا سمعتنا وأموال عملائنا. اكتشفوا معنا كيف كانت مفاتيح...

4 يونيو، 2026 قراءة المزيد
الحوسبة السحابية

من التوقف التام إلى النجاة: كيف أنقذتنا استراتيجية “الضوء المرشد” (Pilot Light) يوم انقطعت السحابة؟

أتذكر ذلك اليوم جيدًا، فنجان القهوة الصباحي، وصوت تنبيهات المراقبة يصرخ كأنه يوم القيامة. كانت منطقة سحابية كاملة قد توقفت عن العمل، لكن بفضل استراتيجية...

4 يونيو، 2026 قراءة المزيد
التوظيف وبناء الهوية التقنية

كانت مهمتي البرمجية للاختبار مجرد كود: كيف أنقذني توثيق القرارات من جحيم الصمت بعد المقابلة؟

أشارككم قصة حقيقية من بداياتي، وكيف تعلمت بالطريقة الصعبة أن المهمة البرمجية ليست مجرد كتابة كود، بل هي فرصة لإظهار طريقة تفكيرك. اكتشف كيف يمكن...

4 يونيو، 2026 قراءة المزيد
التكنلوجيا المالية Fintech

من الانتظار لأيام إلى الدفع في ثوانٍ: كيف أنقذتنا شبكات الدفع الفوري من جحيم التحويلات البنكية؟

أسرد لكم من واقع تجربتي كـ "أبو عمر"، كيف عانينا من بطء وتكلفة التحويلات البنكية الدولية، وكيف جاءت شبكات الدفع الفوري ومعيار ISO 20022 لتكون...

4 يونيو، 2026 قراءة المزيد
البنية التحتية وإدارة السيرفرات

كان كل خادم لدينا ‘ندفة ثلج’ فريدة: كيف أنقذنا ‘الكود كبنية تحتية’ (IaC) من جحيم الانجراف اليدوي؟

في هذه المقالة، أشارككم قصة حقيقية من قلب المعركة التقنية مع "خوادم ندفات الثلج" الفوضوية. سنغوص في مفهوم "الكود كبنية تحتية" (IaC) وكيف أن أدوات...

4 يونيو، 2026 قراءة المزيد
اختبارات الاداء والجودة

كانت تغطية الاختبارات 100% لكن الأخطاء تتسرب: كيف أنقذنا “الاختبار الطفري” من جحيم الثقة الزائفة؟

كنا نظن أن تغطية الاختبار بنسبة 100% هي درعنا الواقي، لكن الأخطاء كانت تتسلل إلى الإنتاج كاللصوص في ليل بهيم. اكتشف كيف أنقذنا "الاختبار الطفري"...

4 يونيو، 2026 قراءة المزيد
البودكاست