পাঠ ৩৩ · ৫১-এর মধ্যে · মডিউল ৮
Home / Courses / System Design / ব্লুম ফিল্টার

ব্লুম ফিল্টার

Bloom filters
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • "সেটে আইটেম আছে কি না" প্রশ্নের জন্য পুরো সেট স্টোর না করেই উত্তর দেওয়ার সমস্যা
  • ব্লুম ফিল্টারের গঠন — বিট অ্যারে + k-টি হ্যাশ ফাংশন, ADD ও CHECK অপারেশন কীভাবে কাজ করে
  • কেন false positive সম্ভব কিন্তু false negative কখনোই সম্ভব নয় — এবং কেন এটাই আসল দরকারি প্রপার্টি
  • Python-এ hashlib দিয়ে একটি বাস্তব ব্লুম ফিল্টার বানানো ও যাচাই করা

১ · সমস্যা — মেমরি না বাড়িয়ে মেম্বারশিপ চেক করা

ধরুন একটি ওয়েব ক্রলার ১০০ কোটি URL ইতিমধ্যে ক্রল করেছে, এবং প্রতিটি নতুন URL দেখার আগে জিজ্ঞেস করতে চায় — "এটি কি আগে ক্রল করা হয়েছে?" প্রতিটি URL-কে একটি set-এ রাখলে নিখুঁত উত্তর পাওয়া যাবে, কিন্তু ১০০ কোটি URL স্টোর করতে বিশাল মেমরি লাগবে — এবং প্রতিটি চেকের আগে ডিস্কে থাকা ডেটাবেসে গিয়ে লুকআপ করা ধীরগতির।

আমাদের এমন একটি কাঠামো দরকার যা খুব কম মেমরিতে "সম্ভবত আছে" বনাম "নিশ্চিতভাবে নেই" এই দুটোর মধ্যে পার্থক্য করতে পারে — নিখুঁত না হলেও চলবে, যতক্ষণ না এটি "নেই" বলে এমন কিছু আসলে থেকে যায় (যা একটি গুরুতর ভুল হবে)।

২ · ব্লুম ফিল্টার — বিট অ্যারে + একাধিক হ্যাশ ফাংশন

ব্লুম ফিল্টারBloom Filterএকটি m সাইজের বিট অ্যারে এবং k-টি হ্যাশ ফাংশন দিয়ে তৈরি একটি স্পেস-এফিশিয়েন্ট প্রোবাবিলিস্টিক ডেটা স্ট্রাকচার — কোনো আইটেম সেটে "থাকতে পারে" নাকি "নিশ্চিতভাবে নেই" তা যাচাই করে, কখনো false negative না দিয়ে। একটি সাইজ-m বিট অ্যারে দিয়ে শুরু হয় (সব বিট প্রথমে ০), সাথে k-টি ভিন্ন হ্যাশ ফাংশন।

ADD(item)
আইটেমটিকে k-টি হ্যাশ ফাংশন দিয়ে হ্যাশ করে k-টি বিট পজিশন বের করা হয়, এবং প্রতিটি পজিশনের বিট ১ সেট করা হয়।
CHECK(item)
একই k-টি পজিশন বের করে দেখা হয় — যদি সবগুলো বিট ১ হয়, আইটেম "possibly present"। যদি যেকোনো একটি বিট ০ হয়, আইটেম "definitely not present"।
কেন false positive হয়, কিন্তু false negative হয় না

যখন একাধিক ভিন্ন আইটেম ADD করা হয়, তাদের হ্যাশ পজিশনগুলো ওভারল্যাপ করতে পারে — অর্থাৎ কোনো আইটেম কখনো ADD না করা হলেও, শুধু অন্য আইটেমদের সেট করা বিটের "কাকতালীয়" মিলের কারণে তার সবগুলো পজিশনই ১ হয়ে যেতে পারে। এটাই false positive। কিন্তু যদি কোনো আইটেম সত্যিই ADD করা হয়ে থাকে, তার সবগুলো বিট নিশ্চিতভাবে ১ হবে (কারণ ADD করার সময়ই সেট করা হয়েছিল) — তাই CHECK কখনোই একটি সত্যিকারের সদস্যকে "নেই" বলবে না। এই কারণেই false negative কখনোই ঘটে না।

আইটেম item k-টি হ্যাশ ফাংশন hash₁, hash₂, hash₃ বিট পজিশন সেট set bits to 1 চেক আইটেম check item সব বিট কি ১? all k bits = 1? না → definitely NOT present
উপরের সারি: ADD অপারেশন বিট সেট করে। নিচের সারি: CHECK অপারেশন — সব বিট ১ হলে উপরের দিকে "possibly present" (একই বিট অ্যারে ব্যবহার করে), না হলে নিশ্চিতভাবে অনুপস্থিত।

