Fundamentals১১ মিনিট পাঠ

ক্যাশিং (Caching)

প্রয়োজনীয় জিনিস কাছে রাখুন — ধীরগতির যাত্রা এড়িয়ে চলুন
scope:বিল্ডিং ব্লকdifficulty:শিক্ষানবিশ-মাঝারি

কেন ক্যাশ করবেন? (Why Cache?)

কল্পনা করুন আপনার প্রিয় বিস্কুট বা চানাচুর রান্নাঘরের বয়ামে রাখা আছে। প্রতিবার যখন আপনার একটু খেতে ইচ্ছে করবে, তখন আপনাকে হেঁটে রান্নাঘরে যেতে হবে, বয়াম খুলতে হবে, কিছুটা নিতে হবে এবং আবার নিজের ঘরে ফিরে আসতে হবে। এটি বেশ ক্লান্তিকর।

এখন কল্পনা করুন আপনি পুরো বয়ামটিই আপনার ডেস্কে নিয়ে এসেছেন। এটাই হলো ক্যাশিং।

সিস্টেম ডিজাইনে, ক্যাশ হলো একটি দ্রুতগামী, অস্থায়ী স্টোরেজ লেয়ার যা আপনার অ্যাপ্লিকেশন এবং ধীরগতির ডেটা সোর্স (সাধারণত একটি ডেটাবেস) এর মাঝখানে থাকে। প্রতিটি রিকোয়েস্টের জন্য ডেটাবেসে যাওয়ার পরিবর্তে, আপনি প্রথমে ক্যাশ চেক করেন। যদি ডেটা সেখানে থাকে (ক্যাশ হিট — cache hit), তবে আপনি তা সাথে সাথেই রিটার্ন করেন। যদি না থাকে (ক্যাশ মিস — cache miss), তবে আপনি ডেটাবেস থেকে সেটি আনেন, ক্যাশে স্টোর করেন এবং তারপর রিটার্ন করেন।

ক্যাশ দ্রুত কাজ করে কারণ এটি RAM (মেমরি) ব্যবহার করে, যা ডিস্ক থেকে পড়ার চেয়ে ১০০-১০০০ গুণ দ্রুত। এর ট্রেড-অফটি কী? RAM সীমিত এবং ব্যয়বহুল, তাই আপনি শুধুমাত্র আপনার ডেটার একটি ছোট অংশই ক্যাশে রাখতে পারেন।

ক্যাশ ছাড়া — প্রতিটি রিকোয়েস্ট ডেটাবেসে হিট করে

ক্যাশিং স্ট্র্যাটেজি (Caching Strategies)

আপনার ক্যাশ এবং ডেটাবেস কীভাবে একসাথে কাজ করবে তার বেশ কয়েকটি প্যাটার্ন রয়েছে। প্রতিটিরই আলাদা ট্রেড-অফ রয়েছে।

ক্যাশ-অ্যাসাইড (Cache-Aside / Lazy Loading)

  • অ্যাপ্লিকেশন প্রথমে ক্যাশ চেক করে।
  • মিস হলে, অ্যাপ ডেটাবেস থেকে পড়ে, তারপর ফলাফলটি ক্যাশে রাইট করে।
  • হিট হলে, অ্যাপ সরাসরি ক্যাশ করা ডেটা রিটার্ন করে।
  • সুবিধা: শুধুমাত্র সেই ডেটাই ক্যাশ করে যা আসলে রিকোয়েস্ট করা হয়। ইমপ্লিমেন্ট করা সহজ।
  • অসুবিধা: প্রতিটি ডেটার জন্য প্রথম রিকোয়েস্ট ধীরগতির হয় (ক্যাশ মিস)। ডেটা পুরানো (stale) হয়ে যেতে পারে।

রাইট-থ্রু (Write-Through)

  • প্রতিটি রাইট একই সময়ে ক্যাশ এবং ডেটাবেস উভয়েই যায়।
  • ক্যাশ সর্বদা আপ-টু-ডেট থাকে।
  • সুবিধা: ডেটা কখনই পুরানো হয় না। রিড সব সময় দ্রুত হয়।
  • অসুবিধা: রাইট ধীরগতির হয় (কারণ আপনাকে দুবার রাইট করতে হয়)। এমন ডেটাও ক্যাশ হতে পারে যা কেউ কখনো পড়ে না।

