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

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

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

قاعدة البيانات كانت حرفياً تستجدي الرحمة، وكل استعلام جديد كان زي طعنة إضافية في ظهرها المنهك. جربنا كل الحلول التقليدية: تحسين الفهارس (Indexes)، عمل Cache للاستعلامات الشائعة… كله كان مجرد مسكّنات مؤقتة. لحد ما واحد من الشباب، وهو بيحتسي كاسة شاي بالنعنع، حكى جملة غيرت كل إشي: “يا جماعة، ليش ما نستخدم إشي يفلترلنا الطلبات قبل ما توصل الداتابيز أصلاً؟ زي حارس أمن على الباب، ما بدخّل إلا اللي معه تصريح”. وقتها لمعت في بالي فكرة كنت قرأت عنها زمان في إحدى أوراق البحث: مرشح بلوم (Bloom Filter).


ما هو هذا الـ ‘مرشح بلوم’؟ وليش هو الساحر اللي احتجناه؟

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

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

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

فهمتوا اللعبة؟ بدل ما نسأل قاعدة البيانات 100% من المرات، صرنا نسألها يمكن 1% أو 2% من المرات فقط! هذا هو التوفير الجبار اللي كنا بنحلم فيه.

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

كيف يعمل مرشح بلوم من الداخل؟ (بدون تعقيد، وعد!)

الفكرة عبقرية في بساطتها. الموضوع كله قائم على مكونين رئيسيين:

  1. مصفوفة بتات (Bit Array): تخيلها سلسلة طويلة من الأصفار، مثلاً بحجم 1000 خانة، وكلها في البداية قيمتها صفر.
  2. عدة دوال هاش (Hash Functions): وظيفتها تاخذ أي مدخل (زي اسم مستخدم مثلاً) وتحوله لرقم ضمن نطاق حجم المصفوفة. مثلاً 3 دوال هاش مختلفة.

المرحلة الأولى: إضافة عنصر جديد (مثلاً، اسم مستخدم “abu_omar”)

  1. نأخذ الاسم “abu_omar” ونمرره على دوال الهاش الثلاثة.
  2. كل دالة هاش بتعطينا رقم (موقع) مختلف في المصفوفة. لنفرض النتائج كانت: 150، 480، 812.
  3. نروح على مصفوفة البتات، وفي المواقع 150، 480، و 812، بنغير القيمة من 0 إلى 1.

وهيك بنكون “سجلنا” وجود “abu_omar”. بنكرر هاي العملية لكل مستخدم جديد بيسجل عنا.

// مصفوفة البتات قبل الإضافة
[0, 0, 0, ..., 0, 0, 0, ..., 0, 0, 0, ..., 0]

// إضافة "abu_omar"
hash1("abu_omar") -> 150
hash2("abu_omar") -> 480
hash3("abu_omar") -> 812

// مصفوفة البتات بعد الإضافة
[0, ..., 1, ..., 1, ..., 1, ..., 0]
         ^       ^       ^
        150     480     812

المرحلة الثانية: التحقق من وجود عنصر (مثلاً، هل “user123” موجود؟)

  1. نأخذ الاسم “user123” ونمرره على نفس دوال الهاش الثلاثة.
  2. لنفترض أن النتائج كانت: 200، 550، 900.
  3. نروح على مصفوفة البتات ونتحقق من القيم في المواقع 200، 550، و 900.
  4. إذا كانت كل القيم في هذه المواقع تساوي 1، فبنحكي: “العنصر محتمل أن يكون موجوداً”.
  5. إذا كانت قيمة واحدة (أو أكثر) منهم تساوي 0، فبنحكي بيقين 100%: “العنصر بالتأكيد غير موجود”.

الإيجابية الكاذبة (False Positive) بتصير لما مواقع البتات الخاصة بعنصر جديد تكون كلها تحولت لـ 1 بسبب عناصر أخرى تمت إضافتها قبله. وهذا احتماله قليل جداً إذا اخترت حجم المصفوفة وعدد دوال الهاش بشكل صحيح.


يلا نكتب كود: مثال عملي بلغة بايثون

الحكي النظري حلو، بس إحنا المبرمجين بنحب نشوف الكود. خلينا نشوف مثال بسيط باستخدام لغة بايثون ومكتبة مشهورة اسمها pybloom_live.

أولاً، ستحتاج إلى تثبيت المكتبة:

pip install pybloom_live

ثم يمكنك استخدامها كالتالي:


from pybloom_live import BloomFilter

# لنفترض أن لدينا 100 ألف مستخدم، ونريد نسبة خطأ (false positive) حوالي 1%
# المكتبة تساعدنا في حساب الحجم المناسب للمرشح
capacity = 100000
error_rate = 0.01

# إنشاء مرشح بلوم
user_filter = BloomFilter(capacity=capacity, error_rate=error_rate)

# لنقم بإضافة بعض المستخدمين الموجودين بالفعل في قاعدة البيانات
existing_users = ["ahmad", "fatima", "omar", "sara", "khaled"]
for user in existing_users:
    print(f"Adding '{user}' to the filter.")
    user_filter.add(user)

print("-" * 20)

# الآن، لنتحقق من بعض أسماء المستخدمين
users_to_check = ["ahmad", "omar", "john", "layla"]

