Course-এর সূচি

15.4

DynamoDB: Key, Capacity আর Consistency

table আর item, partition key আর sort key, RCU/WCU, on-demand বনাম provisioned, consistency আর hot partition।

30 মিনিট পড়া + Animation

টিপস: ডটেড-আন্ডারলাইন করা যেকোনো শব্দে ট্যাপ বা Hover করলে তার অর্থ দেখবে।

এই লেসন শেষে তুমি পারবে

  • Amazon DynamoDB কী, সেটা বলতে (serverless NoSQL key-value আর document database, তিনটা AZ-এ replicate করা, IAM দিয়ে সুরক্ষিত), আর কখন এটা relational database-এর চেয়ে ভালো বা খারাপ মানায়, সেটা বুঝতে।
  • Table, item আর attribute, ৪০০ KB-র item limit আর data type-গুলো বোঝাতে, আর primary key নকশা করতে: শুধু partition key, নাকি one-to-many data আর range query-র জন্য partition key সাথে sort key।
  • Partition key কীভাবে item-কে partition-এ বসায়, কম cardinality-র key কেন hot partition বানায়, আর প্রতি partition-এ ৩,০০০ RCU আর ১,০০০ WCU-র ছাদ মানে কী, সেটা ব্যাখ্যা করতে।
  • Eventually consistent আর strongly consistent read আলাদা করতে (তিনটা replica-তে কীভাবে কাজ করে, কোনটার দাম কত, কোথায় strong read পাওয়া যায় না), আর ঠিকটা বাছতে।
  • যেকোনো item size, read-এর ধরন আর write-এর ধরনের জন্য RCU আর WCU হিসাব করতে, ৪ KB / ১ KB রাউন্ডিং আর transaction-এর দ্বিগুণ খরচসহ, আর সেখান থেকে মাসের bill বের করতে।
  • Scan-এর বদলে Query ব্যবহার করতে, filter expression কী বাঁচায় আর কী বাঁচায় না জানতে, আর বড় scan যাতে live traffic-এর ক্ষতি না করে, সেটা সামলাতে।
  • Provisioned capacity (auto scaling সহ) আর on-demand capacity-র মধ্যে বাছতে, আর এদের সীমাগুলো জানতে: auto scaling-এর দেরি, burst capacity, আগের peak-এর দ্বিগুণের নিয়ম, pre-warming, mode বদলের নিয়ম।
  • ProvisionedThroughputExceededException আর পরীক্ষার সেই চেনা hot-partition প্রশ্নের কারণ ধরতে, আর দরকারমতো DAX, key-এর নকশা, write sharding, on-demand বা backoff দিয়ে সারাতে; adaptive capacity, transaction, PartiQL আর Standard-IA table class কোথায় বসে, তাও জানতে।

#DynamoDB আসলে কী

Chapter 7-এ আমরা relational database শিখেছি: RDS আর Aurora, যেখানে table, join, SQL আছে, আর একটা instance তুমি বাছো, মাপ ঠিক করো। অনেক serverless application-এর কিন্তু অন্য কিছু দরকার: এমন একটা database, যেখানে বাছার, patch করার বা scale করার মতো কোনো server নেই, এক হাজার item থাকুক বা একশো কোটি, কয়েক millisecond-এ উত্তর দেয়, আর bill হয় প্রতি request ধরে। সেটাই Amazon DynamoDB।

  • NoSQL, key-value আর document। তুমি item রাখো, আর key দিয়ে ফেরত পড়ো। Join নেই, table জুড়ে ইচ্ছেমতো SQL query-ও নেই; application কী কী প্রশ্ন করবে, সেই হিসাবে key নকশা করতে হয়।
  • Fully managed আর serverless। কোনো instance নেই, storage আগে থেকে বরাদ্দ করার ব্যাপার নেই, maintenance window নেই, patch নেই। Table বানাও, ব্যবহার শুরু করো।
  • Highly available। প্রতিটা write acknowledge হওয়ার আগে Region-এর তিনটা Availability Zone-এ তিনবার জমা হয় (নিচে বিস্তারিত)। একটা AZ হারালেও acknowledge হওয়া কোনো data হারায় না।
  • বিশাল workload সামলায়। প্রতি সেকেন্ডে লাখ লাখ request, লক্ষ কোটি item, এক table-এই শত শত terabyte, আর সবসময় এক অঙ্কের millisecond latency। Table-এর size-এর কার্যত কোনো সীমা নেই।
  • IAM দিয়ে নিরাপত্তা। Database-এর আলাদা user বা password নেই: প্রতিটা request একটা AWS API call, IAM credential দিয়ে sign করা, তাই কে কোন table পড়বে বা লিখবে, ঠিক করে IAM policy (বা Lambda function-এর execution role)। Data সবসময় at rest encrypted থাকে।
  • খরচ। হয় প্রতি request-এ দাম (on-demand), নয়তো যত capacity provision করো তার ঘণ্টা ধরে দাম, সাথে storage। প্রতি Region-এ ২৫ RCU, ২৫ WCU আর ২৫ GB-র free tier (provisioned, Standard table class) ছোট app-কে চিরকাল বিনা পয়সায় চালায়।

Database chapter-এর notes-এর এক লাইনের সারকথাটাই পরীক্ষারও: “serverless key-value store with massive scale” → DynamoDB, millisecond latency; microsecond latency চাইলে সেটা হয় DynamoDB + DAX (lesson 15.5)।

RDS / Aurora (lesson 7.1–7.4)DynamoDB

  • Relational: নির্দিষ্ট schema, SQL, join, ইচ্ছেমতো query, জটিল report।
  • Instance class তুমি বাছো; বড় করতে size বদলাও, পড়ার চাপ ভাগ করতে read replica।
  • Connection, user আর password (বা IAM auth); থাকে তোমার VPC-র ভেতরে।
  • সবচেয়ে ভালো যখন কী কী প্রশ্ন করা হবে আগে থেকে সব জানা নেই, বা data-গুলো একটা আরেকটার সাথে গভীরভাবে জড়ানো।
  • Key-value আর document: শুধু key নির্দিষ্ট; প্রতিটা item-এর attribute আলাদা হতে পারে; join নেই।
  • কোনো instance নেই: capacity একটা সংখ্যা মাত্র (বা প্রতি request-এ দাম), storage নিজে নিজে বাড়ে।
  • IAM দিয়ে sign করা HTTPS API call; একটা public AWS endpoint (বা VPC gateway endpoint, lesson 15.5)।
  • সবচেয়ে ভালো যখন access pattern জানা (ID দিয়ে user আনো, একজন driver-এর trip সময় ধরে দেখাও) আর যেকোনো scale-এ দ্রুত থাকতেই হবে।

#Table, item আর attribute

ধারণাএটা কীRelational-এ যেটার সমান
Tableএক Region-এ item-এর একটা নামওয়ালা সংগ্রহ। এর primary key ঠিক হয় বানানোর সময়, পরে আর কখনো বদলানো যায় না।Table
Itemএকটা record, তার primary key দিয়ে চেনা যায়। একটা table-এ যত খুশি item থাকতে পারে।Row
AttributeItem-এর ভেতরে একটা নাম আর একটা value। Key ছাড়া কিছুই আগে থেকে ঘোষণা করতে হয় না: সময়ের সাথে attribute যোগ করা যায়, আর একই table-এর দুটো item-এর attribute পুরো আলাদা হতে পারে।Column

শুধু key নির্দিষ্ট বলেই DynamoDB-কে বলে schema-less: কাল থেকে নতুন item-এ একটা loyalty_points attribute যোগ করতে পারো, কোনো migration ছাড়াই; পুরোনো item-এ সেটা থাকবে না, ব্যস (না-থাকা attribute null হিসেবেও জমা হয় না)।

