সেশন ০৫

Sharding - Data স্প্লিট করে Write Scale করা

এই সেশনের লক্ষ্য: Replica আর caching দিয়ে যে write bottleneck solve করা গেলো না, সেটা কীভাবে solve করবো। Sharding, shard key এর decision, sharding যেসব problem তৈরি করে, আর consistent hashing। এগুলো বোঝা।
সতর্কতা

এখানেই অনেক system fail করে। Sharding একটা সত্যিকারের problem solve করে ঠিকই। কিন্তু সাথে system design এর সবচেয়ে কঠিন কিছু problem ও তৈরি করে। Write bottleneck প্রমাণিত না হওয়া পর্যন্ত shard করবেন না।

০০ যে প্রশ্নগুলোর উত্তর খুঁজবো

প্রতিটা প্রশ্নের উত্তর জানার পর আপনি write bottleneck থেকে শুরু করে production sharding পর্যন্ত পুরোটা বুঝতে পারবেন।

  • ১ Replica আর cache দিয়ে read scale করলাম। এখনো কী solve হয়নি?
  • ২ আমরা কি শুধু আরো বড় primary machine কিনতে থাকতে পারি না?
  • ৩ একটা database কে shard করার basic idea টা কী?
  • ৪ কোন data কোন shard এ যাবে, সেটা কীভাবে ঠিক করবো?
  • ৫ cross-shard data দরকার হলে কী হবে?
  • ৬ যখন একটা shard বাকিদের চেয়ে অনেক বেশি traffic পায়, তখন কী হবে?
  • ৭ পরে যখন একটা shard অ্যাড করতে হবে, তখন কী হবে?
  • ৮ এখন system টা দেখতে কেমন, আর downside গুলো কী?

০১ Replica আর cache দিয়ে read scale করলাম। এখনো কী solve হয়নি?

Session 04 এর শেষে read গুলো ভালোভাবেই handle হচ্ছিলো। Replica read load ছড়িয়ে দিচ্ছিলো। Cache বেশিরভাগ read নিজেই serve করছিলো।

কিন্তু সব write এখনো কোথায় যায়? একটা single primary database এ।

তার মানে পুরো system এর প্রতিটা insert, update আর delete একটাই machine এ গিয়ে পৌঁছায়। ধরুন মিনিটে লাখ লাখ order, message বা post আসছে। শুধু write এর চাপেই তখন primary আর কুলিয়ে উঠতে পারে না। তখন write ই হয়ে যায় bottleneck। Replica আর caching এই write ceiling পার করতে পারে না।

Replica কেন এটা solve করতে পারে না

Replica হলো একটা clone। এটা primary এর মতোই হুবহু সব data এর full copy রাখে। Clone গুলোকে সবসময় একরকম থাকতে হয়, তাই writer থাকতে পারে একটাই। দুইটা clone দুজনেই write নিলে তাদের data আলাদা হয়ে যেতো। তখন কোনটা সঠিক, সেটা বলার উপায় থাকতো না (Session 03 এর split-brain problem)। তাই replica যোগ করলে read capacity বাড়ে, কিন্তু write capacity একটুও বাড়ে না।

০২ আমরা কি শুধু আরো বড় primary machine কিনতে থাকতে পারি না?

পারা যায়, আর শুরুতে সেটাই করা উচিত। Vertical scaling মানে machine টাকেই বড় করা (বেশি CPU, RAM, disk)। প্রথম পদক্ষেপ হিসেবে এটাই সঠিক। কাজটা সহজ, code বদলাতে হয় না, আর নতুন কোনো problem ও তৈরি হয় না।

কিন্তু এর তিনটা কঠিন সীমা আছে:

  1. Physical ceiling: Hardware এর একটা শেষ সীমা আছে। সবচেয়ে বড় machine এ পৌঁছে গেলে আর বড় হওয়ার জায়গা থাকে না।
  2. Cost curve: দাম যে হারে বাড়ে, ক্ষমতা সেই হারে বাড়ে না। উপরের দিকে গেলে অল্প একটু বাড়তি ক্ষমতার জন্য অনেক বেশি টাকা খরচ করতে হয়।
  3. এখনো single point of failure: Machine যত বড়ই হোক, সব write এর জন্য এটা একটাই machine। এটা down হলে সব write থেমে যায়।
বাস্তব order টা এমন

আগে primary কে vertical scale করুন, কারণ এটা সহজ। তারপর read এর জন্য replica আর cache যোগ করুন (Session 03, 04)। এরপরও সবচেয়ে বড় single primary write এর চাপ সামলাতে না পারলে, কেবল তখনই shard করুন। Shard করবেন সবার শেষে, প্রথমে না।

