একটা জিনিস চিন্তা করুন, $\pi$ (Pi)-এর মান কি কখনো পরিবর্তন হবে? কিংবা $\tan 30^\circ$-এর মান? কখনো না! এগুলো ধ্রুবক বা Constant, পরিবর্তন হওয়ার প্রশ্নই ওঠে না।
এবার আরেকটা বিষয় ভেবে দেখুন—এমন কিছু ডাটা আছে যেগুলো সবসময় ফিক্সড না হলেও, একটা দীর্ঘ সময়ের জন্য ফিক্সড থাকে এবং খুব কদাচিৎ পরিবর্তন হয়? চোখ মেললে চারপাশে এমন অনেক কিছু দেখতে পাবেন। এই যেমন ধরুন, কোনো এলাকার লোডশেডিংয়ের শিডিউল (দিনে ৪-৫ ঘণ্টা লোডশেডিং হবে)—এটা কিন্তু টানা বেশ কয়েকদিনের জন্য মোটামুটি ফিক্সড।
এখন একটু ভাবুন, এই যে ডাটাগুলো সচরাচর বদলায় না, এগুলোর জন্য প্রতিবার যদি আপনার মেইন ডাটাবেসে হিট না করে অন্য কোনো ফাস্ট মাধ্যমে ডাটাটা সাময়িকভাবে সেভ করে রাখা যায়, তাহলে ডাটাবেসের ওপর থেকে কী পরিমাণ লোড কমে যাবে বলুন তো?
অর্থাৎ, মেইন ডাটাবেসে ডাটা তো থাকবেই, কিন্তু ইউজার যাতে বারবার ডাটাবেস পর্যন্ত না এসে ফাস্ট কোনো মাধ্যম থেকে মুহূর্তের মধ্যে ডাটা পেয়ে যায়—সেই বিশেষ প্রসেসটিই হলো Caching।
আর Caching-এর জন্য দুনিয়ার সবচেয়ে পপুলার ও সেরা চয়েস হলো Redis!
Redis কী?
সহজ কথায়, Redis হলো একটি open-source, NoSQL In-Memory (RAM) Database।
ট্রেডিশনাল ডাটাবেসের (যেমন PostgreSQL, MySQL) মতো Redis হার্ডডিস্কে ডাটা স্টোর না করে সার্ভারের মেমোরি অর্থাৎ RAM ব্যবহার করে। আর আমরা সবাই জানি, হার্ডডিস্কের তুলনায় RAM থেকে ডাটা পড়া ও লেখা বহুগুণ বেশি ফাস্ট।
তবে এর দুটি বড় সীমাবদ্ধতা আছে:
- পকেটের টান (Expensive): ৪ জিবি RAM-এর দামে আপনি ১ টেরাবাইট হার্ডডিস্ক পেয়ে যাবেন! তাই Redis-কে কখনোই Primary Database হিসেবে ব্যবহার করা যায় না।
- Volatile Nature: RAM-এ থাকা ডাটা চিরস্থায়ী নয়। সার্ভার অফ হলে বা রিস্টার্ট নিলে RAM-এর সব ডাটা কিন্তু সম্পূর্ণ মুছে যায়!
এখন প্রশ্ন উঠতে পারে—"তাহলে কি Redis শুধু Caching-এর জন্যই ব্যবহার হয়?"
এক কথায় উত্তর: একদমই না!
Redis is much more than just Caching 🚀
Redis-কে আপনি ডাটাবেসের পাশাপাশি একটা পাওয়ারফুল temporary ডেটা প্রসেসর হিসেবে চিন্তা করতে পারেন। এর অন্যতম সেরা ফিচার হলো—এর মধ্যে Automatic Expiration বা ডাটা স্বয়ংক্রিয়ভাবে মুছে যাওয়ার ইন-বিল্ট ফিচার রয়েছে।
একটু খেয়াল করলেই বুঝবেন, ব্যাকএন্ডের কত জায়গায় এই অটো-ডিলিট বা টেম্পোরারি ডাটা ফিচারগুলো কাজে লাগে:
- Session Store: ইউজারের লগইন সেশন তথ্য স্টোর রাখতে (JWT/Session Cookie)।
- Temporary Discount & Flash Sale: ই-কমার্স সাইটে ১ ঘণ্টার ফ্ল্যাশ সেল বা কুপন ডিসকাউন্টের টাইমার হ্যান্ডেল করতে।
- Job Queue & Message Broker: BullMQ বা Celery-র ব্যাকগ্রাউন্ড জব কিউ হিসেবে।
- Write Batching (Buffer Layer): মনে করুন, প্রতি সেকেন্ডে আপনার সিস্টেমে ১ মিলিয়ন রাইট ডাটা আসছে (যেমন ইউটিউব ভিডিওর ভিউ বা লাইক কাউন্ট)। সরাসরি ডাটাবেসে হিট করলে ডাটাবেস ক্র্যাশ করবে! তাই ডাটাগুলো আগে Redis-এ বাফার হিসেবে জমে, আর অফ-পিকে ব্যাচ আকারে মূল ডাটাবেসে রাইট হয়।
Caching Patterns: কোনটা কখন ব্যবহার করবেন?
সঠিক Caching Pattern বেছে না নিলে ডাটাবেস আর ক্যাশের মধ্যে Data Inconsistency দেখা দেবে। নিচে ৩টি প্রধান পপুলার ক্যাচিং প্যাটার্ন নিয়ে বিস্তারিত আলোচনা করা হলো:
1. Cache-Aside (Lazy Loading) 😴
সবচেয়ে পপুলার ও সিম্পল প্যাটার্ন। ইউজার যখন কোনো ডাটার জন্য রিকোয়েস্ট করে, তখন নিচের ফ্লো অনুসরণ করা হয়:
- প্রথমে Cache Store-এ চেক করা হয় ডাটাটি আছে কিনা। থাকলে সরাসরি ক্যাশ থেকে রেসপন্স রিটার্ন।
- ক্যাশে না থাকলে (Cache Miss), ডাটাবেস থেকে ডাটা ফেচ করা হয়।
- ফেচ করা ডাটা ক্যাশে সেভ করা হয় এবং ইউজারের কাছে রেসপন্স পাঠানো হয়।
// 1. Check Cache
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
// 2. Fetch from DB if Cache Miss
const user = await db.findUser(id);
// 3. Save to Cache for future requests
await redis.setex(`user:${id}`, 3600, JSON.stringify(user));
return user;
✅ কখন ব্যবহার করবেন?
- Read-Heavy Applications: যেখানে রাইটের চেয়ে রিড অনেক বেশি হয় (যেমন: ব্লগ, পোর্টফোলিও, নিউজ সাইট)।
- Unpredictable Queries: কোন ডাটা ইউজার কখন চাইবে তা আগে থেকে নিশ্চিত না হলে।
❌ কখন ব্যবহার করবেন না?
- Strict Consistency Requirement: যেখানে ডাটা পরিবর্তনের সাথে সাথে ১০০% নিখুঁত ডাটা দেখাতে হবে (যেমন: ব্যাংক ব্যালেন্স)।
- Write-Heavy Applications: ঘনঘন ডাটা আপডেট হলে বারবার ক্যাশ ইনভ্যালিডেট হয়ে পারফর্ম্যান্স ড্রপ করতে পারে।
2. Write-Through Pattern ✍️
এই প্যাটার্নে যখনই ডাটাবেসে নতুন কোনো ডাটা লেখা বা আপডেট করা হয়, ঠিক সেই মুহূর্তে ক্যাশ স্টোরটিকেও সাথে সাথে আপডেট করে দেওয়া হয়।
// Write to DB AND Update Cache simultaneously
await db.writeUser(user);
await redis.setex(`user:${user.id}`, 3600, JSON.stringify(user));
// Read always hits up-to-date Cache
const cached = await redis.get(`user:${user.id}`);
if (cached) return JSON.parse(cached);
✅ কখন ব্যবহার করবেন?
- High Read-After-Write Workloads: যেমন গেমিং লিডারবোর্ড বা রিয়েল-টাইম স্কোরবোর্ড, যেখানে স্কোরের রাইট হওয়ার সাথে সাথেই দ্রুত রিড রিকোয়েস্ট আসে।
- Strict Consistency Needed: ডাটাবেস ও ক্যাশে সবসময় সেম ডাটা নিশ্চিত রাখতে।
❌ কখন ব্যবহার করবেন না?
- Rarely Read Data: যেসব ডাটা লেখাই হয় শুধু আর্কাভ করার জন্য, রিড হয় না বললেই চলে—সেখানে অযথা ক্যাশ মেমোরি নষ্ট হবে।
3. Write-Behind (Write-Back) Pattern ⏳
এই প্যাটার্নটি একটু ব্যতিক্রমী! এখানে মূল ডাটাবেসে সরাসরি না লিখে, আগে Redis Cache-এ ফাস্ট রাইট করে নেওয়া হয়। পরবর্তীতে ব্যাকগ্রাউন্ডে ব্যাচ আকারে মূল ডাটাবেসে ডাটা সিঙ্ক (Sync) হয়।
// Instant Write to Redis Cache
await redis.setex(`user:${user.id}`, 3600, JSON.stringify(user));
// Async background job updates main DB later in batches
✅ কখন ব্যবহার করবেন?
- Extreme Write-Heavy Workloads: যেমন প্রতি সেকেন্ডে ১ লাখ লাইক বা ভিউ কাউন্ট প্রসেস করা (ইউটিউব বা ফেসবুক লাইক)।
- Batch Processing: ছোট ছোট ঘনঘন ডাটা রাইটকে এক করে ডাটাবেস লোড কমাতে।
❌ কখন ব্যবহার করবেন না?
- Strict Durability Needed: যদি ব্যাকগ্রাউন্ডে ডাটাবেসে সেভ হওয়ার আগেই Redis সার্ভার ক্র্যাশ করে, তবে সেই সময়ের ডাটা স্থায়ীভাবে হারিয়ে যেতে পারে!
TTL (Time to Live): অটো-ডিলিট মেকানিজম ⏱️
ক্যাশে ডাটা রেখে দিলে তা একসময় পুরানো বা Stale হয়ে যাবে। আগের ডাটা ম্যানুয়ালি ডিলিট করে নতুন ডাটা স্টোর করা অত্যন্ত ঝামেলার।
এই ঝামেলার চমৎকার সমাধান হলো TTL (Time to Live)। Redis-এ ক্যাশ সেট করার সময় TTL নির্ধারণ করে দিলে, নির্দিষ্ট সময় (যেমন ১ ঘণ্টা) পার হওয়ার পর Redis স্বয়ংক্রিয়ভাবে মেমোরি থেকে ডাটাটি ডিলিট করে দেয়।
// ১ ঘণ্টা (৩৬০০ সেকেন্ড) পর ক্যাশ অটোমেটিক মুছে যাবে
await redis.setex("session:abc123", 3600, JSON.stringify(sessionData));
Rate Limiting: Atomic Counter দিয়ে থ্রোটলিং 🛡️
ডিজিটাল সিস্টেমে বট বা অ্যাটাকারদের DDoS ও Brute Force আক্রমণ ঠেকানোর অন্যতম জনপ্রিয় উপায় হলো Rate Limiting।
const key = `ratelimit:${userId}`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 60); // ১ মিনিটের উইন্ডো
if (count > 100) throw new Error("Rate limit exceeded");
এখন প্রশ্ন জাগতে পারে—"অন্যান্য রিলেশনাল ডাটাবেস দিয়ে কেন রেট লিমিটিং করি না?"
উত্তরটা খুব সহজ: রেট লিমিট চেক করার জন্য যদি ইউজারকে প্রতি রিকোয়েস্টে ১০-২০ মিলিসেকেন্ড ডাটাবেসের জন্য ওয়েট করতে হয়, তবে অ্যাপ্লিকেশনের রেসপন্স টাইম স্লো হয়ে যাবে। উপরন্তু, Distributed System-এ মাল্টিপল সার্ভারের মধ্যে সেন্ট্রাল কাউন্টার মেইনটেইন করার জন্য Redis-এর কোনো বিকল্প নেই!
Distributed Lock: কনকারেন্সি কন্ট্রোল 🔐
মাল্টিপল সার্ভার একই সাথে ডাটাবেসের একই রো আপডেট করার চেষ্টা করলে Race Condition তৈরি হয়।
const lockKey = `lock:order:${orderId}`;
const lockValue = crypto.randomUUID();
const lock = await redis.set(lockKey, lockValue, "NX", "EX", 30); // Single Atomic Operation
if (!lock) throw new Error("Resource locked by another process");
try {
// Critical section logic here
} finally {
const releaseLockScript = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
await redis.eval(releaseLockScript, 1, lockKey, lockValue);
}
Memory full হয়ে গেলে কী হবে?
Suppose কোনো কারণে, সার্ভারের মেমোরি ফুল হয়ে গেছে। তাহলে এখন কী হবে? সার্ভার কি ক্র্যাশ করবে? বা Redis কি ক্র্যাশ করবে?
Answer হলো: Not at all! Redis ক্র্যাশ করবে না তবে নতুন কোনো ডাটা ইনসার্ট হবে না। Redis-এ Eviction Policies নামে built-in একটা ফিচার আছে। এই ফিচারটা কাজ করে max memory-র ওপর। আপনি চাইলেই Redis-এর জন্য একটা fixed amount-এর মেমোরি assign করে দিতে পারেন। আর যদি assign না করেন তাহলে যতক্ষণ পারবে সার্ভারের মেমোরি use করতে থাকবে।
Eviction policies-এর কাজ হচ্ছে যখন এই মেমোরি শেষ হয়ে যাবে তখন কী হবে সেটার জন্য rules সেট করা। By default এর policy noeviction থাকে। মানে হচ্ছে কিছুই হবে না।
কিছু popular policies রয়েছে:
-
noeviction(Default): কোনো ডাটা ডিলিট করবে না, তবে নতুন কোনো রাইট রিকোয়েস্ট আসলে Out of Memory error দেবে। -
allkeys-lru: সবচেয়ে কম ব্যবহৃত ডাটাগুলো মুছে ফেলবে (Least Recently Used)। সাধারণত ক্যাশিংয়ের জন্য এটি সবচেয়ে best choice। -
volatile-lru: শুধুমাত্র যেসব key-তে expiry বা TTL সেট করা আছে, সেগুলোর মধ্য থেকে কম ব্যবহৃত ডাটাগুলো মুছে ফেলবে। -
allkeys-lfu: frequency-এর ওপর ভিত্তি করে সবচেয়ে কম রিকোয়েস্ট পাওয়া ডাটাগুলো মুছে ফেলবে (Least Frequently Used)।
Production application-এ এই পলিসিগুলো খুব সাবধানে হ্যান্ডেল করা হয়।
বটম লাইন
Redis শুধু একটা মেমোরি ক্যাশ না—এটি Session Store, Pub/Sub Messaging, Rate Limiter, Distributed Lock এবং High-throughput Write Buffer হিসেবে ব্যবহৃত অন্যতম শক্তিশালী ইনফ্রাস্ট্রাকচারাল টুল।
আপনার সিস্টেমে Redis দিয়ে সবচেয়ে ইন্টারেস্টিং কোন কাজটা করেছেন? কমেন্টে শেয়ার করুন! 👇
Top comments (0)