Data type। একটা attribute-এর value এগুলোর কোনো একটা:

  • Scalar: String, Number, Binary, Boolean, Null।
  • Document: List (যেকোনো value-র ক্রমানুসারে তালিকা) আর Map (ভেতরে ভেতরে নাম-value জোড়া, JSON object-এর মতো), ৩২ স্তর পর্যন্ত।
  • Set: String Set, Number Set, Binary Set (একই ধরনের অনন্য value)।

৪০০ KB-র সীমা। একটা item, attribute-এর নাম আর value মিলিয়ে, সর্বোচ্চ ৪০০ KB। তাই লম্বা attribute নাম প্রতিটা item-এ জায়গা খায়। বড় জিনিস (ছবি, PDF, লম্বা log) যায় S3-এ, আর item-এ থাকে শুধু object-এর key আর অল্প কিছু metadata। ছোট item সস্তাও, কারণ capacity-র দাম ধরা হয় item-এর size দেখে।

#একটা উদাহরণ table

ঢাকার একটা quiz-game app প্রতি খেলোয়াড়ের প্রতি খেলার জন্য একটা করে item রাখে:

user_id (partition key)game_id (sort key)scoreresultঅন্য attribute
u-7791g-442192winstreak: 3
u-7791g-518014lose
u-8730g-189477winbadge: “Eid champion”
u-8730g-4521(খেলা শুরু হয়েছে, শেষ হয়নি: এখনো score নেই)

দুটো row-তে u-7791, দুটোতে u-8730: তবু এরা আলাদা item, কারণ জোড়াটা (user_id, game_id) অনন্য। শেষ item-এ score-ই নেই, আর সেটা চলে। Network-এ item যায় type-সহ JSON হয়ে:

{ "user_id": {"S": "u-7791"}, "game_id": {"S": "g-4421"},
  "score": {"N": "92"}, "result": {"S": "win"}, "streak": {"N": "3"} }

#Primary key: partition key আর sort key

প্রতিটা table-এর primary key দুই রকমের যেকোনো একটা, আর এই বাছাই-ই ঠিক করে কোন প্রশ্ন দ্রুত করা যাবে।

#শুধু partition key (simple primary key)

একটা attribute, যেমন user_id। Table-এ এর value অনন্য হতেই হবে: একই user_id দিয়ে দ্বিতীয়বার PutItem করলে আগের item বদলে যায়। DynamoDB value-টাকে ভেতরের একটা hash function-এ চালায়, আর ফল দেখে ঠিক হয় item কোন partition-এ (physical storage) থাকবে; এই কারণেই API এটাকে বলে HASH key। Item পড়তে হয় GetItem দিয়ে, ঠিক key দিয়ে।

#Partition key সাথে sort key (composite primary key)

দুটো attribute, যেমন driver_id + trip_time। অনেক item একই partition key ভাগ করতে পারে, যতক্ষণ জোড়াটা অনন্য। একই partition key-র সব item এক partition-এ একসাথে থাকে, একটা item collection হয়ে, sort key অনুযায়ী সাজানো (এটাই RANGE key)। এতে দুটো ক্ষমতা আসে, যা simple key-তে নেই:

  • One-to-many: একজন driver, হাজারটা trip, সব একটা Query-তে।
  • Sort key-র ওপর range query: দুই সময়ের between, একটা তারিখ দিয়ে begins_with, <, >, নতুনটা আগে বা পুরোনোটা আগে।

Notes-এর পরীক্ষার কৌশল: “1-to-many relationship” বা “range query” (এক তারিখ থেকে আরেক তারিখের data) → composite primary key। আর সবসময় high cardinality-র partition key বাছো (অনেক আলাদা value, যেমন user_id), যাতে চাপ ছড়িয়ে পড়ে আর কোনো partition hot না হয়।

#Partition key কীভাবে item বসায়

একটা table অনেকগুলো partition-এ ভাগ করা, প্রতিটার হাতে hash value-এর একটা অংশ, SSD-তে রাখা আর তিনবার replicate করা। Table বড় হলে (একেকটা partition-এ মোটামুটি ১০ GB ধরে) বা throughput বাড়লে DynamoDB নিজেই partition যোগ করে, data সরিয়ে নেয়। তুমি এগুলো কখনো দেখো না, সামলাওও না, কিন্তু দুটো সংখ্যা জানতেই হবে, কারণ table-এর মোট capacity যতই হোক, এগুলো বদলায় না:

  • একটা partition প্রতি সেকেন্ডে সর্বোচ্চ ৩,০০০ read unit আর ১,০০০ write unit দেয়;
  • একটা item-ও তাই: একটা key কখনো এই সংখ্যা ছাড়াতে পারে না।

Item কোথায় থাকবে: ঠিক করে partition key

ঢাকার একটা ride-sharing app তার user আর trip রাখে DynamoDB-তে। Request router প্রতিটা item-এর partition key hash করে সেটাকে একটা partition-এ পাঠায়। দেখো একটা simple key, range query সহ একটা composite key, ভুল করে বাছা একটা key, আর একটা অতিরিক্ত বড় item-এর কী হয়।

পরিস্থিতি

Users table, key হলো user_id: প্রতি key-তে একটাই item, এক লাফেই খুঁজে পাওয়া যায়।

Ride app
Request routerhash(partition key)
Partition 1
Partition 2
Partition 3
PutItem u-1001

Item

user_id

u-1001

name

Hasan

phone

+880 1700-000000

ধাপ 1 / 4
1

App PutItem দিয়ে একজন নতুন যাত্রীকে save করে। Users table-এর primary key simple: শুধু partition key user_id। বাকি attribute-এর কোনো schema নেই; এই item-এ ঘটনাচক্রে নাম আর ফোন নম্বর আছে।

#ভালো partition key কীভাবে বাছবে

Partition keyCardinalityরায়
user_id, order_id, device_idলাখ লাখ value, request সমানভাবে ছড়ানোভালো
driver_id + sort key trip_timeঅনেক driver; প্রতি driver-এর trip একসাথেভালো, “একজন driver-এর trip সময় ধরে”-এর জন্য
status (৩টা value), country (প্রায় সবাই বাংলাদেশে)হাতে-গোনা কয়েকটা valueখারাপ: hot partition
date (আজকের তারিখ)আজকের সব write এক key-তেখারাপ: আজকের দিনটা সবসময় hot
Sale-এর সময় product_idঅনেক value, কিন্তু একটা product-ই বেশিরভাগ read পায়Key ভালো, কিন্তু item hot: cache লাগবে (নিচে)

#Item লেখা আর পড়া

রোজকার API (সবই IAM দিয়ে sign করা HTTPS call):

  • PutItem পুরো একটা item লেখে (নতুন বানায় বা বদলে দেয়)। Overwrite আটকাতে attribute_not_exists(user_id)-এর মতো একটা condition expression দাও; শর্ত না মিললে ConditionalCheckFailedException আসে, আর write capacity তবুও খরচ হয়।
  • GetItem পুরো primary key দিয়ে একটা item পড়ে।
  • UpdateItem কিছু attribute বদলায় (বা item বানায়), atomic counter-সহ (SET views = views + 1)। একটা version attribute-এর ওপর শর্ত দিলে পাওয়া যায় optimistic locking: “version এখনো ৭ থাকলে তবেই update করো”।
  • DeleteItem একটা item মুছে দেয়।
  • BatchGetItem (১০০টা পর্যন্ত item, ১৬ MB) আর BatchWriteItem (২৫টা পর্যন্ত put বা delete) round trip বাঁচায়, কিন্তু সব-অথবা-কিছুই-না না: যেগুলো হয়নি সেগুলো ফেরত আসে, আবার চেষ্টা করতে হয় তোমাকেই।
  • Query আর Scan অনেক item পড়ে (নিচে আলাদা অংশ)।