৩ · Python-এ একটি বাস্তব ব্লুম ফিল্টার

নিচের কোডে k=৩টি হ্যাশ ফাংশন hashlib-এর তিনটি ভিন্ন অ্যালগরিদম (md5, sha1, sha256) থেকে তৈরি করা হয়েছে, প্রতিটির হেক্স ডাইজেস্টকে ইন্টিজারে রূপান্তর করে m দিয়ে মড নেওয়া হয়েছে। প্রথমে কিছু আইটেম ADD করে সেগুলোকেই CHECK করা হবে (false negative শূন্য হতেই হবে), তারপর সম্পূর্ণ ভিন্ন কিছু আইটেম CHECK করা হবে (এখানে মাঝেমধ্যে false positive আসতে পারে — এটাই প্রত্যাশিত)।

Python
import hashlib

m = 20   # বিট অ্যারের সাইজ (ইচ্ছাকৃতভাবে ছোট রাখা হলো যাতে false positive দেখা যায়)
bit_array = [0] * m

def positions(item):
    encoded = item.encode()
    p1 = int(hashlib.md5(encoded).hexdigest(), 16) % m
    p2 = int(hashlib.sha1(encoded).hexdigest(), 16) % m
    p3 = int(hashlib.sha256(encoded).hexdigest(), 16) % m
    return [p1, p2, p3]

def add(item):
    for p in positions(item):
        bit_array[p] = 1

def might_contain(item):
    return all(bit_array[p] == 1 for p in positions(item))

added_items = ["url:example.com/a", "url:example.com/b", "url:example.com/c",
               "url:example.com/d", "url:example.com/e"]

for item in added_items:
    add(item)

false_negatives = 0
print("যেসব আইটেম ADD করা হয়েছে তাদের চেক করা:")
for item in added_items:
    result = might_contain(item)
    print(f"  {item}: {'possibly present' if result else 'definitely NOT present'}")
    if not result:
        false_negatives += 1

test_items = ["url:example.com/x", "url:example.com/y", "url:example.com/z",
              "url:example.com/q", "url:example.com/r", "url:example.com/s",
              "url:example.com/t", "url:example.com/u"]

false_positives = 0
print("\nযেসব আইটেম ADD করা হয়নি তাদের চেক করা:")
for item in test_items:
    result = might_contain(item)
    print(f"  {item}: {'possibly present (false positive!)' if result else 'definitely NOT present'}")
    if result:
        false_positives += 1

print(f"\nফলস নেগেটিভ (কখনো হওয়ার কথা নয়): {false_negatives}")
print(f"ফলস পজিটিভ (মাঝেমধ্যে হতে পারে): {false_positives} / {len(test_items)}")

    
লক্ষ্য করুন — ADD করা সব আইটেম সবসময় "possibly present" দেখায় (false negative = ০), যেমনটা গ্যারান্টিড। অ-সদস্য আইটেমগুলোর মধ্যে বেশিরভাগ সঠিকভাবে "definitely NOT present" দেখায়, তবে m ছোট রাখায় (মাত্র ২০ বিট, ৫টি আইটেমের জন্য) মাঝেমধ্যে একটি false positive দেখা যেতে পারে — এটিই দেখায় কেন বাস্তব সিস্টেমে প্রত্যাশিত আইটেম সংখ্যা অনুযায়ী m (এবং k) যথাযথভাবে বড় রাখা জরুরি।
মূল কথা · Key takeaway

ব্লুম ফিল্টার একটি ক্লাসিক স্পেস-বনাম-নিখুঁততা ট্রেড-অফ — সম্পূর্ণ সেট স্টোর না করেই "নিশ্চিতভাবে নেই" এই উত্তরটি বিশ্বাসযোগ্যভাবে দেয়, যা একটি ব্যয়বহুল ডিস্ক/ডেটাবেস লুকআপ এড়াতে যথেষ্ট। "সম্ভবত আছে" উত্তর এলে তখনই আসল লুকআপ চালানো হয় — অর্থাৎ ব্লুম ফিল্টার প্রতিস্থাপন নয়, বরং একটি দ্রুত প্রি-ফিল্টার।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ ব্লুম ফিল্টার থেকে কখনো কোনো আইটেম "মুছে ফেলা" (delete) যায় না কেন? একটি নিয়মিত বিট-অ্যারে থেকে সরাসরি বিট ০ করে দিলে কী সমস্যা হবে?