০৩ একটা database কে shard করার basic idea টা কী?

মূল চিন্তাটা একটা ছোট প্রশ্ন থেকে আসে:

Clone নাকি Slice?

Replica হলো clone: সবার কাছে একই data, আর writer একটাই। কিন্তু প্রতিটা machine আলাদা data রাখলে কী হতো?

ধরুন, Machine A তে আছে user 1-1M, আর Machine B তে আছে user 1M-2M। এরা আলাদা data রাখছে। তাই দুজনেই একসাথে write নিতে পারবে। কোনো conflict হবে না, কারণ এরা কখনো একই row বদলায় না।

Replica: Clone Machine A user 1 - 10M Machine B user 1 - 10M একই data copy একটাই Writer (conflict এড়াতে) Shard: Slice Machine A user 1 - 5M Machine B user 5M - 10M আলাদা data আলাদা Writer, দুজনেই write নিতে পারে
বাম: Replica হলো clone। সব machine এ একই data, তাই writer একটাই। ডান: Shard হলো slice। প্রতিটা machine এ আলাদা data, তাই প্রতিটাই write নিতে পারে।
Sharding কী

Sharding মানে data কে কয়েকটা টুকরোয় ভাগ করা, আর প্রতিটা টুকরো আলাদা machine এ রাখা। প্রতিটা টুকরোকে বলে একটা shard। প্রতিটা machine আলাদা data রাখে, তাই প্রতিটা machine একসাথে নিজের write নিতে পারে। এভাবেই write horizontally scale হয়, অর্থাৎ machine এর সংখ্যা বাড়িয়ে।

Shard যোগ করলে write capacity বাড়ে। Write ceiling এভাবেই সরানো যায়।

০৪ কোন data কোন shard এ যাবে, সেটা কীভাবে ঠিক করবো?

Shard key কী

Shard key হলো সেই data field, যেটা দেখে ঠিক করা হয় একটা row কোন shard এ থাকবে। user_id দেখে shard করলে user_id ই আপনার shard key।

Sharding এ এটাই সবচেয়ে গুরুত্বপূর্ণ decision। ভুল করলে পরে কোনো hardware upgrade দিয়ে সেটা ঠিক করা যাবে না।

দুইটা main strategy

Range-based sharding: key কে কয়েকটা range এ ভাগ করা হয়, আর প্রতিটা range একটা shard এ যায়।

Shard A: user_id        1 - 1,000,000
Shard B: user_id 1,000,001 - 2,000,000

Hash-based sharding: key কে hash করা হয়, আর সেই hash value দেখে shard বেছে নেওয়া হয়।

shard_number = hash(user_id) % number_of_shards

Trade-off

Range-basedHash-based
Data distributionঅসমান। Data ক্রমানুসারে এলে hot shard তৈরি হয় (নতুন সব ID একটা shard এ জমে, বাকিগুলো idle থাকে)সমান। Data যে ক্রমেই আসুক, সব shard এ সমানভাবে ছড়িয়ে যায়
Range queryসহজ। কাছাকাছি row গুলো একটা shard এ একসাথে থাকেকঠিন। Row সব shard এ ছড়িয়ে থাকে, তাই সবাইকে query করতে হয়
মূল trade-off

Range-based পাশাপাশি data একসাথে রাখে, তাই range query এর জন্য ভালো। কিন্তু এতে hot shard এর risk থাকে। Hash-based data সমানভাবে ছড়ায়, কিন্তু পাশাপাশি data আলাদা আলাদা shard এ চলে যায়। সব ক্ষেত্রে সঠিক এমন কোনো choice নেই। আপনার application কীভাবে data read করে, সেটাই ঠিক করে দেয় কোনটা ভালো।

ভালো shard key এর গুণ

একটা shard key কাজের হয় তখনই, যখন এর দুইটা গুণ একসাথে থাকে।

  1. High cardinality: field টার অনেকগুলো আলাদা আলাদা value থাকতে হবে।

Shard key এর আলাদা value যতগুলো, shard ও সর্বোচ্চ ততগুলো হতে পারে। is_active এর value মাত্র দুইটা। তাই shard ও সর্বোচ্চ দুইটা: সব active user একটায়, সব inactive user আরেকটায়। এর বেশি scale করার জায়গাই নেই। country তে প্রায় ২০০টা value, তাই এটা কিছুটা ভালো। কিন্তু USA shard এ কোটি কোটি row থাকবে, আর Iceland shard এ মাত্র কয়েক হাজার। Value অনেক আছে, কিন্তু data সমানভাবে ছড়ায়নি। user_id এর value কোটি কোটি, তাই এটা ভালো। যত shard দরকার, তত তৈরি করা যায়।

  1. Even access: কোনো একটা value বাকিদের চেয়ে অনেক অনেক বেশি traffic পেলে চলবে না।