#Consistency: eventually নাকি strongly consistent read

দুই রকম read বুঝতে হলে আগে জানতে হবে DynamoDB data রাখে কীভাবে। AWS-এর documentation বলে প্রতিটা item জমা থাকে তিনটা Availability Zone-এ; নিচের খুঁটিনাটি এসেছে DynamoDB-র ভেতরের গঠন নিয়ে AWS-এর engineer-দের ২০২২ সালে প্রকাশ করা একটা research paper থেকে, তাই এটাকে ভেতরে কীভাবে কাজ করে তার একটা ছবি হিসেবে নাও, API-র প্রতিশ্রুতি হিসেবে না। প্রতিটা partition-এর তিনটা replica, তিনটা AZ-এ একটা করে, আর তার মধ্যে একটা replica ওই partition-এর leader। Write যায় leader-এর কাছে, leader বাকি দুটোতে কপি করে, আর একটা quorum, মানে তিনটার মধ্যে দুটো, নিজের log-এ টেকসইভাবে লিখে ফেললেই write acknowledge হয় (200 OK)। তৃতীয়টা কয়েক millisecond পরে ধরে ফেলে। পুরো গল্পটা এই ছোট্ট ফাঁকটুকু:

  • Eventually consistent read (GetItem, Query আর Scan-এর default): তিনটা replica-র যেকোনোটা উত্তর দিতে পারে। যে replica কয়েক millisecond পিছিয়ে, সে উত্তর দিলে তুমি আগের value পাবে। একটু পরে (সাধারণত এক সেকেন্ডের মধ্যে) আবার পড়লে নতুনটা পাবে। খরচ অর্ধেক: একটা RCU দিয়ে ৪ KB পর্যন্ত item-এর প্রতি সেকেন্ডে দুটো eventually consistent read। Latency সবচেয়ে কম, আর একটা replica না পাওয়া গেলেও কাজ চলে।
  • Strongly consistent read (request-এ ConsistentRead = true): উত্তর দেয় leader, তাই read-এর আগে acknowledge হওয়া প্রতিটা write সে দেখায়। খরচ প্রতি ৪ KB-তে পুরো এক RCU, latency একটু বেশি, আর leader-কে মুহূর্তের জন্য না পাওয়া গেলে (network-এর গোলমাল, replica বদল) read একটা server error (HTTP 500) দিয়ে fail করতে পারে, যেটা SDK আবার চেষ্টা করে; অথচ eventual read তখনো চলে।

Strongly consistent read আছে শুধু table আর local secondary index-এ। Global secondary index, DynamoDB Streams, DAX-এর item cache আর (default-এ) global table-এর অন্য Region সবসময় eventually consistent (সবই lesson 15.5-এ)।

Notes বলে strong read-এ “DynamoDB আগে চেক করে ৩টি নোডেই ডেটা কপি হয়েছে কি না”। ফলটা ঠিক (সবসময় সর্বশেষ acknowledge হওয়া write দেখবে), কিন্তু ভেতরের কৌশলটা leader replica, তিনটাই চেক করা না: এই কারণেই strong read সবচেয়ে ধীর replica-র জন্য অপেক্ষা করে না।

তিনটা কপি, দুই রকম read

প্রতিটা partition তিনবার রাখা হয়, তিনটা Availability Zone-এ। একটা replica হলো leader, write সে-ই নেয়। Hasan তার profile-এর নাম Ali থেকে বদলে Hasan করল; দেখো কোন read কোন নাম দেখে।

পরিস্থিতি

Write নেয় leader; 200 OK মানে সেটা টেকসইভাবে জমা হয়েছে।

AZ a
AZ b
AZ c
Profile screenলেখে
আরেকটা screenপড়ে
Request router
Replica AAli
Replica BleaderAli
Replica CAli
ধাপ 1 / 4
1

বদলানোর আগে তিনটা replica-তেই আছে name = Ali।

#Capacity unit: RCU আর WCU

DynamoDB throughput মাপে unit দিয়ে, আর দুই capacity mode-এই হিসাব একই (provisioned mode এদের বলে capacity unit, on-demand বলে request unit)।

  • ১ RCU (read capacity unit) = ৪ KB পর্যন্ত একটা item-এর প্রতি সেকেন্ডে একটা strongly consistent read, বা দুটো eventually consistent read। Transactional read-এ প্রতি ৪ KB-তে লাগে দুটো RCU।
  • ১ WCU (write capacity unit) = ১ KB পর্যন্ত একটা item-এর প্রতি সেকেন্ডে একটা write। Transactional write-এ প্রতি ১ KB-তে লাগে দুটো WCU।

#রাউন্ডিংয়ের নিয়ম

  1. Item-এর size ওপরের দিকে রাউন্ড করো: read-এ পরের ৪ KB-তে, write-এ পরের ১ KB-তে। ৪.১ KB-র item পড়তে ৮ KB-র সমান খরচ; ২.৫ KB-র item লিখতে ৩ WCU।
  2. Read-এর ধরন দিয়ে গুণ করো: eventual × ০.৫, strong × ১, transactional × ২। Write: সাধারণ × ১, transactional × ২।
  3. প্রতি সেকেন্ডের request সংখ্যা দিয়ে গুণ করো।

প্রশ্নে যে খুঁটিনাটি আসে:

  • GetItem আর BatchGetItem প্রতিটা item আলাদা করে রাউন্ড করে। Query আর Scan পড়া সব item-এর size যোগ করে, তারপর মোটটা একবার রাউন্ড করে; তাই অনেকগুলো ছোট item একটা Query দিয়ে পড়া একটা একটা করে আনার চেয়ে অনেক সস্তা।
  • UpdateItem-এর দাম ধরা হয় update-এর আগের আর পরের item-এর size-এর মধ্যে যেটা বড়, বদলের size না: ৩ KB-র item-এ একটা counter এক বাড়াতেও ৩ WCU।
  • কয়েকটা attribute চাইলেই (projection) read unit কমে না: পুরো item-টাই পড়া হয়।
  • শর্ত না মেলা write-এও write unit খরচ হয়।

#হাতে-কলমে হিসাব

Workloadপ্রতি request-এপ্রতি সেকেন্ডে unit
৬ KB item-এর প্রতি সেকেন্ডে ১০টা strongly consistent read৬ → ৮ KB = ২ block × ১ = ২ RCU২০ RCU
৬ KB item-এর প্রতি সেকেন্ডে ৮০টা eventually consistent read২ block × ০.৫ = ১ RCU৮০ RCU
৩ KB item-এর প্রতি সেকেন্ডে ৫০টা transactional read১ block × ২ = ২ RCU১০০ RCU
২.৫ KB item-এর প্রতি সেকেন্ডে ১২টা write২.৫ → ৩ KB = ৩ WCU৩৬ WCU
১.৫ KB item-এর প্রতি সেকেন্ডে ১০টা transactional write২ KB × ২ = ৪ WCU৪০ WCU
০.৫ KB-র ১২টা item ফেরত দেওয়া একটা Query, eventualমোট ৬ KB → ৮ KB = ২ block × ০.৫পুরো Query-তে ১ RCU

Unit থেকে টাকায় যেতে (US East, Standard table class): provisioned capacity-র দাম প্রতি RCU-ঘণ্টায় ০.০০০১৩ USD আর প্রতি WCU-ঘণ্টায় ০.০০০৬৫ USD; on-demand-এ প্রতি দশ লাখ read request unit-এ ০.১২৫ USD আর প্রতি দশ লাখ write request unit-এ ০.৬২৫ USD। নিচের calculator ঢাকার shop-এর Orders table-এর জন্য দুটোই হিসাব করে দেয়।