রাইট-ব্যাক (Write-Back / Write-Behind)

  • রাইটগুলো শুধুমাত্র ক্যাশেই যায়। ক্যাশ পরবর্তীতে ব্যাচ (batch) আকারে ডেটাবেসে রাইট করে।
  • সুবিধা: অতি-দ্রুত রাইট সম্পন্ন হয়। রাইট-হেভি ওয়ার্কলোডের জন্য ভালো।
  • অসুবিধা: ডেটাবেসে ফ্লাশ করার আগে ক্যাশ ক্র্যাশ করলে ডেটা হারানোর ঝুঁকি থাকে। এটি তুলনামূলক জটিল।

রিড-থ্রু (Read-Through)

  • এটি ক্যাশ-অ্যাসাইডের মতোই, তবে ক্যাশ মিস হলে ডেটাবেস থেকে লোড করার দায়িত্ব ক্যাশের নিজেরই
  • অ্যাপ্লিকেশন শুধুমাত্র ক্যাশের সাথেই যোগাযোগ করে, ডেটাবেসের সাথে কখনোই সরাসরি কথা বলে না।
Click chart to zoom
ক্যাশ-অ্যাসাইড প্যাটার্ন — অ্যাপ প্রথমে ক্যাশ চেক করে, মিস হলে ডেটাবেস থেকে নেয় এবং পরবর্তী সময়ের জন্য ক্যাশ আপডেট করে
ক্যাশ হিট — দ্রুত মেমরি থেকে ডেটা পরিবেশন করা হয়
ক্যাশ মিস — ডেটাবেস থেকে আনে, এরপর ক্যাশ পপুলেট করে

ক্যাশ-অ্যাসাইড প্যাটার্ন ইমপ্লিমেন্টেশন

import redis
import json
import time
class CacheAside:
def __init__(self):
self.cache = redis.Redis(host='localhost', port=6379)
self.ttl = 3600 # Cache entries expire after 1 hour
def get_user(self, user_id: int) -> dict:
# Step 1: Check cache
cache_key = f"user:{user_id}"
cached = self.cache.get(cache_key)
if cached:
print(f"Cache HIT for user {user_id}")
return json.loads(cached)
# Step 2: Cache miss — query database
print(f"Cache MISS for user {user_id} — querying DB...")
user = self._query_database(user_id)
# Step 3: Store in cache for next time
self.cache.setex(cache_key, self.ttl, json.dumps(user))
return user
def update_user(self, user_id: int, data: dict):
# First, update the database
self._update_database(user_id, data)
# Then invalidate (delete) the cache entry
self.cache.delete(f"user:{user_id}")
# Next read will fetch fresh data from the database
def _query_database(self, user_id):
time.sleep(0.05) # Simulated 50ms database query
return {"id": user_id, "name": "Anika", "email": "[email protected]"}
def _update_database(self, user_id, data):
time.sleep(0.05) # Simulated 50ms database write
# Usage
service = CacheAside()
service.get_user(42) # Cache miss — slow (50ms)
service.get_user(42) # Cache hit — fast (<1ms)
Output
Cache MISS for user 42 — querying DB...
Cache HIT for user 42

ক্যাশ ইভিকশন পলিসি (Cache Eviction Policies)

আপনার ক্যাশের স্পেস বা জায়গা সীমিত। ক্যাশ পূর্ণ হয়ে গেলে এবং নতুন স্পেস প্রয়োজন হলে কোন পুরোনো আইটেমটি বাদ দেওয়া হবে? এটিই হলো ইভিকশন পলিসি

LRU (Least Recently Used) — যে আইটেমটি সবচেয়ে বেশি সময় ধরে অ্যাক্সেস করা হয়নি সেটি বাদ দেওয়া হয়। "যদি আপনি এটি অনেকদিন ব্যবহার না করে থাকেন, তবে সম্ভবত আপনার এটির আর প্রয়োজন নেই।" এটি সবচেয়ে জনপ্রিয় ইভিকশন পলিসি।