শুধু high cardinality থাকলেই যথেষ্ট না। user_id hash করলে data সমানভাবে ছড়ায়, প্রতিটা shard এ row এর সংখ্যা প্রায় সমান থাকে। কিন্তু ধরুন একজন celebrity এর post viral হলো। তার user_id যে shard এ আছে, সেটা তখন সেকেন্ডে লাখ লাখ request পায়। বাকি shard গুলো তখন প্রায় বসে থাকে। Data সমানভাবে ছড়ালেও traffic সমানভাবে ছড়ায়নি। এই অবস্থাকে বলে hot key।

উদাহরণ: E-commerce Orders Table

Shard keyসুবিধাDownside
order_idHigh cardinality, data সমানভাবে ছড়ায়“customer X এর সব order” সব shard এ ছড়িয়ে যায়
customer_idএকজন customer এর সব order এক shard এ থাকে। “আমার সব order দেখান” query দ্রুত হয়বিশাল কোনো corporate customer একাই hot shard তৈরি করে
countryতেমন কোনো সুবিধা নেইLow cardinality; data অত্যন্ত অসমানভাবে ছড়ায়
মূল lesson

Perfect shard key বলে কিছু নেই। সবচেয়ে বেশি যে query চলে (dominant access pattern), সেটা দেখে key বেছে নিন, আর trade-off টা মেনে নিন। আসলে shard key বেছে নেওয়ার সময় আপনি ঠিক করছেন, কোন problem গুলো আপনি মেনে নিতে রাজি।

০৫ cross-shard data দরকার হলে কী হবে?

এক database এ কিছু operation একদম সহজ ছিলো। Shard করার পর সেই operation গুলোই কঠিন, ধীর, বা অসম্ভব হয়ে যায়।

সমস্যা ১: Cross-shard Query

ধরুন query টা হলো “আজকের সব order দেখান”। customer_id দিয়ে shard করা থাকলে আজকের order গুলো সব shard এ ছড়িয়ে আছে। তাই আপনাকে সব shard এ query পাঠাতে হবে। সবচেয়ে ধীর shard টার উত্তরের জন্য অপেক্ষা করতে হবে। তারপর সব result নিজে জোড়া লাগাতে (merge করতে) হবে।

Scatter-Gather

সব shard এ query পাঠিয়ে result গুলো merge করার এই pattern কে বলে scatter-gather। এটা ধীর, আর shard বাড়লে আরো ধীর হয়।

সমস্যা ২: Cross-shard Join

SQL join শুধু একটা database এর ভেতরে কাজ করে। একসাথে লাগে এমন data আলাদা shard এ থাকলে database নিজে সেগুলো join করতে পারে না। তখন প্রতিটা shard থেকে data এনে application code এ join করতে হয়।

এই কারণেই একটা ভালো shard key একসাথে ব্যবহার হওয়া data কে একই shard এ রাখে।

সমস্যা ৩: Cross-shard Transaction

Transaction মানে কয়েকটা কাজকে একসাথে বেঁধে ফেলা, যাতে হয় সবগুলো কাজ হয়, নয়তো একটাও হয় না। ধরুন A এর account থেকে B এর account এ ৳১০০ পাঠাতে হবে। এক database এ এটা একটা নিরাপদ transaction: টাকা কাটা আর টাকা যোগ হওয়া, দুটোই হয়, নয়তো কোনোটাই হয় না।

কিন্তু A আর B আলাদা shard এ থাকলে দুই shard এর কাজকে একটা transaction এ বাঁধা যায় না। ধরুন A এর account থেকে টাকা কাটা গেলো, কিন্তু B এর account এ যোগ করার সময় error হলো। তখন ১০০ টাকা A এর account থেকে চলে গেলো, অথচ B পেলো না।

এটা সামলাতে distributed transaction লাগে (যেমন two-phase commit বা saga)। এটা computing এর সবচেয়ে কঠিন problem গুলোর একটা।

এক কথায় lesson

Sharding এক shard এর ভেতরের operation কে দ্রুত আর scalable করে। কিন্তু যে operation একাধিক shard ছুঁয়ে যায় (query, join, transaction), সেগুলো কঠিন হয়ে যায়। তাই sharding এর আসল দক্ষতা হলো এমনভাবে shard key বেছে নেওয়া, যাতে বেশিরভাগ operation একটা shard এর ভেতরেই শেষ হয়।