for user in users_to_check:
    if user in user_filter:
        # المرشح يقول "ربما يكون موجوداً"، لذلك يجب التحقق من قاعدة البيانات
        print(f"Checking for '{user}': Filter says MAYBE. Must check DB.")
        # هنا تضع الكود الذي يتصل بقاعدة البيانات للتحقق الفعلي
        if user in existing_users: # محاكاة للتحقق من قاعدة البيانات
            print(f"-> DB Confirmed: '{user}' exists.")
        else:
            # هذا هو مثال على الإيجابية الكاذبة (نادر الحدوث)
            print(f"-> DB Confirmed: '{user}' does NOT exist. (False Positive!)")
    else:
        # المرشح يقول "بالتأكيد غير موجود"، لا داعي لإرهاق قاعدة البيانات
        print(f"Checking for '{user}': Filter says DEFINITELY NOT. No need to check DB.")

إذا قمت بتشغيل هذا الكود، ستلاحظ أن “ahmad” و “omar” سيتم التعرف عليهما على أنهما “ربما موجودان” (وهو صحيح). أما “john” و “layla”، فسيتم رفضهما فوراً، مما يوفر علينا استعلامين لقاعدة البيانات. لو كان لدينا مليون مستخدم، تخيل حجم التوفير!


نصائح أبو عمر الذهبية لاستخدام مرشحات بلوم

من خبرتي في الميدان، تعلمت كم درس مهم عن هذا “الإشي” العبقري. خذوها مني نصائح عملية:

  • اعرف متى تستخدمه: أفضل استخدام لمرشح بلوم هو في سيناريوهات “التحقق من الوجود” قبل القيام بعملية مكلفة (مثل استعلام قاعدة بيانات، أو طلب عبر الشبكة). أمثلة: التحقق من توفر اسم مستخدم، منع نشر محتوى مكرر، التحقق مما إذا كان الرابط قد تمت زيارته من قبل (web crawler).
  • اعرف متى لا تستخدمه: إذا كنت لا تستطيع تحمل أي نسبة خطأ (Zero tolerance for false positives)، أو إذا كنت بحاجة إلى حذف عناصر من المجموعة، فإن مرشح بلوم القياسي ليس لك. (هناك بدائل مثل Counting Bloom Filter أو Cuckoo Filter تسمح بالحذف، لكنها أكثر تعقيداً).
  • المقاس مهم (Size Matters): حجم مصفوفة البتات وعدد دوال الهاش يحددان نسبة الإيجابيات الكاذبة. كلما زاد الحجم وقلت نسبة الخطأ المطلوبة، زادت الذاكرة المستخدمة. لا تخف، لست بحاجة لحسابها يدوياً. استخدم الآلات الحاسبة المتوفرة على الإنترنت (ابحث عن “Bloom Filter Calculator”) لتحديد الأرقام المثلى لتطبيقك.
  • احتفظ به في الذاكرة (In-Memory): جمال مرشح بلوم يكمن في سرعته، وهذا لأنه عادةً ما يتم الاحتفاظ به بالكامل في ذاكرة الوصول العشوائي (RAM). تأكد من أن لديك ذاكرة كافية لذلك. يمكنك أيضاً حفظ حالته على القرص الصلب واستعادته عند إعادة تشغيل التطبيق.

الخلاصة: مش كل مشكلة حلها مطرقة! 🔨

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

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

نصيحتي لكم: لا تتوقفوا عن التعلم واستكشاف هياكل البيانات والخوارزميات المختلفة. في المرة القادمة التي تواجهون فيها مشكلة أداء، تذكروا قصة أبو عمر، واسألوا أنفسكم: “هل أنا أستخدم الأداة الصحيحة لهذه المهمة؟”.

ودمتم مبدعين! ☕️

أبو عمر

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

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

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

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

آخر المدونات

ذكاء اصطناعي

نماذجنا اللغوية كانت تهذي! كيف أنقذنا الذكاء الاصطناعي من الهلوسة بتقنية RAG؟

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

28 مايو، 2026 قراءة المزيد
تسويق رقمي

كانت حملاتنا تصرخ في الفراغ: كيف أنقذتنا ‘التجزئة الديناميكية بالتعلم الآلي’ من جحيم العروض غير الملائمة؟

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

28 مايو، 2026 قراءة المزيد
تجربة المستخدم والابداع البصري

كان تطبيقنا يستبعد 15% من المستخدمين بصمت: كيف أنقذتنا ‘إمكانية الوصول الرقمية’ (a11y) من كارثة التصميم؟

كنا نظن أن تطبيقنا ناجح، حتى اكتشفنا أننا كنا نستبعد 15% من مستخدمينا بصمت. هذه قصتي كـ 'أبو عمر' وكيف غيرت 'إمكانية الوصول الرقمية' (a11y)...

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

ما وراء البحث التقليدي: كيف تُمكّن قواعد بيانات المتجهات تطبيقات الذكاء الاصطناعي من فهم المعنى؟

في هذه المقالة، يأخذكم أبو عمر في رحلة من المطبخ الفلسطيني إلى عالم الذكاء الاصطناعي، لنكتشف معًا كيف غيرت قواعد بيانات المتجهات (Vector Databases) مفهوم...

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

كانت فواتيرنا السحابية لغزاً محيراً: كيف أنقذتنا ثقافة FinOps من جحيم الإنفاق غير المنضبط؟

في هذه المقالة، أشارككم قصة حقيقية من قلب المعاناة مع فواتير الحوسبة السحابية الباهظة، وكيف استطعنا كفريق أن نتبنى ثقافة الـ FinOps لتحويل الفوضى إلى...

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

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

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

27 مايو، 2026 قراءة المزيد
التوسع والأداء العالي والأحمال

كانت طلباتنا تضيع في الزحام: كيف أنقذتنا طوابير الرسائل (Message Queues) من جحيم الانهيار؟

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

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