LFU (Least Frequently Used) — যে আইটেমটি সব মিলিয়ে সবচেয়ে কমবার অ্যাক্সেস করা হয়েছে সেটি বাদ দেওয়া হয়। "যে আইটেমটি কেউ বেশি দেখে না।" এটি স্থিতিশীল পপুলারিটি প্যাটার্ন সহ কাজের ক্ষেত্রে ভালো।

TTL (Time To Live) — প্রতিটি আইটেমের একটি এক্সপায়ারেশন টাইমার থাকে। X সেকেন্ড/মিনিট পর, এটি স্বয়ংক্রিয়ভাবে মুছে যায়। এটি ঠিক ইভিকশন নয়, তবে এটি পুরানো ডেটাকে চিরতরে থেকে যাওয়া প্রতিরোধ করে।

FIFO (First In, First Out) — সবচেয়ে পুরোনো আইটেমটি বাদ দেওয়া হয়। এটি সহজ তবে সব সময় স্মার্ট নয় — পুরোনো আইটেমটিও অনেক বেশি পপুলার হতে পারে।

র‍্যান্ডম ইভিকশন (Random Eviction) — বাদ দেওয়ার জন্য র‍্যান্ডমভাবে কোনো একটি আইটেম বেছে নেয়। শুনতে অদ্ভুত লাগলেও এটি দ্রুত এবং কিছু ওয়ার্কলোডে আশ্চর্যজনকভাবে কার্যকর।

বাস্তবে, অধিকাংশ সিস্টেম LRU + TTL একসাথে ব্যবহার করে। LRU সীমিত স্পেস বা স্থানাভাব পরিচালনা করে এবং TTL ডেটার সতেজতা (freshness) নিশ্চিত করে।

ক্যাশ ইভিকশন — LRU সবচেয়ে কম ব্যবহৃত হওয়া পুরোনো আইটেমটিকে সরিয়ে দেয়
Note: এখানে মূল বিষয়টি হলো: ক্যাশ ইনভ্যালিডেশন (Cache invalidation) কম্পিউটার বিজ্ঞানের দুটি সবচেয়ে কঠিন সমস্যার একটি (অন্যটি হলো নামকরণ এবং অফ-বাই-ওয়ান এরর)। চ্যালেঞ্জটি হলো: ক্যাশ করা ডেটা কখন পুরানো হয়ে যায়? অতিরিক্ত আগ্রাসী (aggressive) ইনভ্যালিডেশনের অর্থ হলো ক্যাশ হিট রেট কমে যাওয়া। আর খুব বেশি অলস (lazy) ইনভ্যালিডেশনের অর্থ হলো ব্যবহারকারীরা পুরোনো ডেটা দেখবে। এই ক্ষেত্রে TTL সাধারণত আপনার সেরা বন্ধু।

ক্যাশ ইনভ্যালিডেশন (Cache Invalidation)

ক্যাশের মধ্যে থাকা মেয়াদোত্তীর্ণ বা পুরানো ডেটা বাস্তব সমস্যা তৈরি করতে পারে। ধরুন একজন ব্যবহারকারী তার প্রোফাইল ছবি আপডেট করেছে, কিন্তু অন্যরা তাকে পুরোনো ছবিতেই দেখতে পাচ্ছে কারণ এটি ক্যাশ করা আছে। আপনি ক্যাশ কীভাবে ফ্রেশ বা আপ-টু-ডেট রাখবেন?

পার্জ অন রাইট (Purge on write): যখন ডেটা আপডেট করা হয়, সাথে সাথে ক্যাশ করা ভার্সনটি মুছে দিন (বা আপডেট করুন)। এটি সিঙ্গেল-সার্ভার সেটআপের জন্য সহজ এবং কার্যকর।

TTL-ভিত্তিক মেয়াদোত্তীর্ণ (TTL-based expiry): প্রতিটি ক্যাশ করা আইটেমে একটি টাইম-টু-লিভ (TTL) সেট করুন। ৫ মিনিট পর, ডেটা স্বয়ংক্রিয়ভাবে বাতিল হয়ে যায় এবং পরবর্তী রিড রিকোয়েস্ট ফ্রেশ ডেটা নিয়ে আসে। এখানে আমরা কিছুটা সময় পুরানো ডেটা মেনে নিই।