RCU আর WCU-র হিসাব

ঢাকার একটা shop-এর Orders table। Item-এর size, traffic আর read ও write-এর ধরন বেছে নাও, তারপর হিসাব করো capacity unit কত লাগবে, আর দুই capacity mode-এ ৩০ দিনের bill কত (US East-এর দাম, Standard table class)।

Item-এর size

দিনে কত ঘণ্টা ব্যস্ত

প্রতি সেকেন্ডে read

Read-এর ধরন

প্রতি সেকেন্ডে write

Write-এর ধরন

ফল দেখতে “হিসাব করো” চাপো।

Default setting-এ (৬ KB item, প্রতি সেকেন্ডে ১০০টা strongly consistent read আর ৫টা write, সারাদিন) table-এর লাগে ২০০ RCU আর ৩০ WCU। ৩০ দিনে সেটা provisioned-এ প্রায় ৩২.৭৬ USD, on-demand-এ ১১৩.৪০ USD: স্থির, পুরোপুরি ব্যবহৃত capacity provisioned-এ মোটামুটি সাড়ে তিন গুণ সস্তা। এবার busy hour ২ করে দাও, ছবিটা উল্টে যায়: on-demand নেমে আসে প্রায় ৯.৪৫ USD-তে, অথচ স্থির provisioned table তখনো ৩২.৭৬ USD দিচ্ছে এমন capacity-র জন্য, যা দিনে ২২ ঘণ্টা বসে থাকে।

#Query আর Scan

অনেক item পড়ার আগে নিজেকে জিজ্ঞেস করো: partition key কি আমি জানি?

Query পড়ে একটা partition key value-র item। তুমি দাও partition key = value (বাধ্যতামূলক, ঠিক একটা value), আর চাইলে একটা sort key-র শর্ত (=, <, <=, >, >=, between, begins_with)। DynamoDB একটা partition-এ যায়, একটা টানা, সাজানো অংশ পড়ে, আর sort key-র ক্রমে ফেরত দেয় (উল্টো ক্রম চাইলে ScanIndexForward = false)। দ্রুত, আর টাকা দিতে হয় শুধু ওই অংশটুকুর।

Scan পড়ে table-এর (বা index-এর) প্রতিটা item, একটার পর একটা partition, আর ফেরত দেয় কোনো কাজের ক্রম ছাড়াই। তুমি যা-ই ফেরত পাও, খরচ পুরো table-এর size-এর।

দুটোই প্রতি call-এ সর্বোচ্চ ১ MB পড়া data ফেরত দেয় (filter-এর আগের হিসাবে)। আরও থাকলে response-এ আসে LastEvaluatedKey, সেটাকে শুরুর key বানিয়ে আবার call করো; CLI আর SDK-র paginator এটা নিজেই করে। Limit প্রতি call-এ কতগুলো item পড়া হবে, তার সীমা বাঁধে।

Filter expression (fare > 500, city = Sylhet) লাগানো হয় item পড়ার পরে। এতে network-এ কম data ফেরত আসে, কিন্তু খরচ হওয়া capacity কমে না: ৩০০টা ফেরত দিতে যে Scan ২০ লাখ item পড়ে, তার bill হয় ২০ লাখ item-এর। Query-তে filter দিলে সস্তা পড়ে শুধু এই কারণে যে Query শুরুতেই কম পড়েছিল।

Parallel scan table-কে segment-এ ভাগ করে (TotalSegments N-এর মধ্যে Segment 0 থেকে N-1) আর N-টা worker-কে একসাথে scan করতে দেয়: একই item, একই খরচ, N গুণ দ্রুত শেষ, আর প্রতি সেকেন্ডে N গুণ capacity টানে। বড় scan চালাও peak-এর বাইরে, প্রতিটা worker-কে Limit দিয়ে বাঁধো, নয়তো table S3-এ export করে Athena দিয়ে query করো (lesson 15.5 আর lesson 17.1), যাতে কোনো read capacity-ই লাগে না।

Query নাকি Scan: একটা তাক, নাকি পুরো গুদাম

Trips table-এ ২০ লাখ trip (প্রায় ২ GB), চারটা partition-এ। দেখো কোন ধরনের read কতটা জায়গা ছোঁয়, আর টাকা দিতে হয় কিসের।

পরিস্থিতি

Partition key দেওয়া আছে: একটা partition, শুধু মেলা item।

Ride app
Request router
P1
P2
P3
P4
Query d-17
ধাপ 1 / 3
1

Driver d-17-এর আজকের trip দেখাও: Query, যেখানে driver_id = d-17 আর trip_time শুরু হয় 2026-10-28 দিয়ে। Query-তে ঠিক একটা partition key value দিতেই হবে; sort key-র শর্ত ঐচ্ছিক।

Animation-এর সংখ্যাগুলো: ০.৫ KB-র ১২টা item একটা Query-তে পড়া মানে ৬ KB, রাউন্ড করে ৮ KB, তাই strongly consistent হলে ২ RCU, eventually consistent হলে ১ RCU। ২ GB-র একটা table-এর eventually consistent Scan হলো ২ GB ÷ ৪ KB = ৫,২৪,২৮৮ block × ০.৫ ≈ ২,৬২,০০০ RCU। ১,০০০ RCU-তে provisioned একটা table-এর জন্য সেটা তার পুরো read capacity-র চার মিনিটেরও বেশি, scan চলাকালীন প্রতিটা live request throttle করানোর জন্য যথেষ্ট।

#Capacity mode: provisioned নাকি on-demand

#Capacity কেন বাড়ে আর কমে

Notes-এ দুটো কারণ: traffic সামলানো আর টাকা বাঁচানো। Flash sale-এর সময় লাখো ক্রেতা একসাথে product পড়ে আর order দেয়; table-এর capacity traffic-এর চেয়ে কম হলে request throttle হয় (ProvisionedThroughputExceededException দিয়ে ফিরিয়ে দেওয়া হয়), তাই capacity বাড়াতে হয়। রাত ২টায় প্রায় কেউই কেনাকাটা করে না; সন্ধ্যার capacity রেখে দেওয়া মানে এমন capacity-র দাম দেওয়া, যা কেউ ব্যবহার করছে না, তাই সেটা কমা উচিত। এটা সামলানোর জন্য DynamoDB দুটো পথ দেয়, এদেরই বলে capacity mode।

#Provisioned capacity (auto scaling সহ)

Table (আর প্রতিটা GSI) প্রতি সেকেন্ডে কত RCU আর WCU ব্যবহার করতে পারবে, সেটা তুমি ঠিক করো, আর তার দাম দাও প্রতি ঘণ্টায়, ব্যবহার হোক বা না হোক। এর ওপরের request throttle হয়।