ভারী cross-shard analytics (“সব customer এর total revenue”) সাধারণত live shard এ চালানো হয় না। এই query গুলো সব shard এ scatter-gather করে, আর live user দের জন্য system ধীর করে দেয়। এর বদলে দুইটা উপায় আছে:

  • আলাদা analytics database: সব shard এর raw data একটা আলাদা read-only database এ copy করে রাখা হয়। সেখানে যেকোনো সময় যত ইচ্ছা ভারী query চালানো যায়। Live shard এ এর কোনো প্রভাব পড়ে না।
  • Scheduled bulk job: raw data কোথাও copy করা হয় না। বরং রাতে, যখন traffic কম, একটা job সব shard থেকে data এনে হিসাব করে। তারপর শুধু final result টা store করে, যেমন: আজকের total revenue ৳৪৭ লাখ।

০৬ যখন একটা shard বাকিদের চেয়ে অনেক বেশি traffic পায়, তখন কী হবে?

Data সমানভাবে ছড়ালেই traffic সমানভাবে ছড়ায় না। এই problem এর দুইটা আলাদা রূপ আছে।

Version 1: খারাপ Data Distribution থেকে Hot Shard

খারাপ shard key এর কারণে একটা shard বাকিদের চেয়ে অনেক বেশি row রাখে। উদাহরণ: country দিয়ে shard করলে India shard এ 400M row থাকে, আর Iceland shard এ মাত্র 80,000।

Version 2: Uneven Access থেকে Hot Key

Data সমানভাবে ছড়ানো আছে, কিন্তু একটা নির্দিষ্ট key অনেক বেশি traffic পাচ্ছে। উদাহরণ: user_id hash করে post shard করা হয়েছে। সব shard এ row এর সংখ্যা প্রায় সমান। কিন্তু একজন celebrity এর viral post সেকেন্ডে লাখ লাখ read পাচ্ছে। ওই key যে shard এ আছে, সেটা overloaded হয়ে যায়। এর কারণ row এর সংখ্যা না। কারণ হলো একটা row অস্বাভাবিক রকম popular হয়ে গেছে।

Runtime Fix গুলো
  1. Hot key টা cache করুন (Session 04)। তাহলে বেশিরভাগ read shard পর্যন্ত পৌঁছায়ই না। সাধারণত এটাই প্রথম পদক্ষেপ, কারণ এটা সবচেয়ে সস্তা আর দ্রুত। সবাই যখন একই জিনিস পড়ছে, তখন cache ই সবচেয়ে ভালো কাজ করে।
  2. Hot shard টা replicate করুন (Session 03)। Read replica যোগ করে read load কয়েকটা machine এ ছড়িয়ে দিন।
  3. ওই একটা key কে আলাদা shard দিন (dedicated shard)। কোনো key সবসময়ই বিশাল হলে (যেমন একটা celebrity account), তাকে তার নিজের shard দিয়ে দিন।
Lesson

ভালো shard key design এর সময়েই Version 1 (খারাপ distribution) আটকে দেয়। কিন্তু Version 2 (hot key) আটকাতে পারে না, কারণ virality আগে থেকে বোঝা যায় না। Version 2 সামলাতে হয় runtime এ, caching আর replica দিয়ে। এগুলো আপনি আগেই শিখেছেন। Sharding এগুলোর জায়গা নেয় না, এগুলোর সাথে মিলে কাজ করে।

কোন case এ কোন fix:

  • Cache: যখন traffic মূলত read, সবাই একই key চাইছে, আর ভিড়টা সাময়িক (একটা viral মুহূর্ত)।
  • Dedicated shard: যখন key টা viral event ছাড়াই সবসময় বেশি traffic পায়, আর ওই key এর data ও অনেক বেশি।

০৭ পরে যখন একটা shard অ্যাড করতে হবে, তখন কী হবে?

Naive % N Hashing এ Resharding Problem

ধরুন 4 টা shard আছে, আর formula হলো shard = hash(key) % 4। Hash value 10 থেকে 14 কোন কোন shard এ পড়ে, দেখুন:

10 % 4 = 2    11 % 4 = 3    12 % 4 = 0    13 % 4 = 1    14 % 4 = 2

এখন একটা পঞ্চম shard যোগ করলে formula হয়ে যায় % 5:

10 % 5 = 0    11 % 5 = 1    12 % 5 = 2    13 % 5 = 3    14 % 5 = 4

