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

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

اسمحولي اليوم أحكيلكم قصة صارت معي ومع فريقي قبل كم سنة، قصة علّمتنا درس قاسي ومهم في نفس الوقت. كنا وقتها شغالين على نظام فيه تسجيل مستخدمين، زي أي نظام طبيعي. وكان في ميزة بسيطة جداً لكنها كانت سبب صداعنا الأكبر: التحقق من توفر اسم المستخدم (Username) بشكل فوري وهو المستخدم بيكتب.

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

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

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

ما هو “مرشح بلوم”؟ وليش هو مش مجرد هيكل بيانات عادي؟

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

لاحظوا الدقة في الكلام:

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

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

الفائدة الجبارة هنا: الحارس (مرشح بلوم) منع 99% من الناس غير المدعوين من إزعاج موظف الاستقبال (قاعدة البيانات) الغالي والمشغول!

كيف يعمل هذا السحر من الداخل؟

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

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

مرحلة الإضافة (لما نضيف عنصر للمجموعة)

لما بدك تضيف عنصر جديد (مثلاً، اسم مستخدم تم حجزه “omar123”) للمرشح، بتعمل الآتي:

  1. بتمرر العنصر “omar123” على كل دالة من دوال التجزئة الـ k.
  2. كل دالة راح تعطيك رقم (index) معين في مصفوفة البتات.
  3. بتروح على هاي المواقع في المصفوفة وبتحول قيمتها من 0 إلى 1.

وهيك بنكون “سجلنا” وجود العنصر في المرشح، بس بشكل مشفر وغامض.

مرحلة التحقق (لما نسأل “هل العنصر موجود؟”)

الآن، لما يجي مستخدم جديد ويجرب يكتب “omar123″، وقبل ما نروح نسأل قاعدة البيانات، بنسأل مرشح بلوم أولاً:

  1. بنمرر العنصر “omar123” على نفس دوال التجزئة الـ k.
  2. كل دالة بتعطينا رقم (index).
  3. بنروح على هاي المواقع في مصفوفة البتات وبنتفحص قيمتها.
  4. وهنا مربط الفرس:
    • إذا وجدنا ولو بت واحد فقط قيمته 0، فهذا يعني أن هذا العنصر مستحيل يكون قد أُضيف من قبل. ليش؟ لأنه لو كان أُضيف، لكانت كل البتات المقابلة له قد تحولت إلى 1. هنا نرجع للمستخدم فوراً ونقول له “الاسم متاح” بدون ما نلمس قاعدة البيانات. (No False Negatives)
    • إذا وجدنا أن كل البتات في المواقع المحددة قيمتها 1، فهنا نقول أن العنصر “قد يكون” موجوداً. ليش “قد يكون”؟ لأنه ممكن عنصر آخر أو مجموعة عناصر أخرى، بالصدفة، تكون قد حولت نفس هذه البتات إلى 1. هذا هو الـ False Positive. في هذه الحالة فقط، نذهب ونسأل قاعدة البيانات للتأكد النهائي.

بفضل هذه الآلية، استطعنا تصفية أكثر من 99% من الاستعلامات غير الضرورية لقاعدة البيانات. فقط الـ 1% المشكوك في أمرها هي التي كانت تصل لمرحلة التحقق النهائي.

ورينا الكود يا أبو عمر!

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


# First, install the library: pip install py-bloom-filter
from bloom_filter import BloomFilter

# لنفترض أننا نتوقع وجود مليون اسم مستخدم في نظامنا
# ونريد نسبة خطأ (false positive) لا تتجاوز 0.1%
# المكتبة ستقوم بحساب حجم المصفوفة وعدد دوال التجزئة الأمثل
capacity = 1000000
error_rate = 0.001

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

# لنفترض أن لدينا بعض أسماء المستخدمين المحجوزة في قاعدة البيانات
# سنقوم بإضافتها إلى مرشح بلوم عند بدء تشغيل التطبيق
existing_users = ["abu_omar", "ahmad_dev", "fatima_coder", "user1234"]

print("Adding existing users to the Bloom Filter...")
for user in existing_users:
    username_filter.add(user)
    print(f"- Added '{user}'")

print("n--- Now, let's check for new usernames ---")

# الحالة الأولى: مستخدم يحاول حجز اسم موجود بالفعل
new_attempt_1 = "abu_omar"
if new_attempt_1 in username_filter:
    print(f"Checking '{new_attempt_1}': Filter says 'Probably Exists'. Must check DB. (Correct)")
    # في الواقع، هنا نقوم باستعلام قاعدة البيانات للتأكيد
else:
    # هذه الحالة لن تحدث للعناصر الموجودة
    print(f"Checking '{new_attempt_1}': Filter says 'Definitely Not Exists'. (Incorrect - this won't happen)")

# الحالة الثانية: مستخدم يحاول حجز اسم جديد وغير موجود
new_attempt_2 = "sami_gamer"
if new_attempt_2 in username_filter:
    # هذا هو الـ False Positive - قد يحدث نادراً
    print(f"Checking '{new_attempt_2}': Filter says 'Probably Exists'. Must check DB. (This is a potential False Positive)")