ইভেন্ট-ড্রিভেন ইনভ্যালিডেশন (Event-driven invalidation): ডেটা পরিবর্তিত হলে ডেটাবেস ইভেন্ট পাবলিশ করে (মেসেজ কিউ ব্যবহার করে)। ক্যাশ লিসেনাররা এই ইভেন্টগুলো গ্রহণ করে এবং প্রাসঙ্গিক এন্ট্রিগুলো ইনভ্যালিডেট করে দেয়। এটি জটিল, তবে আরও সুনির্দিষ্ট।

ভার্সনড কি (Versioned keys): user:42 এর পরিবর্তে user:42:v3 ব্যবহার করুন। যখন ডেটা পরিবর্তিত হয়, ভার্সন বাড়িয়ে দিন। তখন পুরানো ক্যাশ এন্ট্রিগুলো স্বাভাবিকভাবেই প্রাসঙ্গিকতা হারাবে।

Click chart to zoom
ক্যাশ ইনভ্যালিডেশন স্ট্র্যাটেজি — রাইট-থ্রু ক্যাশকে কনসিস্টেন্ট রাখে, রাইট-ব্যাক গতি বাড়ায়, এবং রাইট-অ্যারাউন্ড কম-পঠিত ডেটার ক্যাশিং এড়ায়

Redis এবং Memcached

ক্যাশিং এর সবচেয়ে জনপ্রিয় দুটি সল্যুশন হলো:

Redis — এটি একটি ইন-মেমরি ডেটা স্ট্রাকচার স্টোর। এটি স্ট্রিং, লিস্ট, সেট, হ্যাশ, সর্টেড সেট এবং আরও অনেক কিছু সমর্থন করে। এর ডেটা পারসিস্টেন্স বা স্থায়িত্বের অপশন (RDB স্ন্যাপশট, AOF লগ) রয়েছে, তাই রিস্টার্ট করার পরও ডেটা সুরক্ষিত থাকে। এটি পাব/সাব (pub/sub), লুয়া (Lua) স্ক্রিপ্টিং এবং ট্রানজেকশন সমর্থন করে। এটিকে ক্যাশিং-এর সুইস আর্মি নাইফ বলা যায়।

Memcached — এটি একটি সাধারণ, পিওর কী-ভ্যালু (key-value) ক্যাশ। এটি অনেক বেশি লাইটওয়েট হওয়ায় সাধারণ গেট/সেট অপারেশনের জন্য দ্রুত। এর কোনো পারসিস্টেন্স বা উন্নত ডেটা স্ট্রাকচার নেই। একদম সহজ বা সরল ক্যাশিং প্রয়োজনের জন্য এটি সেরা।

কখন কোনটি বেছে নেবেন?

  • যদি আপনার ডেটা স্ট্রাকচার (লিডারবোর্ডের জন্য সর্টেড সেট, কিউ-এর জন্য লিস্ট), পারসিস্টেন্স, বা পাব/সাব প্রয়োজন হয়, তবে Redis ব্যবহার করুন।
  • যদি আপনার একটি সাধারণ, অতি-দ্রুত ক্যাশের প্রয়োজন হয় এবং পারসিস্টেন্স এর প্রয়োজন না থাকে, তবে Memcached ব্যবহার করুন।

CDN ক্যাশিং এবং থান্ডারিং হার্ড (The Thundering Herd)

CDN ক্যাশিং (CDN caching) হলো ক্যাশিংয়ের একটি বিশেষ রূপ যেখানে স্ট্যাটিক কন্টেন্ট (যেমন- ছবি, CSS, জাভাস্ক্রিপ্ট, ভিডিও) বিশ্বের বিভিন্ন স্থানে ছড়িয়ে থাকা এজ সার্ভারে ক্যাশ করা হয়। যখন টোকিওতে থাকা কোনো ব্যবহারকারী একটি ছবি রিকোয়েস্ট করেন, তখন সেটি ডাটা-সেন্টারয় আপনার মূল সার্ভারের পরিবর্তে কাছের একটি CDN নোড থেকে পরিবেশন করা হয়। CDN অধ্যায়ে আমরা এটি নিয়ে বিস্তারিত আলোচনা করব।