Traffic-এর সাথে বারবার হাতে সংখ্যা বদলানো ঝামেলা, তাই যোগ করো auto scaling (target tracking policy সহ Application Auto Scaling): একটা minimum (ধরো ১০০), একটা maximum (ধরো ৪,০০০) আর একটা target utilization (ধরো ৭০%, ২০ থেকে ৯০%-এর মধ্যে রাখা যায়)।

  • ব্যবহৃত capacity পরপর দুই মিনিট target-এর ওপরে থাকলে (DynamoDB এক মিনিট করে data point পাঠায়, আর alarm বাজতে আরও কয়েক মিনিট পর্যন্ত দেরি হতে পারে) একটা CloudWatch alarm বাজে, আর Application Auto Scaling UpdateTable call করে capacity বাড়ায়, যাতে utilization আবার target-এ ফেরে: ৭০% target-এ ৮৪০ ব্যবহৃত RCU হয়ে যায় ৮৪০ ÷ ০.৭ = ১,২০০ RCU। বদলাতে কয়েক মিনিট লাগে, downtime ছাড়াই।
  • Utilization পরপর ১৫টা data point (মোটামুটি ১৫ মিনিট) target-এর নিচে থাকলে কমায়, তবে minimum-এর নিচে কখনো না। কমানোর সংখ্যা বাঁধা: প্রতিটা UTC দিনের শুরুতে ৪টা, তারপর প্রতি ঘণ্টায় আরও একটা, দিনে সর্বোচ্চ ২৭টা। বাড়ানোর কোনো সীমা নেই।
  • Burst capacity: অব্যবহৃত capacity-র সর্বোচ্চ ৩০০ সেকেন্ড DynamoDB জমিয়ে রাখে, আর ছোট spike-এ সেটা খরচ করে। এটা কয়েক সেকেন্ড সামলায়, কয়েক মিনিট না।
  • তাই auto scaling চলে আন্দাজযোগ্য বা ধীরে বদলানো traffic-এর পেছনে পেছনে; খাড়া, হঠাৎ spike তাকে পেছনে ফেলে দেয়, আর মাঝের ফাঁকে throttle হয়।

স্থির আর ভালোভাবে ব্যবহৃত traffic-এ provisioned mode-ই সবচেয়ে সস্তা throughput, আর reserved capacity (১ বা ৩ বছরের commitment, Standard table class) আরও কমায়। চিরস্থায়ী free tier-ও (২৫ RCU, ২৫ WCU) শুধু provisioned mode-এ খাটে।

#On-demand capacity

RCU বা WCU ঠিক করার কিছু নেই, scale করারও কিছু নেই: আসলে যতগুলো read request unit আর write request unit খরচ হলো (হিসাব RCU আর WCU-র মতোই), তার দাম দাও, আর traffic না থাকলে throughput-এর কোনো দাম নেই। ২০২৪-এর নভেম্বর-এ AWS on-demand-এর দাম ৫০% কমিয়েছে, আর তখন থেকে AWS on-demand-কে বলে বেশিরভাগ workload-এর default আর সুপারিশ করা mode; console-এও এটাই আগে থেকে বাছা থাকে।

“Capacity planning লাগে না” মানে “কোনো সীমা নেই” না। On-demand আসলে যা কথা দেয়:

  • নতুন on-demand table প্রথম থেকেই প্রতি সেকেন্ডে ৪,০০০ write আর ১২,০০০ read পর্যন্ত চালাতে পারে।
  • সে সাথে সাথে আগের peak-এর দ্বিগুণ পর্যন্ত সামলায়। তার ওপরের traffic-ও DynamoDB capacity যোগ করতে করতে সামলে নেয়, কিন্তু ৩০ মিনিটের ভেতরে আগের peak-এর দ্বিগুণের বেশি বাড়লে throttle হতে পারে।
  • জানা কোনো event-এর জন্য (ঈদের launch, ticket sale) table pre-warm করো: ২০২৪-এর নভেম্বর থেকে UpdateTable দিয়ে তার warm throughput প্রত্যাশিত মাত্রায় তুলে রাখা যায়, নয়তো traffic অন্তত ৩০ মিনিট ধরে বাড়াও।
  • Default table quota প্রতি সেকেন্ডে ৪০,০০০ read আর ৪০,০০০ write request unit (বাড়ানো যায়)। খরচ সামলাতে প্রতিটা on-demand table-এ একটা maximum throughput-ও বেঁধে দিতে পারো।
  • প্রতি partition আর প্রতি item-এর ছাদ (৩,০০০ / ১,০০০) তখনো খাটে: on-demand hot key সারায় না।

#এক mode থেকে আরেক mode-এ যাওয়া

Notes বলে mode বদলানো যায় প্রতি ২৪ ঘণ্টায় একবার। নিয়মটা এখন ঢিলে হয়েছে: আজ একটা table provisioned থেকে on-demand-এ যেকোনো ২৪ ঘণ্টায় চারবার পর্যন্ত নেওয়া যায়, আর on-demand থেকে provisioned-এ যখন খুশি। পুরোনো প্রশ্নে এখনো “২৪ ঘণ্টায় একবার” থাকতে পারে; কোনো বিকল্প যদি প্রতিদিন একবার এদিক-ওদিক করার ওপর দাঁড়িয়ে থাকে, দুই নিয়মেই সেটা চলে।

Provisioned (+ auto scaling)On-demand

  • RCU / WCU তুমি ঠিক করো (min, max, target ৭০%); যা ঠিক করা আছে তার ঘণ্টা ধরে দাম।
  • স্থির, আন্দাজযোগ্য, বেশি ব্যবহৃত traffic-এ সবচেয়ে সস্তা; reserved capacity; free tier।
  • Auto scaling সাড়া দেয় কয়েক মিনিটে; burst capacity কয়েক সেকেন্ড সামলায়; হঠাৎ spike-এ throttle।
  • ঠিক করা সংখ্যার ওপরে throttle (ProvisionedThroughputExceededException)।
  • ঠিক করার কিছু নেই; দাম প্রতি request-এ; traffic শূন্য হলে দাম শুধু storage-এর।
  • আন্দাজের বাইরে, হঠাৎ লাফানো বা অজানা traffic, নতুন app, dev আর test, দিনের বেশিরভাগ সময় বসে থাকা table-এর জন্য সেরা। ২০২৪ থেকে default আর সুপারিশ করা।
  • আগের peak-এর ২ গুণ পর্যন্ত সাথে সাথে; ৩০ মিনিটের ভেতরে তার বেশি বাড়লে throttle হতে পারে; জানা event-এর আগে pre-warm।
  • প্রতি unit-এ পুরোপুরি ব্যবহৃত provisioned capacity-র প্রায় সাড়ে তিন গুণ দাম, তাই provisioned capacity মোটামুটি ৩০%-এর কম ব্যবহার হলে on-demand-ই সস্তা।

Auto scaling সহ provisioned, নাকি on-demand

একটা online shop-এর Products table: একটা সাধারণ দিন, হঠাৎ flash sale, নিরিবিলি রাত আর ঈদের launch। Stage-এর নিচের table-এ দেখো প্রতি সেকেন্ডে কত read চাওয়া হলো, আর ওই মুহূর্তে table-এর capacity কত ছিল।

পরিস্থিতি

Target tracking ব্যবহৃত capacity-কে ৭০%-এর কাছে ধরে রাখে।

ক্রেতা
Products table500 RCU
CloudWatch alarm
Application Auto Scaling
৩০০ read/s

Products table

সময়TrafficCapacityফল
09:00300500৬০% ব্যবহৃত, ঠিক আছে
ধাপ 1 / 4
1

Provisioned mode: RCU আর WCU তুমি ঠিক করো, আর প্রতি ঘণ্টায় তার দাম দাও, ব্যবহার হোক বা না হোক। Auto scaling চালু: সর্বনিম্ন ১০০, সর্বোচ্চ ৪,০০০ RCU, target utilization ৭০%। সকাল ৯টায় table-এর আছে ৫০০ RCU, ক্রেতারা ব্যবহার করছে ৩০০।

#Throttling আর hot partition

#Throttling দেখতে কেমন