else:
    # هذه هي الحالة المثالية والأكثر شيوعاً
    print(f"Checking '{new_attempt_2}': Filter says 'Definitely Not Exists'. Username is available! (Correct, and no DB query needed!)")

# الحالة الثالثة: اسم آخر جديد
new_attempt_3 = "developer_palestine"
if new_attempt_3 in username_filter:
    print(f"Checking '{new_attempt_3}': Filter says 'Probably Exists'. Must check DB. (False Positive)")
else:
    print(f"Checking '{new_attempt_3}': Filter says 'Definitely Not Exists'. Username is available! (Correct, and no DB query needed!)")

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

نصائح من خبرتي (الزبدة)

بعد ما استخدمنا مرشح بلوم في مشاريع مختلفة، تعلمت كم شغلة بحب أشارككم فيها:

  • لا تبنِ المرشح بنفسك في بيئة الإنتاج: الفكرة النظرية بسيطة، لكن اختيار حجم المصفوفة (m) وعدد دوال التجزئة (k) بشكل أمثل هو علم بحد ذاته ويعتمد على معادلات. استخدم مكتبة موثوقة تقوم بهذه الحسابات عنك. كل ما عليك هو تحديد السعة المتوقعة (كم عنصر ستخزن) ونسبة الخطأ المقبولة (False Positive Rate).
  • نسبة الخطأ هي قرار عملي (Business Decision): اجلس مع فريقك أو مدير المنتج. ما هي نسبة الخطأ المقبولة؟ 1%؟ 0.01%؟ كلما قلت نسبة الخطأ المطلوبة، زاد حجم مرشح بلوم في الذاكرة. إنها مقايضة. في حالتنا، كانت نسبة 1% مقبولة جداً، لأن تكلفة استعلام إضافي خاطئ لقاعدة البيانات كانت قليلة جداً مقارنة بالفائدة الهائلة من منع 99% من الاستعلامات الصحيحة.
  • مرشح بلوم لا يدعم الحذف: في تصميمه الأساسي، لا يمكنك حذف عنصر من مرشح بلوم. لأنك لو حولت بت من 1 إلى 0، قد تكون “أفسدت” تسجيل عنصر آخر يعتمد على نفس البت. هناك أنواع متقدمة مثل (Counting Bloom Filter) تدعم الحذف ولكنها أكثر تعقيداً وتستهلك ذاكرة أكبر.
  • استخداماته لا حصر لها: لا تفكر فقط في أسماء المستخدمين. يمكن استخدامه في أي سيناريو “هل رأيت هذا من قبل؟”:
    • أنظمة تتبع الزحف (Web Crawlers): لتجنب زيارة نفس الرابط مرتين.
    • قواعد البيانات: بعض قواعد البيانات مثل Cassandra و RocksDB تستخدمه داخلياً لتقليل عمليات القراءة من القرص.
    • شبكات توزيع المحتوى (CDNs): للتحقق مما إذا كان ملف معين موجود في الكاش السلبي (Negative Cache) لتجنب البحث عنه في الخادم الأصلي.

الخلاصة 💡

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

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

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

أبو عمر

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

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

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

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

آخر المدونات

اختبارات الاداء والجودة

تغطية اختبارات 100% وكود مليء بالعلل: كيف أنقذنا “الاختبار الطفري” من الثقة الزائفة

كنا نظن أن تغطية الاختبارات بنسبة 100% هي درعنا الحصين، لكن الواقع كان صادماً. اكتشف كيف كشف لنا "الاختبار الطفري" (Mutation Testing) ضعف اختباراتنا وأنقذ...

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

مراجعات الكود حلبة مصارعة؟ كيف أنقذتنا خطاطيف ما قبل الإيداع (Pre-commit Hooks) من جحيم الجدالات

أتذكرون تلك الأيام التي كانت فيها مراجعات الكود ساحة للجدل حول الفواصل المنقوطة والمسافات؟ في هذه المقالة، أشارككم قصة كيف أنقذتنا أداة بسيطة تُدعى "خطاطيف...

23 مايو، 2026 قراءة المزيد
نصائح برمجية

كانت دوالنا البرمجية هرمًا من الجحيم: كيف أنقذتنا ‘الشروط الحارسة’ (Guard Clauses) من فوضى الـ if المتداخلة؟

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

23 مايو، 2026 قراءة المزيد
ذكاء اصطناعي

كانت نماذجنا تلتهم موارد السيرفر: كيف أنقذنا ‘تكميم النماذج’ (Model Quantization) من جحيم فواتير الحوسبة؟

أشارككم قصة حقيقية من قلب المعركة مع فواتير الحوسبة السحابية، وكيف كانت تقنية "تكميم النماذج" (Model Quantization) هي طوق النجاة الذي أنقذنا. سنتعلم معاً كيف...

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

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

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

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