কারণ একটি বিট পজিশন একাধিক আইটেমের হ্যাশের মধ্যে শেয়ার্ড হতে পারে (কোল্লিশন)। যদি আইটেম A ডিলিট করার সময় তার একটি বিট ০ করে দেওয়া হয়, কিন্তু সেই একই বিট পজিশন যদি আইটেম B-এরও একটি হ্যাশ পজিশন হয়ে থাকে, তাহলে B-কে ভুলভাবে "definitely not present" দেখাবে — এটি একটি false negative তৈরি করবে, যা ব্লুম ফিল্টারের মূল গ্যারান্টি ভঙ্গ করে। ডিলিট সাপোর্ট করতে হলে একটি ভিন্ন ভ্যারিয়েন্ট — কাউন্টিং ব্লুম ফিল্টার (প্রতিটি পজিশনে বিট না রেখে একটি কাউন্টার রাখা) দরকার হয়।

প্র ০২ একটি ব্লুম ফিল্টারে বেশি আইটেম ADD করতে থাকলে false positive rate কী হয় — বাড়ে, কমে, নাকি অপরিবর্তিত থাকে? কেন?

বাড়ে। প্রতিটি ADD অপারেশন বিট অ্যারেতে আরও বিট ১ সেট করে দেয় — যত বেশি বিট ১ হয়, ততই সম্ভাবনা বাড়ে যে কোনো অ-সদস্য আইটেমের হ্যাশ পজিশনগুলো কাকতালীয়ভাবে সবই ইতিমধ্যে ১ হয়ে থাকবে। এই কারণেই বাস্তব ব্যবহারে m (বিট অ্যারের সাইজ) ও k (হ্যাশ ফাংশনের সংখ্যা) প্রত্যাশিত আইটেম সংখ্যার সাথে মিলিয়ে আগে থেকেই হিসাব করে নির্ধারণ করা হয়, যাতে false positive rate একটি গ্রহণযোগ্য সীমার (যেমন ১%) মধ্যে থাকে।

প্র ০৩ ডেটাবেস সিস্টেমে (যেমন Cassandra) ব্লুম ফিল্টার কোথায় ব্যবহৃত হয় এবং কেন সেটি একটি স্বাভাবিক ফিট?

Cassandra-এর মতো LSM-tree-ভিত্তিক ডেটাবেসে ডেটা একাধিক ডিস্ক ফাইলে (SSTable) ছড়িয়ে থাকে। একটি কী খুঁজতে প্রতিটি SSTable ডিস্ক থেকে পড়ে চেক করা খুবই ধীরগতির। প্রতিটি SSTable-এর জন্য একটি ব্লুম ফিল্টার মেমরিতে রাখা হয় — একটি কী খোঁজার আগে ফিল্টারে চেক করা হয়; ফিল্টার "definitely not present" বললে সেই SSTable-টি সম্পূর্ণ স্কিপ করা যায় (কোনো ডিস্ক I/O ছাড়াই), আর "possibly present" বললে তখনই আসল ডিস্ক লুকআপ চালানো হয়। এভাবে বেশিরভাগ অপ্রয়োজনীয় ডিস্ক রিড এড়ানো যায়।

অনুশীলন

  1. পরিবর্তন করুন: উপরের কোড সেলে m-এর মান ২০ থেকে ২০০ করে দিন এবং false positive সংখ্যা কীভাবে বদলায় লক্ষ করুন।

    m = 200 করলে বিট অ্যারেতে অনেক বেশি খালি (০) পজিশন থাকবে, তাই একই ৫টি আইটেম ADD করলেও বিট ঘনত্ব (কতগুলো বিট ১) অনেক কমে যাবে — ফলে অ-সদস্য আইটেমদের হ্যাশ পজিশন কাকতালীয়ভাবে সবই ১ হওয়ার সম্ভাবনা কমে যায়, এবং সাধারণত false positive সংখ্যা ০-তে নেমে আসে। এটাই দেখায় m বাড়ানো সরাসরি false positive rate কমায়, স্পেস খরচের বিনিময়ে।

  2. চিন্তা করুন: যদি একটি ব্লুম ফিল্টারের সবগুলো বিট ১ হয়ে যায় (সম্পূর্ণ পূর্ণ), তাহলে CHECK কী রিটার্ন করবে যেকোনো আইটেমের জন্য — এবং এতে ফিল্টারটি কি এখনো "সঠিক" (correct) থাকে?

    সব বিট ১ হয়ে গেলে CHECK সবসময় "possibly present" রিটার্ন করবে — এমনকি এমন আইটেমের জন্যও যা কখনো ADD করা হয়নি। এটি এখনো টেকনিক্যালি "সঠিক" (কোনো false negative নেই), কিন্তু সম্পূর্ণ অকেজো — ১০০% false positive rate হলে ফিল্টার আর কোনো তথ্যই দিচ্ছে না। এই কারণেই m ও k সঠিকভাবে সাইজ করা এত গুরুত্বপূর্ণ — একটি খুব ছোট বা ওভারলোডেড ফিল্টার তার পুরো উপযোগিতা হারিয়ে ফেলে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
গসিপ প্রোটোকল