মনে করুন, আপনার একজন বন্ধু আছে যার ধৈর্যের লেভেল যেকোনো সন্ন্যাসীর চেয়ে কম না। কিন্তু স্বভাবটা একটু খিটখিটে। আপনার ইচ্ছা হলো তাকে একটু রাগানোর। আপনি তাকে নানাভাবে বিরক্ত করার চেষ্টা করলেন। সে নানাভাবে আপনার এই উত্যক্ত করা সহ্য করছে। টানা ২-৩ ঘণ্টা ধরে আপনি সব ধরনের আজাইরা কথাবার্তা বলা শুরু করে দিয়েছেন। এই পর্যায়ে আপনার বন্ধুর ধৈর্যের বাঁধ ভেঙে গেল। তার সহ্য ক্ষমতার লিমিট শেষ! সে রেগে গিয়ে একটা তাড়া দিয়ে আপনাকে চুপ করিয়ে দিল। এবার আপনি চুপ!
আপনার বন্ধুর মতো ডাটাবেসেরও একটা লিমিট বা সহ্য ক্ষমতা আছে। আপনি চাইলে ডাটাবেসকে বিভিন্নভাবে optimize করতে পারেন, যেমন: Indexing করা, Connection Pool বাড়ানো ইত্যাদি। এগুলো আপনাকে সাময়িকভাবে সাপোর্ট দেবে। কিন্তু আপনার অ্যাপ্লিকেশন বড় হলে আর ট্রাফিক বহুগুণ বাড়লে একদিন না একদিন ডাটাবেসের Hardware Limitation দেখা দেবেই।
তাহলে এখন উপায় কী?
সমাধান হচ্ছে—একাধিক ডাটাবেসকে একসাথে একটা সিঙ্গেল ডাটাবেস হিসেবে ব্যবহার করা। অনেকগুলো ডাটাবেস থাকবে, আর ডাটার ধরন অনুযায়ী নির্দিষ্ট ডাটাবেসে ডাটা স্টোর হবে। এতে করে ডাটাবেসের হার্ডওয়্যার লিমিটেশন টপকে যাওয়া সম্ভব হবে।
এখন আপনার মনে প্রশ্ন জাগতে পারে, "ভাই, আমার তো অলরেডি এক্সিস্টিং একটা ডাটাবেস আছে এবং সেখানে কোটি কোটি ডাটা জমে আছে! নতুন প্রজেক্টে না হয় মাল্টিপল ডাটাবেস নিলাম, কিন্তু এই এক্সিস্টিং ডাটাবেসের ডাটাগুলোর কী হবে?"
এখানেও সমাধানটা একই, জাস্ট উল্টো প্রসেসে! বড় এক্সিস্টিং ডাটাবেসটাকে ভেঙে টুকরো টুকরো করে একাধিক ডাটাবেসে ভাগ করে দেওয়া। এতে করে আপনার এক্সিস্টিং কোনো ডাটাই হারাবে না। আর এই যে অনেকগুলো ডাটাবেস একসাথে ব্যাকগ্রাউন্ডে মিলে একটা সিঙ্গেল ডাটাবেস হিসেবে কাজ করছে—
এই টেকনিকটাকেই বলে Database Sharding।
Sharding কী?
সহজ কথায় ডাটাবেসকে ভেঙে টুকরো টুকরো করে ফেলা। সিস্টেমে যখন ইউজারের সংখ্যা ও ডাটা এত বেড়ে যায় যে একটা সিঙ্গেল ডাটাবেস সার্ভার আর সামলাতে পারে না, তখনই ডাটাবেসকে বিভক্ত করতে হয়।
এখন প্রশ্ন করতে পারেন, "ডাটাবেস ভেঙে আলাদা করে ফেললে ইউজারের ডাটার ভেতরে Inconsistency বা এলেমেলো অবস্থা তৈরি হবে না?"
হ্যাঁ, অবশ্যই তৈরি হতে পারে—যদি আপনি কোনো সঠিক Strategy না মেনে এলোমেলোভাবে ভাঙেন! Database Sharding-এর জন্য বেশ কিছু পপুলার স্ট্র্যাটেজি আছে, যেমন: Range-based, Hash-based, এবং Directory-based Sharding।
ডাটাবেসকে ভেঙে ফেলার পর যে ছোট ছোট আলাদা টুকরোগুলো বা সার্ভারগুলো তৈরি হয়, তাদের প্রতিটাকে বলে এক একটা Shard।
ধরা যাক, আপনার ডাটাবেসে ১০০ মিলিয়ন (১০ কোটি) ইউজার আছে। আপনি ডাটাবেসটিকে ৪টি শার্ডে ভাগ করলেন। এখন প্রতিটা শার্ডে সমানভাবে ২৫ মিলিয়ন করে ইউজার থাকবে।
Range-Based Sharding: সহজ কিন্তু বিপজ্জনক ⚠️
ধরা যাক আপনার শার্ডের ভাগগুলো এমন:
- User ID 1–25M → Shard 1
- User ID 25M–50M → Shard 2
- User ID 50M–75M → Shard 3
- User ID 75M–100M → Shard 4
এটাকে বলে Range-Based Sharding। এই পদ্ধতিটা বোঝা ও সেটআপ করা একদম সহজ। কিন্তু এর মধ্যে একটা বড় বিপদ লুকিয়ে আছে!
কারণটা চিন্তা করুন: আপনার সিস্টেমে যত নতুন ইউজার সাইনআপ করবে, তাদের সবার ID হবে সর্বশেষে (যেমন 99M, 100M...) — অর্থাৎ নতুন সব রাইট ট্রাফিক কিন্তু গিয়ে পড়বে শুধু Shard 4-এর ওপর! বাকি Shard 1, 2, 3 একদম অলস বসে থাকবে।
ডাটাবেসের এই অবস্থাকে বলে Hot Spot Problem। অর্থাৎ একটা নির্দিষ্ট শার্ডের ওপর মাত্রাতিরিক্ত চাপ পড়া। যে চাপ কমানোর জন্য ডাটাবেসকে ভাগ করেছিলাম, ঘুরেফিরে সেই শার্ডেই আবার চাপ পড়ছে! এটাই Range-Based Sharding-এর সবচেয়ে বড় সীমাবদ্ধতা।
Hash-Based Sharding: সুষম বণ্টন ⚖️
shard_id = hash(user_id) % total_shards
আপনি যদি কখনো কোডিংয়ে HashMap বা Hash Table ডেটা স্ট্রাকচার ব্যবহার করে থাকেন, তবে জানেন যে সেখানে একটা Hash Function থাকে। সেই হ্যাশ ফাংশন দিয়ে নির্দিষ্ট একটা key-র বিপরীতে অনন্য একটা ভ্যালু বের করে ডাটা রাখা হয়।
Hash-Based Sharding-এও ঠিক একই কনসেপ্ট কাজ করে। ইউজার আইডি বা ইউনিক কী-র ওপর ভিত্তি করে একটা Hash Function রান করানো হয় এবং যে Shard ID বের হয়, ডাটাটি সেই নির্দিষ্ট শার্ডেই গিয়ে জমা হয়। একইভাবে যখন ডাটা রিট্রিভ করা হবে, একই হ্যাশ ফাংশন দিয়ে мгновенно জেনে নেওয়া যাবে ডাটাটি ঠিক কোন শার্ডে আছে। এর ফলে ডাটা পুরো ক্লাস্টারে একদম সমানভাবে ডিস্ট্রিবিউট হয় এবং Hot Spot প্রবলেম তৈরি হয় না।
কিন্তু আসল ক্যাচালটা বাঁধে যখন ভবিষ্যতে আপনার ক্লাস্টারে নতুন কোনো Shard যোগ করার দরকার হয়!
কারণ আপনার Modulo Hash Function-টি মোট শার্ড সংখ্যার সাথে Tightly Coupled থাকে। এখন শার্ডের সংখ্যা ৪ থেকে বাড়িয়ে ৫ করলেই সব ইউজারের Hash ID পুরো পাল্টে যাবে! ফলে পুরো ডাটাবেসের কোটি কোটি ডাটা আবার নতুন করে রিশাফলিং করতে হবে—যা প্রোডাকশনে এক বিরাট নাইটমেয়ার!
এই সমস্যার দারুণ এক সমাধান হলো Consistent Hashing। এখানে নতুন নোড বা শার্ড যোগ হলে সব ডাটা সরাতে হয় না, বরং সামান্য কিছু ডাটা নতুন শার্ডে শিফট হয়।
Directory-Based Sharding: ডিরেক্টরি লুকআপ 📖
এই স্ট্র্যাটেজিটা একদম পানির মতো সোজা। আলাদা একটা Central Lookup Table (বা ডিরেক্টরি) মেইনটেইন করা হয়। এই টেবিলে সোজা বাংলায় লেখা থাকে—কোন ইউজারের ডাটা ঠিক কোন শার্ডে রাখা আছে।
এর সবচেয়ে বড় সুবিধা হলো, আপনি যেকোনো সময় ব্যাকগ্রাউন্ডে কোনো ডাটা এক শার্ড থেকে আরেক শার্ডে মাইগ্রেট করতে পারবেন, জাস্ট লুকআপ টেবিলের ম্যাপটা আপডেট করে দিলেই হলো!
কিন্তু এর রিভার্স সমস্যা হলো—এই Lookup Table-টি নিজেই সিস্টেমে Single Point of Failure হয়ে দাঁড়াতে পারে। কোনো কারণে যদি এই ডিরেক্টরি সার্ভিসটি ডাউন বা স্লো হয়ে যায়, তবে পুরো ক্লাস্টারই অচল হয়ে পড়বে।
Sharding-এর আসল যন্ত্রণা ও চ্যালেঞ্জসমূহ 💣
মূল সমস্যায় যাওয়ার আগে একটা ছোট বাস্তব রূপক বলি।
মনে করুন একটা যৌথ পরিবারে সদস্য সংখ্যা অনেক বেড়ে গেছে। এক ঘরে আর আটাচ্ছে না। বাড়ির কর্তা সিদ্ধান্ত নিলেন সবাই আলাদা ঘরে থাকবে। তিনি গ্রামের ৪ মাথায় ৪ ভাইয়ের জন্য ৪টা বাড়ি বানিয়ে সবাইকে আলাদা করে দিলেন। এখন কোনো কারণে মূল পরিবারে যদি জরুরি সভা ডাকা হয় বা কোনো প্রবলেম হয়, তবে ৪ মাথা থেকে সবাই ঠিক সময়ে খবর পাবে কিনা আর একসাথে উপস্থিত হতে পারবে কিনা—সেটা নিয়ে বিশাল ঝক্কি তৈরি হবে!
একইভাবে Database Sharding করলে সিস্টেমে বেশ কিছু জটিলতা জন্ম নেয়:
- Cross-Shard Query: ধরা যাক, আপনাকে এমন একটা রিলেশনাল কোয়েরি চালাতে হবে যার ডাটা ২টা আলাদা শার্ডে আছে! ডাটাবেস ইঞ্জিন সরাসরি এই JOIN করতে পারবে না। অ্যাপ্লিকেশন লেয়ারে কোড লিখে আলাদা আলাদা শার্ড থেকে ডাটা এনে মার্জ করতে হবে—যা অত্যন্ত স্লো এবং যার টাইম কমপ্লেক্সিটি অনেক ক্ষেত্রে $O(n^2)$ বা তারও বেশি হয়ে দাঁড়ায়!
- Distributed Transactions: দুইটা আলাদা শার্ডে একসাথে একটা Atomic Transaction চালানো (ACID বজায় রাখা) এক মহাযজ্ঞ! Distributed Transaction ম্যানেজ করতে Two-Phase Commit (2PC) বা Saga Pattern-এর মতো জটিল জিনিস আর্কিটেকচারে আনতে হয়।
- Rebalancing Nightmare: ডাটা অনবরত বাড়তে থাকলে নতুন শার্ড অ্যাড করা আর পুরানো শার্ড থেকে ডাটা Rebalance করা অত্যন্ত ঝুঁকিপূর্ণ কাজ।
Sharding কখন করবেন? 🚦
শার্ডিংকে সবসময় আপনার সিস্টেম স্কেলিংয়ের শেষ অস্ত্র (Last Resort) হিসেবে বিবেচনা করবেন। তার আগে নিচের স্টেপগুলো অবশ্যই ট্রাই করে দেখবেন:
- Vertical Scaling: আপনার ডাটাবেস সার্ভারের RAM, CPU আর Storage বাড়িয়ে আরওশক্তিশালী করুন।
- Read Replicas: রিড ট্রাফিক বেশি হলে Master-Replica আর্কিটেকচার দিয়ে রিড লোড ভাগ করে দিন।
- Caching: Redis বা Memcached ব্যবহার করে ঘনঘন কোয়েরি হওয়া ডাটা ক্যাশে নিয়ে আসুন।
- Data Archiving: ২-৩ বছরের পুরানো বা অলস ডাটা কোল্ড স্টোরেজে বা আর্কাভে সরিয়ে ফেলুন।
যখন এই সবকটি অপশন শেষ হয়ে যাবে এবং ডাটাবেস আর কোনোভাবেই লোড নিতে পারছে না, তখনই কেবল Sharding-এর পথে হাঁটবেন!
বটম লাইন
Sharding হলো একটা খাঁটি Architectural Trade-off। এখানে কোনো পরম সঠিক বা ভুল উত্তর নেই। আপনার বিজনেস প্রয়োজন ও ডাটা স্কেলের ওপর নির্ভর করে সঠিক স্ট্র্যাটেজি সিলেক্ট করতে হবে। Sharding একটি অত্যন্ত পাওয়ারফুল টুল, কিন্তু এটি সিস্টেমের জটিলতা বহুগুণ বাড়িয়ে দেয়। তাই যখন স্কেল করতেই হবে, Hash-based দিয়ে শুরু করুন এবং ভবিষ্যতে নিশ্চিন্ত থাকতে Consistent Hashing আর্কিটেকচারে ইনভেস্ট করুন।
আপনার সিস্টেমে কি কখনো Sharding ব্যবহারের প্রয়োজন পড়েছে? কোন স্ট্র্যাটেজি বেছে নিয়েছিলেন? কমেন্টে জানান! 👇
Top comments (0)