প্রতিটা key ই অন্য একটা shard এ চলে গেলো। ভাগের সংখ্যাটা (divisor) বদলালে প্রায় প্রতিটা key এর result বদলে যায়। ধরুন আপনার 500M row আছে। তখন একটা machine যোগ করতে গেলে কোটি কোটি row এক machine থেকে আরেক machine এ সরাতে হবে। Shard যোগ বা বাদ দিয়ে data নতুন করে সাজানোর এই কাজকে বলে resharding। Naive hashing এই কাজটাকে বিশাল data movement এ পরিণত করে।

Fix: Consistent Hashing

Naive % N এর মূল সমস্যা হলো, key কোন shard এ যাবে সেটা shard এর সংখ্যার উপর নির্ভর করে। N বদলালে প্রায় সব হিসাব নতুন করে হয়। Consistent hashing এই সংখ্যাটাকে হিসাব থেকে সরিয়ে দেয়। এতে key এর জায়গা fixed থাকে, আর shard কয়টা আছে তাতে কিছু যায় আসে না।

Ring তৈরি হয় কীভাবে

০ থেকে ২৩২ পর্যন্ত সংখ্যাগুলোকে একটা বৃত্ত বা ring হিসেবে ভাবুন। শেষ সংখ্যার পর আবার শুরুতে ফিরে আসে। এই ring এ দুই ধরনের জিনিস বসানো হয়:

  1. Shard গুলো: প্রতিটা shard এর identifier (নাম বা IP) hash করলে ring এ তার একটা fixed জায়গা পাওয়া যায়। Shard A, B, C, D সবার জায়গাই এভাবে ঠিক হয়, আর এই জায়গা বদলায় না।
  2. Key গুলো: প্রতিটা data key (যেমন user_id) hash করলে ring এ তার জায়গা পাওয়া যায়। এই জায়গাটাও fixed। Shard কয়টা আছে, তার সাথে এর কোনো সম্পর্ক নেই।

Routing rule: key এর জায়গা থেকে ring ধরে clockwise এগোন। প্রথম যে shard পাবেন, সেটাই ওই key এর shard।

বাম diagram এ user_m hash করে ring এ একটা জায়গা পায়। সেখান থেকে clockwise এগোলে প্রথমে A আসে। তাই user_m এর shard হলো A। একইভাবে user_7 clockwise এগিয়ে সরাসরি B তে পৌঁছায়, তাই user_7 যায় shard B তে।

নতুন shard যোগ হলে কী বদলায়

এবার E যোগ করতে হবে। E এর identifier hash করলে সে ring এ D আর A এর মাঝে একটা জায়গা পায়। এখন D এর পর clockwise এগোলে আগে E আসে, তারপর A।

ফলে শুধু D থেকে E পর্যন্ত arc এর key গুলো A ছেড়ে E তে যায়। বাকি সব key এর clockwise প্রতিবেশী আগের মতোই থাকে, তাই তাদের কিছুই বদলায় না।

N টা shard থাকলে ring মোটামুটি N টা arc এ ভাগ হয়। নতুন shard একটা arc এর মাঝে বসে, আর শুধু সেই arc এর একটা অংশের data নেয়। গড়ে এটা মোট data এর প্রায় 1/N ভাগ।

আগে: ৪টা Shard clockwise A B C D user_7 → B user_m → A user_m clockwise → D পার করে → A তে পৌঁছায় E যোগ হওয়ার পর affected ~1/5 data A B C D E নতুন! user_7 → B user_m → E শুধু D-E arc এর key গুলো E তে গেলো। বাকি সব অপরিবর্তিত।
বাম: ৪টা shard; user_m clockwise এগিয়ে A তে পৌঁছায়। ডান: D আর A এর মাঝে E যোগ হলো। শুধু highlighted arc এর key গুলো নতুন E তে গেলো, বাকি সব আগের মতোই আছে।

Diagram এ দেখুন, শুধু highlighted D থেকে E arc এর key গুলো E তে গেছে। user_7 আগে B তে ছিলো, এখনো B তেই আছে, কারণ ওর arc এ E বসেনি। বাকি সব আগের মতোই আছে।

কেন কাজ করে

Naive % N এ প্রতিটা key এর shard সরাসরি N এর উপর নির্ভর করে (hash(key) % N)। তাই N বদলালে প্রায় সব result বদলে যায়।

Consistent hashing এ key এর ring position নির্ভর করে শুধু hash(key) এর উপর। এখানে N এর কোনো ভূমিকা নেই। তাই শুধু নতুন shard এর ঠিক আগের arc এর data সরে, বাকি সব যেমন ছিলো তেমনই থাকে।

Virtual Nodes