কোনো request-এর যতটা capacity দরকার ততটা না থাকলে DynamoDB তাকে HTTP 400 আর ProvisionedThroughputExceededException দিয়ে ফিরিয়ে দেয় (নামটা পুরোনো আমলের: on-demand table-ও কোনো partition বা table-এর maximum ছাড়ালে এটাই দেয়)। AWS SDK throttle হওয়া request নিজে থেকেই exponential backoff আর jitter দিয়ে আবার চেষ্টা করে (প্রায় ৫০ ms অপেক্ষা, তারপর ১০০, তারপর ২০০, প্রতিবার তার একটা random অংশ, যাতে হাজারটা client একই তালে retry না করে)। ছোট burst এভাবেই হজম হয়ে যায়; কিন্তু throttling থামেই না যদি, তাহলে retry শুধু চাপ বাড়ায়, আসল সমাধান capacity বা key-এর নকশা। CloudWatch-এ এটা দেখা যায় ReadThrottleEvents / WriteThrottleEvents হিসেবে, আর DynamoDB-র জন্য CloudWatch Contributor Insights দেখায় কোন key সবচেয়ে বেশি পড়া হচ্ছে আর সবচেয়ে বেশি throttle হচ্ছে।

কারণ দুই রকম, আর সমাধানও আলাদা:

কারণলক্ষণকীসে সারে
Table-এ capacity কম পড়েছে: মোট traffic provisioned RCU/WCU ছাড়িয়েছেসব key throttle হয়; ব্যবহৃত capacity ১০০%Auto scaling, বেশি maximum, on-demand, pre-warming
Hot partition / hot key: অল্প কয়েকটা key বেশিরভাগ traffic পাচ্ছেThrottle হচ্ছে, অথচ table-এর মোট capacity-র বেশিরভাগ অব্যবহৃতKey-এর নকশা, hot read-এর জন্য cache (DAX), hot write-এর জন্য write sharding বা queue

#Burst আর adaptive capacity

Adaptive capacity সবসময় চালু, বিনা খরচে। Access অসমান হলে সে সাথে সাথে ব্যস্ত partition-কে table-এর বাকি অংশের অব্যবহৃত capacity ব্যবহার করতে দেয়, আর কয়েকটা item টানা hot থাকলে সেগুলোকে আলাদা partition-এ সরিয়ে দেয়, যাতে প্রতিবেশীর সাথে সীমা ভাগ করতে না হয়। যেটা সে পারে না, তা হলো ছাদ তোলা: একটা partition, আর তাই একটা item, প্রতি সেকেন্ডে সর্বোচ্চ ৩,০০০ RCU আর ১,০০০ WCU-ই পায়। Burst capacity-র সাথে মিলে এই কারণেই মাঝারি অসমতা খুব কমই ক্ষতি করে, আর চরম অসমতা তবুও করে।

#ঈদের sale-এর প্রশ্ন

Notes-এর সেই চেনা পরীক্ষার প্রশ্ন, ঢাকার প্রেক্ষাপটে: একটা e-commerce site DynamoDB ব্যবহার করে। গত বছর ঈদের sale-এ (মূল প্রশ্নে Christmas) কয়েকটা খুব জনপ্রিয় item বারবার পড়া হচ্ছিল, আর table ProvisionedThroughputExceededException দিচ্ছিল। এ বছর এটা কীভাবে ঠেকাবে?

আসল কারণ। সবাই একই কয়েকটা product item পড়ছিল। প্রতিটা item থাকে একটা partition-এ, তাই সেই partition-গুলোতে যতটা read আসছিল, তারা তার চেয়ে অনেক কম দিতে পারে (hot partition, বা hot key), অথচ পুরো table-এ capacity বাড়তি পড়ে ছিল। সংখ্যায়: ২ KB-র একটা product item-এর প্রতি সেকেন্ডে ৮,০০০টা eventually consistent view মানে এক key-তে ৪,০০০ RCU; ছাদ ৩,০০০, তাই প্রায় এক-চতুর্থাংশ read fail করে।

উত্তরগুলো, পরীক্ষা যে ক্রমে চায়:

  1. DynamoDB Accelerator (DAX)। DynamoDB-র জন্যই বানানো in-memory, write-through cache, একই API বোঝে: app শুধু client আর endpoint বদলায়, আর কিছু না। Item-এর প্রথম read একটা miss, DAX সেটা table থেকে আনে; পরের হাজারটা hit, memory থেকে microsecond-এ উত্তর। Hot key আর table পর্যন্ত পৌঁছায় না, throttling থামে, RCU-র bill কমে। “Read often”, “popular item”, “read-এ hot partition”, “microsecond latency”, “minimal code changes” → DAX। (ElastiCache-ও cache করত, কিন্তু প্রতিটা cache read আর invalidation-এর জন্য application-এ code লিখতে হয়। DAX বিস্তারিত: lesson 15.5।)
  2. On-demand mode, বা বেশি maximum সহ auto scaling (notes-এর দ্বিতীয় আর তৃতীয় উত্তর)। এগুলো সারায় পুরো table-এর capacity-র ঘাটতি, আর কোনো প্রশ্ন যদি শুধু এগুলোই বিকল্প হিসেবে দেয়, তাহলে এদের একটাই উত্তর। কিন্তু প্রতি item-এর ৩,০০০ RCU-র ছাদ এরা তোলে না, তাই সত্যিকারের hot item-এর জন্য শুধু এগুলো যথেষ্ট না। Notes-এর “on-demand-এ কখনো throttle হয় না” কথাটা বাড়াবাড়ি।
  3. নিজের তৈরি অসমতার জন্য (status বা date key) key-এর নকশা ঠিক করা: high cardinality-র partition key।
  4. Client-এ exponential backoff: ক্ষণিকের spike-এর জন্য ঠিক, রোগ সারানোর ওষুধ না।