থান্ডারিং হার্ড প্রবলেম (The Thundering Herd Problem): কল্পনা করুন একটি খুব জনপ্রিয় রিকোয়েস্টের ক্যাশ এন্ট্রির মেয়াদ শেষ হয়ে গেছে। আচমকা ১০,০০০ সমসাময়িক রিকোয়েস্ট ক্যাশ মিস পেল এবং একই সময়ে ডেটাবেসে হিট করল। অতিরিক্ত লোডের কারণে আপনার ডেটাবেস ক্র্যাশ হয়ে যাবে। একেই বলা হয় "থান্ডারিং হার্ড" বা ছুটে আসা পালের সমস্যা।

সমাধান:

  • ক্যাশ লকিং (Cache locking): শুধুমাত্র একটি রিকোয়েস্ট ডেটাবেস থেকে ডেটা ফেচ করতে পারবে। বাকিরা ক্যাশ পুনরায় পূর্ণ হওয়া পর্যন্ত অপেক্ষা করবে।
  • স্টেল-হোয়াইল-রিভ্যালিডেট (Stale-while-revalidate): ব্যাকগ্রাউন্ডে একটি রিকোয়েস্ট যখন ডেটা রিফ্রেশ করছে, তখন অন্যদের পুরানো ক্যাশ করা ডেটা সার্ভ করুন।
  • জিটারড TTL (Jittered TTL): TTL মানের সাথে কিছু র‍্যান্ডম ভ্যারিয়েশন বা সময় যোগ করুন যাতে সব এন্ট্রি একই সময়ে মেয়াদোত্তীর্ণ না হয়। TTL=3600 এর পরিবর্তে, TTL=3600 + random(0, 300) ব্যবহার করুন।
কোথায় ক্যাশ করবেন — ব্রাউজার থেকে ডেটাবেস পর্যন্ত
Note: সাক্ষাৎকারের টিপস: যখন ক্যাশিং নিয়ে আলোচনা করবেন, তখন সব সময় কনসিস্টেন্সি (consistency) এবং পারফরম্যান্সের (performance) মধ্যে ট্রেড-অফের কথা উল্লেখ করবেন। বেশি অ্যাগ্রেসিভ ক্যাশ করার অর্থ হলো দ্রুততর রিড, কিন্তু এর সাথে সম্ভাব্য পুরানো ডেটার ঝুঁকি থাকে। এছাড়াও আপনার ক্যাশের আকারের সীমাবদ্ধতার (size limitations) কথা বলুন — আপনি সবকিছু ক্যাশ করতে পারবেন না, তাই ইভিকশন পলিসি এখানে খুবই গুরুত্বপূর্ণ।

Key Metrics

ক্যাশ রিড (Redis)
ইন-মেমরি লুকআপ
~০.১-০.৫ ms \(O(1)\)
ক্যাশ রাইট (Redis)
ইন-মেমরি রাইট
~০.১-০.৫ ms \(O(1)\)
ডেটাবেস রিড (ক্যাশ ছাড়া)
ডিস্ক I/O + কোয়েরি এক্সিকিউশন
~৫-৫০ ms \(O(\log n)\)
CDN হিট
এজ সার্ভার থেকে পরিবেশন করা হয়
~১-২০ ms \(O(1)\)
CDN মিস (অরিজিন ফেচ)
ক্রস-রিজিয়ন রাউন্ড ট্রিপ
~৫০-২০০ ms \(O(1)\)
সাধারণ ক্যাশ হিট রেশিও
সঠিকভাবে টিউন করা রিড-হেভি ওয়ার্কলোড
৮৫-৯৯% —

ছোট কুইজ

ক্যাশ-অ্যাসাইড প্যাটার্নে, ক্যাশ মিস হলে কী হয়?

পড়া চালিয়ে যান