Diagram এ চারটা shard ঠিক ৯০° দূরে দূরে বসানো। দেখতে সুন্দর, কিন্তু বাস্তবে এমন হয় না। বাস্তবে shard এর identifier hash করলে ring এ এলোমেলো জায়গা পড়ে। তখন shard গুলোর মাঝের arc গুলো ছোট বড় হয়ে যায়। একটা shard ring এর ৪০% পেতে পারে, আরেকটা মাত্র ৫%। বড় arc মানে বেশি key, আর বেশি key মানে বেশি load। Data সমানভাবে ছড়ানোর কথা ছিলো, কিন্তু ছড়ালো না।

Virtual nodes এই সমস্যার সমাধান। প্রতিটা physical shard কে ring এ একটা জায়গার বদলে অনেকগুলো জায়গায় বসানো হয় (সাধারণত ১৫০-২০০টা)। এই প্রতিটা জায়গাকে বলে একটা virtual node। Shard A এর ১৫০টা virtual node পুরো ring এ ছড়িয়ে থাকে, Shard B এর ১৫০টাও তাই। জায়গাগুলো এলোমেলো হলেও ১৫০টা ছোট arc যোগ করলে গড়ে প্রায় সমান হয়ে যায়। তখন কোনো physical shard ই তার ন্যায্য ভাগের চেয়ে অনেক বেশি বা অনেক কম key পায় না।

০৮ এখন system টা দেখতে কেমন, আর downside গুলো কী?

আগে সব write একটা primary তে যেতো। এখন তিনটা আলাদা primary আছে, আর প্রতিটা নিজের write নিজে নিচ্ছে। এভাবেই write scale হয়। প্রতিটা shard এর এখনো নিজের replica আর backup আছে, আর সামনে cache আছে।

CLIENT LOAD BALANCER APPLICATION CACHE SHARD ROUTER SHARDS ইউজার ইউজার ইউজার LB-1 Active LB-2 Passive heartbeat Stateless Web 1 App Server Web 2 App Server Web 3 App Server Redis Cache cache-aside · TTL · delete-on-write cache miss Shard Router hash(shard_key) → correct shard Shard A Primary Shard B Primary Shard C Primary Replica Layer প্রতিটা Shard Primary নিজের Replica তৈরি করে Read Traffic এই Layer থেকে Serve হয় · Write সরাসরি সংশ্লিষ্ট Primary তে আসে Sharding replication আর caching কে replace করে না, এদের সাথে নিয়েই কাজ করে
Web থেকে Cache হলো মূল read path (highlighted)। Cache miss হলে request Shard Router হয়ে সঠিক Shard Primary তে যায়। প্রতিটা Shard Primary নিজের replica সামলায়, আর read সেই replica layer থেকে serve হয়।

Sharding আপনাকে কী দিলো

  • Write scalability: replica আর cache যেটা দিতে পারেনি, sharding সেটা দেয়।
  • Data capacity: যে data এক machine এ ধরে না, সেটা অনেক machine এ ভাগ হয়ে যায়।
  • Smaller blast radius: একটা shard fail করলে শুধু তার ভাগের user রা affected হয়।

Downside গুলো

  • Cross-shard query, join, transaction কঠিন বা অসম্ভব: এগুলোর জন্য scatter-gather, application code এ join, বা distributed transaction দরকার হয়।
  • Shard key প্রায় স্থায়ী একটা decision: ভুল choice করলে hot shard তৈরি হয়, নয়তো বারবার cross-shard operation করতে হয়।
  • Consistent hashing থাকলেও resharding সহজ না: কাজটা সহনীয় হয়, কিন্তু free হয় না।
  • Operational complexity অনেক বেড়ে যায়: এখন অনেকগুলো database cluster monitor করতে হয়, backup নিতে হয়, fail over করতে হয়, আর debug করতে হয়।
  • Application code আরো complex হয়: routing, cross-shard handling আর partial failure (কিছু shard উত্তর দিলো, কিছু দিলো না), এই সবকিছু code এর ভেতরেই সামলাতে হয়।
সবচেয়ে গুরুত্বপূর্ণ lesson

Write bottleneck প্রমাণিত না হওয়া পর্যন্ত shard করবেন না। এটা scaling এর শেষ ধাপ, প্রথম ধাপ না। আগে vertical scaling, read replica আর caching শেষ করুন। একটা primary সত্যিই write এর চাপ সামলাতে না পারলে, আর তার যথেষ্ট প্রমাণ থাকলে, তখনই sharding এর স্থায়ী complexity মেনে নিন। অনেক team খুব আগেভাগে shard করে ফেলে। তারপর বছরের পর বছর তার cost বহন করে, অথচ তখন হয়তো sharding এর দরকারই ছিলো না।