Hot traffic যদি write হয়, DAX কাজে আসে না: সে write সোজা table-এ পাঠিয়ে দেয়, তাই প্রতিটা vote বা counter-এর update তবুও একই key-তে পড়ে। একটা reality show-তে প্রতি সেকেন্ডে ২,৫০০ দর্শক প্রতিযোগী ৭-কে vote দিলে, ব্যবহার করো write sharding (key-এর শেষে ১ থেকে ১০-এর একটা random suffix: votes#7#1 … votes#7#10, তাহলে প্রতিটা shard-এ প্রতি সেকেন্ডে প্রায় ২৫০ write, আর পড়ার সময় দশটা shard যোগ করো), নয়তো vote-গুলো একটা SQS queue-তে রাখো (lesson 13.1) আর একটা consumer সমান হারে লিখুক, বা memory-তে জমিয়ে শুধু মোট সংখ্যা লেখো।

ঈদের sale-এর hot partition, আর কীসে সারে

গত ঈদে shop-এর table ProvisionedThroughputExceededException দিয়েছিল, অথচ মোট capacity ছিল যথেষ্ট। কয়েকটা খুব জনপ্রিয় product বারবার পড়া হচ্ছিল। কেন হলো দেখো, আর সমাধানগুলো তুলনা করো।

পরিস্থিতি

একটা item এত read পায়, যা একটা partition দিতে পারে না।

ক্রেতা
Product API
P1
P2: p-501
P3
৮,০০০ view/s
ধাপ 1 / 3
1

Table-এ provisioned আছে ১২,০০০ RCU। মাঝরাতে একটা ফোনের, মানে item p-501-এর, ঈদ অফার চালু হলো, আর প্রতি সেকেন্ডে ৮,০০০ ক্রেতা তার ২ KB-র page খুলছে।

#পাশের কথা: transaction, SQL syntax আর table class

#Transaction

একই account আর Region-এর item আর table জুড়ে DynamoDB ACID transaction দেয়। TransactWriteItems সর্বোচ্চ ১০০টা put, update, delete আর condition-check (৪ MB পর্যন্ত) এমনভাবে বাঁধে যে সব একসাথে সফল হয়, নয়তো একসাথে fail; TransactGetItems ১০০টা পর্যন্ত item একটা consistent snapshot হিসেবে পড়ে। উদাহরণ: একটা order, যেটা Products-এ stock কমায়, Orders-এ order ঢোকায় আর Wallets-এ টাকা কাটে, সব-অথবা-কিছুই-না। খরচ দ্বিগুণ (প্রতি ১ KB লেখায় ২ WCU, প্রতি ৪ KB পড়ায় ২ RCU), কারণ প্রতিটা item আগে prepare হয়, তারপর commit; বাতিল হওয়া transaction-এরও দাম কাটে। টাকা বা stock যেখানে জড়িত, সেখানে ব্যবহার করো, প্রতিটা write-এ না।

#PartiQL দিয়ে SQL-এর মতো লেখা

DynamoDB একটা SQL-compatible query language-ও বোঝে, PartiQL (ExecuteStatement, BatchExecuteStatement, ExecuteTransaction, আর console-এ একটা query editor):

SELECT * FROM "Trips" WHERE driver_id = 'd-17' AND trip_time BETWEEN '2026-10-28T08:00' AND '2026-10-28T12:00'

এটা একই operation-এর শুধু আলাদা লেখার ঢং: partition key-এর ওপর WHERE দিলে সেটা Query হয়ে যায়, আর সেটা ছাড়া SELECT হয়ে যায় একটা পুরো Scan, একই খরচে। Join তখনো নেই।

#Table class: Standard আর Standard-IA

প্রতিটা table-এর একটা table class থাকে। বেশিরভাগ table-এর জন্য Standard (default)-ই ঠিক। Standard-Infrequent Access (Standard-IA) data রাখে ৬০% কম দামে (US East-এ প্রতি GB-মাসে ০.২৫-এর বদলে ০.১০ USD), কিন্তু প্রতিটা read আর write-এ প্রায় ২৫% বেশি নেয়। লাভ হয় যখন storage-ই খরচের বড় অংশ, মোটামুটি যখন storage-এর খরচ throughput-এর খরচের অর্ধেক ছাড়ায়: order-এর ইতিহাস, পুরোনো post, log, পুরোনো খেলার অর্জন। Performance, durability আর feature একই, API বদলায় না, আর যেকোনো ৩০ দিনে একটা table-এর class দুবার বদলানো যায়। Standard-IA-তে free tier বা reserved capacity খাটে না।

#কোথায় গোলমাল হয়

লক্ষণসম্ভাব্য কারণসমাধান
প্রতিটা key-তে ProvisionedThroughputExceededException, ব্যবহৃত capacity ১০০%Table-এ capacity কমবেশি maximum সহ auto scaling, on-demand, event-এর আগে pre-warm
Throttling চলছে, অথচ table-এর capacity-র বেশিরভাগ অব্যবহৃতHot partition বা hot itemবেশি cardinality-র key; hot read-এর জন্য DAX; hot write-এর জন্য write sharding বা SQS
প্রতিটা flash sale-এর শুরুতে কয়েক মিনিট throttlingখাড়া spike-এর পেছনে auto scaling পিছিয়ে; burst capacity শেষSale-এর আগে minimum বাড়াও, scheduled scaling, on-demand + pre-warm
Launch-এর সময় on-demand table throttle করে৩০ মিনিটের ভেতরে traffic আগের peak-এর দ্বিগুণ ছাড়িয়েছে, বা ৪০,০০০ unit-এর table quota ছুঁয়েছেPre-warm (warm throughput), ধীরে বাড়াও, quota বাড়ানোর অনুরোধ
Save করার ঠিক পরেই app পুরোনো value দেখায়Write-এর পরে eventually consistent readযেখানে জরুরি সেখানে ConsistentRead = true, বা নিজের write-এর response থেকেই value নাও
Index-এ ConsistentRead প্রত্যাখ্যানGSI-তে strong read চাওয়া হয়েছেGSI শুধু eventual; table পড়ো বা LSI ব্যবহার করো
PutItem-এ ValidationExceptionItem ৪০০ KB-র বড় (নামও গোনা হয়)বড় জিনিস S3-এ রাখো, item-এ তার key; attribute-এর নাম ছোট করো
দ্বিতীয় PutItem চুপচাপ data বদলে দিলএকই primary key, কোনো শর্ত নেইattribute_not_exists শর্ত; version attribute দিয়ে optimistic locking
একটা report job চালালেই live app ধীর হয়ে যায়Scan বা parallel scan table-এর capacity খেয়ে ফেলছেPeak-এর বাইরে Limit দিয়ে চালাও; S3-এ export করে Athena; ওই access pattern-এর জন্য GSI
Bill আশার চেয়ে অনেক বেশিFilter সহ Scan, বড় item, যেখানে eventual চলত সেখানে strong read, অলস provisioned capacityScan-এর বদলে Query, ছোট item, eventual read, on-demand বা scale-down
Batch call “সফল”, কিন্তু কিছু item নেইThrottling-এর মধ্যে BatchWriteItem unprocessed item ফেরত দিয়েছেUnprocessedItems backoff দিয়ে আবার পাঠাও (SDK-র helper এটা করে)

#System design-এ DynamoDB

  • Default serverless database। API Gateway → Lambda → DynamoDB (lesson 15.6)-তে কোথাও কোনো server নেই, আর শূন্য থেকে scale করে; table-এ ঢোকার অনুমতি দেয় Lambda-র execution role (lesson 15.1)। RDS-এর মতো connection pool ফুরিয়ে যাওয়ার ভয় নেই (lesson 15.3)।
  • Stateless EC2 বা container বহরের জন্য session state আর cart (chapter 9-এর stateless-app pattern): key session_id, মেয়াদ শেষে মুছতে TTL (lesson 15.5)।
  • Access pattern থেকে key নকশা করো। আগে প্রশ্নগুলোর তালিকা বানাও (“user আনো”, “একজন driver-এর trip সময় ধরে”, “status ধরে order”), তারপর এমন partition key, sort key আর index বাছো, যাতে প্রতিটার উত্তর একটা Query-তে আসে। তালিকাটাই লিখতে না পারলে হয়তো relational database বেশি মানাবে।
  • চাপ ছড়াও। High cardinality-র key; hot read-এর জন্য cache; hot write-এর জন্য sharding বা queue; অজানা traffic-এ on-demand।
  • Consistency বাছো প্রতিটা read ধরে, পুরো table ধরে না: balance আর stock-এ strong, feed আর counter-এ eventual।
  • বড় জিনিস S3-এ, DynamoDB-তে শুধু তার ঠিকানা; analytics চালাও S3 export-এর ওপর, Scan দিয়ে না।
  • Multi-Region, stream, backup আর index পরের lesson-এ (lesson 15.5); এগুলো দিয়ে বানানো পুরো একটা serverless app আছে lesson 15.8-এ।

#নিজে করে দেখো

একটা on-demand Trips table বানাও, item লেখো আর পড়ো, আর খরচ হওয়া capacity দেখো। Output ছোট করা; নাম আর সংখ্যা উদাহরণ।

  1. ১. Composite key সহ একটা table বানাও

    aws dynamodb create-table --table-name Trips-example \
      --attribute-definitions AttributeName=driver_id,AttributeType=S AttributeName=trip_time,AttributeType=S \
      --key-schema AttributeName=driver_id,KeyType=HASH AttributeName=trip_time,KeyType=RANGE \
      --billing-mode PAY_PER_REQUEST --region ap-southeast-1
    
    "TableStatus": "CREATING",
    "TableArn": "arn:aws:dynamodb:ap-southeast-1:111122223333:table/Trips-example",
    "BillingModeSummary": { "BillingMode": "PAY_PER_REQUEST" }
    

    Table ACTIVE হলে aws dynamodb wait table-exists --table-name Trips-example ফিরে আসে।

  2. ২. Duplicate আটকে একটা item লেখো

    aws dynamodb put-item --table-name Trips-example \
      --item '{"driver_id":{"S":"d-17"},"trip_time":{"S":"2026-10-28T08:05"},"from":{"S":"Mirpur"},"to":{"S":"Banani"},"fare":{"N":"320"}}' \
      --condition-expression "attribute_not_exists(trip_time)" \
      --return-consumed-capacity TOTAL
    
    "ConsumedCapacity": { "TableName": "Trips-example", "CapacityUnits": 1.0 }
    

    আবার চালাও, trip-টা overwrite না করে ConditionalCheckFailedException দিয়ে fail করবে। একইভাবে d-17-এর আরও দুটো trip যোগ করো, 2026-10-28T10:40-এ (বনানী থেকে গুলশান) আর 2026-10-28T13:15-এ (গুলশান থেকে মতিঝিল)।

  3. ৩. পড়ো, eventually আর strongly consistent দুইভাবে

    aws dynamodb get-item --table-name Trips-example \
      --key '{"driver_id":{"S":"d-17"},"trip_time":{"S":"2026-10-28T08:05"}}' \
      --return-consumed-capacity TOTAL
    # "CapacityUnits": 0.5
    
    aws dynamodb get-item --table-name Trips-example \
      --key '{"driver_id":{"S":"d-17"},"trip_time":{"S":"2026-10-28T08:05"}}' \
      --consistent-read --return-consumed-capacity TOTAL
    # "CapacityUnits": 1.0
    

    একই item, default read-এ অর্ধেক দাম।

  4. ৪. একটা range Query করো, তারপর Scan-এর সাথে মেলাও

    aws dynamodb query --table-name Trips-example \
      --key-condition-expression "driver_id = :d AND trip_time BETWEEN :a AND :b" \
      --expression-attribute-values '{":d":{"S":"d-17"},":a":{"S":"2026-10-28T08:00"},":b":{"S":"2026-10-28T12:00"}}' \
      --return-consumed-capacity TOTAL
    
    "Count": 2, "ScannedCount": 2,
    "ConsumedCapacity": { "CapacityUnits": 0.5 }
    
    aws dynamodb scan --table-name Trips-example \
      --filter-expression "#t = :c" --expression-attribute-names '{"#t":"to"}' \
      --expression-attribute-values '{":c":{"S":"Sylhet"}}' --return-consumed-capacity TOTAL
    
    "Count": 0, "ScannedCount": 3,
    "ConsumedCapacity": { "CapacityUnits": 0.5 }
    

    তুমি টাকা দিয়েছ ScannedCount-এর; Count হলো filter যতগুলো পার হতে দিয়েছে। লাখ লাখ item-এর table-এ এই ফারাকটাই bill।

  5. ৫. Auto scaling সহ provisioned-এ যাও

    aws dynamodb update-table --table-name Trips-example --billing-mode PROVISIONED \
      --provisioned-throughput ReadCapacityUnits=100,WriteCapacityUnits=20
    
    aws application-autoscaling register-scalable-target --service-namespace dynamodb \
      --resource-id table/Trips-example --scalable-dimension dynamodb:table:ReadCapacityUnits \
      --min-capacity 100 --max-capacity 4000
    
    aws application-autoscaling put-scaling-policy --service-namespace dynamodb \
      --resource-id table/Trips-example --scalable-dimension dynamodb:table:ReadCapacityUnits \
      --policy-name trips-read-70 --policy-type TargetTrackingScaling \
      --target-tracking-scaling-policy-configuration \
      '{"TargetValue":70.0,"PredefinedMetricSpecification":{"PredefinedMetricType":"DynamoDBReadCapacityUtilization"}}'
    

    Policy নিজেই সেই দুটো CloudWatch alarm (scale out, scale in) বানায়, যা animation-এ দেখেছ।

  6. ৬. Event-এর জন্য pre-warm করো, তারপর গুছিয়ে ফেলো

    aws dynamodb update-table --table-name Trips-example \
      --warm-throughput ReadUnitsPerSecond=40000,WriteUnitsPerSecond=10000
    
    aws dynamodb describe-table --table-name Trips-example --query "Table.WarmThroughput"
    # { "ReadUnitsPerSecond": 40000, "WriteUnitsPerSecond": 10000, "Status": "UPDATING" }
    
    aws dynamodb delete-table --table-name Trips-example
    

    Pre-warming-এর bill হয় বাড়ানো অংশের জন্য একবার; কাজ শেষে table মুছে দাও।

#সারসংক্ষেপ

মূল কথাগুলো

  • DynamoDB = serverless NoSQL key-value আর document database: fully managed, ৩টা AZ-এ ৩টা কপি, প্রতি সেকেন্ডে লাখ লাখ request, এক অঙ্কের ms, access-এর জন্য IAM, join নেই। বড় জিনিস যায় S3-এ; একটা item সর্বোচ্চ ৪০০ KB (নাম + value)।
  • Table → item → attribute, key ছাড়া schema নেই। Primary key = partition key (অনন্য, hash হয়ে একটা partition-এ যায়) বা partition key + sort key (sort key অনুযায়ী সাজানো item collection: one-to-many আর range query)। Partition key বাছো high cardinality-র।
  • একটা partition, আর একটা item, সর্বোচ্চ ৩,০০০ RCU আর ১,০০০ WCU দেয়; table-এর মোট capacity একটা hot key-কে বাঁচাতে পারে না।
  • Eventually consistent read (default) কয়েক millisecond আগের write মিস করতে পারে, খরচ অর্ধেক; strongly consistent (ConsistentRead = true) পড়ে leader থেকে, পুরো দাম, বিভ্রাটের সময় ক্ষণিকের জন্য fail করতে পারে, আর GSI-তে পাওয়া যায় না।
  • ১ RCU = ৪ KB পর্যন্ত item-এর প্রতি সেকেন্ডে ১টা strong বা ২টা eventual read; ১ WCU = ১ KB পর্যন্ত item-এর প্রতি সেকেন্ডে ১টা write; size ওপরের দিকে রাউন্ড; transaction-এ দ্বিগুণ; Query আর Scan মোটটা রাউন্ড করে, Get প্রতিটা item।
  • Query = একটা partition key value, সাজানো অংশ, সস্তা। Scan = পুরো table, ১ MB করে page, দামি; filter capacity কমায় না; parallel scan দ্রুত, সস্তা না। অন্য attribute দিয়ে খোঁজা → GSI (15.5)।
  • স্থির traffic-এ provisioned + auto scaling (target ~৭০%, সাড়া দেয় কয়েক মিনিটে, দিনে ২৭বার কমানো, ৩০০ সেকেন্ডের burst); হঠাৎ লাফানো বা অজানা traffic-এ on-demand (প্রতি request-এ দাম, সাথে সাথে আগের peak-এর ২ গুণ, pre-warm, ৪০,০০০ unit-এর table quota, ২০২৪ থেকে default আর সুপারিশ করা)। On-demand-এ যাওয়া যায় ২৪ ঘণ্টায় ৪বার পর্যন্ত, ফেরা যখন খুশি।
  • ProvisionedThroughputExceededException: table-এ ঘাটতি → auto scaling / on-demand; hot read key → DAX; hot write key → write sharding বা SQS; সবসময় exponential backoff। Adaptive capacity প্রতি partition-এর ছাদ পর্যন্ত অসমতা সামলায়।
  • পাশের কথা: transaction (১০০টা item, সব-অথবা-কিছুই-না, দ্বিগুণ খরচ), PartiQL (SQL-এর মতো লেখা, ভেতরে সেই একই Query/Scan), Standard-IA table class (storage ৬০% সস্তা, request দামি)।