০৯ মগজে প্রেশার দিন

আগে নিজে ভাবুন। তারপর দেখুন: সবাই যেভাবে ভাবে, কেন সেটা অসম্পূর্ণ, আর আসল insight টা কোথায়।

প্রশ্ন ১: Replication যেখানে পারেনি, Sharding সেখানে write scale করে কেন?
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“Replication এ সব machine এ একই data থাকে, তাই writer একটাই হতে হয়। Sharding এ প্রতিটা machine এ আলাদা data থাকে, তাই অনেকে write করতে পারে।”

এই চিন্তা সঠিক দিকেই যাচ্ছে, কিন্তু একটা সূক্ষ্ম বিষয় বাদ পড়ে যাচ্ছে।

যে জায়গাটা সবাই miss করে

শুধু “অনেকে write করতে পারে” বললে পুরো কথা বলা হয় না। আসল কারণ হলো, shard গুলোর মধ্যে কোনো conflict হয় না। দুইটা shard কখনো একই row বদলায় না। তাই এক shard এর write এর সাথে আরেক shard এর write এর ঝামেলা বাধার সুযোগই নেই।

তবে একটা সতর্কতা আছে। “Sharding এ কোনো sync issue নেই” বলাটা একটু ভুল। প্রতিটা shard এর ভেতরে replica গুলো এখনো primary এর সাথে sync করে। তাই প্রতিটা shard এ replication lag এখনো থাকে। যেটা চলে যায়, সেটা হলো দুই shard এর write এর মধ্যে conflict।

সঠিক ভাষায়

Replica তে writer একটাই থাকে, কারণ সবার কাছে একই data, অর্থাৎ shared state। Shared state থাকলে কোন write কোন ক্রমে apply হবে, সেটা নিয়ে সবাইকে একমত হতে হয়। এই সমন্বয় নিশ্চিত করতেই একটা writer রাখা হয়, আর বাকিরা তাকে অনুসরণ করে।

Shard এ এই problem নেই, কারণ কোনো shared state নেই। Shard A রাখে user 1-5M, Shard B রাখে user 5M-10M। তারা কখনো একই row বদলায় না, তাই তাদের মধ্যে কোনো সমন্বয়ের দরকার হয় না।

সমন্বয় লাগে না বলে প্রতিটা shard নিজে নিজেই write নিতে পারে, কারো অনুমতির জন্য অপেক্ষা করতে হয় না। তাই একটা shard যোগ করলে একটা নতুন স্বাধীন write এর পথ যোগ হয়। 4 টা shard থেকে 8 টা করলে write capacity মোটামুটি দ্বিগুণ হয়। একেই বলে horizontal write scaling।

প্রশ্ন ২: Chat app এ একটা messages table shard করতে হবে। সবচেয়ে common query হলো "conversation #123 এর সব message order এ দেখাও।" Shard key কী হবে? Downside কী?
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“message_id দিয়ে shard করবো। এর high cardinality আছে।”

High cardinality ঠিকই আছে। কিন্তু এখানে সবচেয়ে বেশি চলা query টাকে উপেক্ষা করা হয়েছে।

সঠিক choice আর কেন

conversation_id দিয়ে shard করা উচিত। কারণ:

  • একটা conversation এর সব message তখন একটা shard এ থাকে।
  • তাই “conversation #123 এর সব message দেখাও” query টা একটা shard থেকেই উত্তর পায়। Scatter-gather লাগে না।

message_id দিয়ে shard করলে একটা conversation এর message সব shard এ ছড়িয়ে পড়তো। তখন প্রতিটা read request scatter-gather হয়ে যেতো। সব shard এ query পাঠাতে হতো, আর সবচেয়ে ধীর shard টার জন্য অপেক্ষা করতে হতো।

Downside টা কী

বিশাল কোনো active group chat (ধরুন, লাখ লাখ member এর একটা group) তার shard কে overload করে দেয়। একটা মাত্র conversation এর কারণেই পুরো shard hot হয়ে যায়।

মূল pattern

সবচেয়ে বেশি চলা query টা খুঁজে বের করুন, তারপর সেই অনুযায়ী shard key বেছে নিন। একসাথে ব্যবহার হওয়া data একই shard এ রাখুন। এতে hot shard এর risk থাকে, সেটা মেনে নিন, কারণ বিকল্পটা (scatter-gather) আরো খারাপ।

প্রশ্ন ৩: ৫,০০০ user এর একটা startup বলছে "এখনই shard করবো, যাতে পরে scale নিয়ে কখনো চিন্তা করতে না হয়।" কীভাবে বোঝাবেন যে এটা ভুল?
উত্তর দেখুন আগে নিজে ভাবুন

সবাই যেভাবে ভাবে

“Shard করা উচিত নয়। এতে complexity বাড়ে, transaction কঠিন হয়, আর shard key বেছে নেওয়াও কঠিন।”

এই কারণগুলো সত্যি। কিন্তু সবচেয়ে জোরালো যুক্তিটা এখানে বাদ পড়ে গেছে।

আরো জোরালো যুক্তি

৫,০০০ user এর একটা startup এর কোনো write-scaling problem নেই। একটা সাধারণ database ই এই চাপ সহজে সামলায়। যে problem নেই, সেটা solve করতে গেলে বিশাল আর স্থায়ী complexity ঘাড়ে নিতে হবে।

সঠিক orderকেন
Simple single database দিয়ে শুরু করুনএখন কোনো problem নেই
Vertical scale করুনসহজ, code বদলাতে হয় না, নতুন problem আসে না
Replica আর cache যোগ করুনএতে read scale হয়
Write bottleneck প্রমাণিত হলে তখন shard করুনতখন complexity টা যুক্তিসঙ্গত

আগেভাগে shard করা মানে কাল্পনিক একটা problem solve করতে গিয়ে কয়েকটা সত্যিকারের problem তৈরি করা। যেমন cross-shard join এর জটিলতা, ভুল shard key এর স্থায়ী ফল, আর operational complexity।

সোজা কথা

কোনো engineering decision নেওয়ার আগে যাচাই করুন: সমস্যাটা কি আদৌ আছে? ৫,০০০ user এ write bottleneck নেই, তার কোনো প্রমাণও নেই। প্রমাণ ছাড়া সমাধান বানানোকে বলে premature optimization।

১০ যে টার্মগুলো শিখলাম

টার্মমানে
ShardingData কে কয়েকটা টুকরোয় (shard) ভাগ করা। প্রতিটা machine আলাদা data রাখে।
Shardভাগ করা data এর একটা টুকরো। এটা slice, clone না।
Shard keyযে data field দেখে ঠিক করা হয় একটা row কোন shard এ থাকবে।
Range-based shardingKey এর range দেখে ভাগ করা। Range query সহজ, কিন্তু hot shard এর risk আছে।
Hash-based shardingKey hash করে shard বেছে নেওয়া। Data সমানভাবে ছড়ায়, কিন্তু range query কঠিন।
High cardinalityField এর অনেকগুলো আলাদা value থাকা। ভালো shard key এর একটা গুণ।
Hot shardযে shard বেশি DATA রাখার কারণে overloaded। খারাপ distribution থেকে আসে (Version 1)।
Hot keyযে একটা key বেশি TRAFFIC পাওয়ার কারণে overloaded। অসমান access থেকে আসে (Version 2)।
Scatter-gatherসব shard এ query পাঠিয়ে result merge করা। Cross-shard query তে লাগে।
Transactionকয়েকটা কাজকে একসাথে বাঁধা, যাতে হয় সবগুলো হয়, নয়তো একটাও না।
Cross-shard join / transactionএকাধিক shard ছুঁয়ে যাওয়া operation। কঠিন বা অসম্ভব; application code এ সামলাতে হয়, বা distributed transaction লাগে।
ReshardingShard যোগ বা বাদ দিয়ে data নতুন করে সাজানো। % N এ বিশাল data movement, consistent hashing এ সহনীয়।
Consistent hashingKey আর shard কে একটা ring এ বসানো, যাতে shard যোগ বা বাদ দিলে শুধু প্রায় 1/N data সরে।
Virtual nodesপ্রতিটা physical shard কে ring এ অনেক জায়গায় বসানো, যাতে load সমান থাকে।
Shard routerযে component shard key hash করে request টা সঠিক shard এ পাঠায়।

১১ শর্ট সামারি

  1. Sharding data কে টুকরো করে অনেক machine এ ছড়িয়ে দেয়, যাতে প্রতিটা machine নিজের write নিতে পারে। Write scale করার এটাই একমাত্র উপায়, যেটা replica আর cache পারে না।
  2. Shard key সবচেয়ে গুরুত্বপূর্ণ decision। এর high cardinality আর even access থাকতে হবে। এটাই ঠিক করে কোন operation দ্রুত থাকবে (single-shard), আর কোনটা কঠিন হবে (cross-shard)।
  3. Naive % N hashing এ একটা shard যোগ করলে প্রায় সব data সরে যায়। Consistent hashing key আর shard কে একটা ring এ বসিয়ে এটা ঠিক করে, তখন শুধু প্রায় 1/N data সরে।

১২ মনে রাখুন