Course-এর সূচি

15.5

DynamoDB Index, DAX, Stream আর Global Table

LSI বনাম GSI, query বনাম scan, DAX, Streams, Global Table, TTL, backup আর S3-এ export/import।

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

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

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

  • Scan না করেই DynamoDB table-কে নতুন প্রশ্নের উত্তর দেওয়াতে: table-এ Query, local secondary index (LSI) আর global secondary index (GSI)-এর মধ্যে বেছে নিতে, আর প্রতিটার দাম জানতে (কখন বানানো যায়, key, consistency, capacity, ১০ GB item collection-এর সীমা, এক table-এ কয়টা)।
  • কম capacity-ওয়ালা GSI কেন মূল table-এর write আটকে দেয়, সেটা বুঝিয়ে বলতে, আর এমন projection বাছতে যাতে index ছোট থাকে।
  • প্রচুর read হওয়া table-এর সামনে DAX বসাতে, cache hit, miss আর পুরোনো ফল কখন আসবে আগেই বলে দিতে (item cache, query cache, write-through, strongly consistent read সোজা table-এ যাওয়া, ৫ মিনিটের default TTL), আর DAX-কে ElastiCache থেকে আলাদা করে চিনতে।
  • Event-driven কাজে Lambda-র সাথে DynamoDB Streams ব্যবহার করতে (welcome email, derived table, audit), বারবার fail করা record সামলাতে, আর কখন Kinesis Data Streams for DynamoDB ভালো change log, সেটা জানতে (১ বছর পর্যন্ত রাখা, বেশি consumer)।
  • Global Tables দিয়ে design করতে: active-active replication, last writer wins, Region বিকল হলে failover, আর ২০২৫-এর multi-Region strong consistency mode।
  • TTL দিয়ে বিনা খরচে data মেয়াদ শেষে মুছতে, PITR (১ থেকে ৩৫ দিন) আর on-demand backup বা AWS Backup দিয়ে ভুল ফেরাতে, আর table-এর capacity খরচ না করেই S3-এ export আর S3 থেকে import দিয়ে data বিশ্লেষণ বা লোড করতে।
  • বাকি integration-গুলো কোথায় বসে বলতে: Redshift আর OpenSearch-এ zero-ETL, VPC gateway endpoint, আর dynamodb:LeadingKeys দিয়ে fine-grained access।

#এই লেসন কোথা থেকে শুরু

Lesson 15.4-এ ভিতটা তৈরি হয়েছে: table, ৪০০ KB পর্যন্ত item, partition key আর চাইলে একটা sort key মিলিয়ে primary key, read আর write capacity unit, eventually আর strongly consistent read, আর Query (একটা partition key value পড়ে, সস্তা) বনাম Scan (পুরো table পড়ে, ধীর আর দামি)-এর পার্থক্য। সেখানকার পরীক্ষার নিয়মটা ছিল সোজাসাপ্টা: “cost-effective and performant way to search” চাইলে Scan কখনো বেছো না।

এই নিয়ম থেকেই প্রথম প্রশ্নটা আসে, যেটার উত্তর এই লেসন শুরুতেই দেবে: app যদি এমন কিছু দিয়ে খুঁজতে চায় যেটা primary key-ই না? Index-এর পরে আসবে সেই feature-গুলো, যেগুলো একটা table-কে বড় system-এর ইট বানিয়ে দেয়: একটা cache (DAX), একটা change log (Streams), অন্য Region-এ কপি (Global Tables), নিজে নিজে মেয়াদ শেষ (TTL), backup, আর S3-এ যাওয়ার দরজা।

#Secondary index: একই table অন্যভাবে পড়া

DynamoDB শুধু key দিয়েই Query করতে পারে। Secondary index হলো table-এর data-র (পুরো বা কিছু অংশের) আরেকটা কপি, অন্য একটা key দিয়ে সাজানো, যেটা DynamoDB নিজেই হালনাগাদ রাখে। Index-এ Query করো ঠিক table-এর মতোই, শুধু --index-name বলে দিতে হয়।

ধরো একটা e-commerce shop-এর Orders table:

  • Partition key user_id, sort key order_date।
  • “User_101-এর order, নতুনগুলো আগে” table-এ একটা Query-তেই হয়ে যায় (ScanIndexForward=false দিলে sort key উল্টো দিক থেকে পড়ে)।

এবার customer care আরও দুটো জিনিস চায়: user_101-এর সবচেয়ে দামি order আগে, আর ফোনে কেউ শুধু tracking number বললে সেই order খুঁজে বের করা। Table সাজানো তারিখ দিয়ে, দাম দিয়ে না; আর key হলো user, tracking number না। দুটো প্রশ্নের জন্য দুই রকম index লাগবে।

#Local secondary index (LSI)

LSI table-এর partition key একই রাখে, আর একটা আলাদা sort key যোগ করে। দামের প্রশ্নের জন্য: partition key user_id (একই), sort key price (নতুন)। LSI-তে user_id = user_101 দিয়ে উল্টো ক্রমে Query করলে ওই customer-এর order আসে, সবচেয়ে দামিটা আগে।

Partition key একই বলে LSI-এর entry-গুলো থাকে যে item-এর কথা বলছে তাদের একই partition-এ, একদম পাশাপাশি। এজন্যই নাম local, আর পরের সব নিয়ম এখান থেকেই আসে:

  • এটা define করতে হয় table বানানোর সময়েই। পরে যোগ করা যায় না, আর table delete না করে একে delete-ও করা যায় না।
  • এটা খরচ করে table-এর নিজের read আর write capacity; এর আলাদা কোনো capacity নেই।
  • মূল item যে operation-এ বদলায়, সেই একই operation-এ এটাও update হয়, তাই eventually consistent-এর পাশাপাশি strongly consistent read-ও দেয়।
  • এক table-এ সর্বোচ্চ ৫টা LSI।
  • Table-এ LSI থাকলে একই partition key value-এর সব item, table আর তার LSI মিলিয়ে, একটা item collection তৈরি করে, আর একটা item collection ১০ GB-র বেশি বড় হতে পারে না। Notes বলছে “User_101-এর data ১০ GB-র বেশি হতে পারবে না”: ঠিক এটাই, আর এটা খাটে শুধু LSI-ওয়ালা table-এ।

#Global secondary index (GSI)

GSI যেকোনো attribute-কে partition key বানাতে পারে, সাথে চাইলে নিজের একটা sort key (ঐচ্ছিক)। Tracking-এর প্রশ্নের জন্য: partition key tracking_no। এটা data-র আলাদা একটা নতুন key-তে সাজানো কপি, নিজের partition-গুলোতে ছড়ানো, এজন্যই নাম global।

  • এটা যেকোনো সময় যোগ করা যায়, মাসের পর মাস চলা table-এও (DynamoDB আগের item দিয়ে এটাকে backfill করে, table চালুই থাকে), আর যেকোনো সময় delete করা যায়।
  • এর নিজের capacity আছে (provisioned RCU আর WCU, অথবা table-এর মতো on-demand)।
  • এটা update হয় asynchronously, সাধারণত এক সেকেন্ডেরও কম সময়ে। Read শুধু eventually consistent; GSI-তে strongly consistent read চাইলে error আসে।
  • প্রতি key value-তে কোনো আকারের সীমা নেই।
  • Default সীমা প্রতি table-এ ২০টা GSI (soft limit, বাড়ানো যায়)।
  • Table-এর মতো GSI-এর key value unique হতে হয় না: অনেক order-এর একই status থাকতে পারে, আর index-এ Query করলে সবগুলোই আসে।

২০২৫ থেকে GSI-এর key multi-attribute key-ও হতে পারে: partition key-তে সর্বোচ্চ চারটা attribute, sort key-তেও চারটা, নিজে নিজে status#date-এর মতো জোড়া string বানানোর বদলে। পুরোনো সমাধান, মানে জোড়া দেওয়া একটা string attribute, এখনো চলে, আর পুরোনো exam প্রশ্ন সেটাই ধরে নেয়।

#এক নজরে LSI বনাম GSI

এটা owner-এর পরীক্ষার table, এখনকার DynamoDB documentation-এর সাথে মিলিয়ে দেখা:

LSI (local secondary index)GSI (global secondary index)
Partition keyTable-এর মতোই (user_id)যেকোনো attribute (tracking_no, email)
Sort keyTable থেকে আলাদা (price)ঐচ্ছিক, যেকোনো attribute (order_date)
কখন বানানো যায়শুধু table বানানোর সময়যেকোনো সময়, বানানোর সময় বা পরে (backfill হয়)
কখন delete করা যায়শুধু table delete করলেযেকোনো সময়
CapacityTable-এর RCU / WCU ভাগ করে নেয়নিজের RCU / WCU
আকারের সীমাপ্রতি item collection-এ ১০ GB (একটা partition key value, table + LSI)নেই
Read consistencyStrong আর eventual দুটোইশুধু eventual
কয়টাপ্রতি table-এ ৫টাপ্রতি table-এ ২০টা (default সীমা, বাড়ানো যায়)

#Projection: index কী কী কপি রাখে

Index রাখে table-এর key attribute আর index-এর key, সাথে তুমি যা project করো:

  • KEYS_ONLY: সবচেয়ে ছোট index; বাকি তথ্য দেখাতে হলে key দিয়ে table থেকে পুরো item আনতে হয়।
  • INCLUDE: key-এর সাথে নাম ধরে বলে দেওয়া কিছু attribute (যেমন support screen-এর জন্য status আর price)।
  • ALL: সব attribute; দ্বিতীয়বার পড়তে হয় না, কিন্তু index table-এর সমান বড়, আর প্রতিটা write-এ index-এর খরচও table-এর সমান।

LSI-তে Query করার সময় project না করা attribute চাইলেও পাওয়া যায়: DynamoDB সেটা table থেকে এনে দেয়, বাড়তি read খরচে। GSI সেটা পারে না: শুধু project করা attribute-ই ফেরত আসে। তাই access pattern-এ যা লাগে ঠিক ততটুকু project করো, বেশি না।

#GSI-এর write ফাঁদ: back-pressure

Notes-এর table বলছে GSI “মূল table-এর capacity ব্যবহার করে না”। কথাটা সত্যি, কিন্তু এর আড়ালে লুকিয়ে আছে GSI-এর সবচেয়ে বেশি জিজ্ঞেস করা তথ্যটা। Table-এ যে write কোনো indexed attribute ছোঁয়, সেটা GSI-তেও লিখতে হয়, আর তাতে খরচ হয় GSI-এর write capacity। GSI-এর capacity ফুরিয়ে গেলে DynamoDB মূল table-এর write-ই throttle করে দেয় (ProvisionedThroughputExceededException), table-এর নিজের capacity অনেক খালি পড়ে থাকলেও। এর নাম back-pressure।

তাই provisioned mode-এ প্রতিটা GSI-কে অন্তত table-এর সমান write capacity দাও (অথবা দুটোকেই auto scaling-এ রাখো), কিংবা table on-demand mode-এ চালাও, যেখানে index table-এর সাথে সাথে বাড়ে। Read-এর হিসাব উল্টো: যে GSI কেউ পড়েই না, সেটাকেও প্রতিটা write সামলাতে হয়।

দ্বিতীয় ফাঁদ হলো কম রকমের মানওয়ালা GSI key। status-কে key করা GSI-তে মান হাতে গোনা কয়েকটা, তাই sale-এর দিনে প্রায় সব write গিয়ে পড়ে index-এর shipped partition-এ: index-এর ভেতরে hot partition, আর table-এর ওপর সেই একই back-pressure। একটা suffix দিয়ে ছড়িয়ে দাও (shipped#0 … shipped#9), আর shard-গুলোতে একসাথে query চালাও; lesson 15.4-এর hot partition-এর সেই write sharding-এর বুদ্ধিই।

LSI আর GSI: একই table অন্যভাবে পড়ার দুই উপায়

একটা e-commerce shop সব order রাখে একটা table-এ, যার partition key user_id আর sort key order_date। এখন customer care অন্য রকম প্রশ্নের উত্তর চায়: একজন customer-এর সবচেয়ে দামি order কোনগুলো, আর tracking number দিয়ে একটা order খুঁজে বের করা।

পরিস্থিতি

Partition key একই, sort key আলাদা, strongly consistent read চলে।

একই partition
Shop app
LSI: price দিয়েuser_id + price
Orders tableuser_id + order_date
GSI: tracking দিয়েtracking_no
Query user_101

Orders table, partition user_101

user_idorder_dateprice
user_1012026-03-021,200
user_1012026-05-1845,000
user_1012026-08-093,500
ধাপ 1 / 4
1

Table প্রত্যেক customer-এর order একসাথে রাখে, order_date অনুযায়ী সাজিয়ে। “User_101-এর order, নতুনগুলো আগে” একটা সস্তা Query-তেই হয়ে যায়। কিন্তু “User_101-এর সবচেয়ে দামি order আগে” হয় না: table তো price অনুযায়ী সাজানো না।

এবার নিজেই একটা index plan করো। App-কে কোন প্রশ্নের উত্তর দিতে হবে আর table সম্পর্কে তিনটা তথ্য বেছে নাও; planner বলে দেবে কোনটা দিয়ে উত্তর মিলবে, কোন key, ছোট একটা নমুনা থেকে কোন row ফেরত আসে, আর কোথায় সাবধান থাকতে হবে।

Index planner: এই প্রশ্নের উত্তর কোন index দেবে?

একটা e-commerce shop-এর orders table-এর partition key user_id, sort key order_date। App-কে কোন প্রশ্নের উত্তর দিতে হবে আর table সম্পর্কে তুমি কী জানো, সেটা বেছে নাও, তারপর plan করো। নমুনা table-এ ৮টা order আছে; production-এ আছে ২০ লাখ।

প্রশ্ন

Table কি মাসের পর মাস ধরে চালু?

Strongly consistent read লাগবে?

একজন customer-এর order কি ১০ GB পার হতে পারে?

ফল দেখতে “Plan করো” চাপো।

#DAX: code না বদলেই microsecond

DynamoDB উত্তর দেয় এক অঙ্কের millisecond-এ। বেশিরভাগ app-এর জন্য এটাই যথেষ্ট। কিন্তু কিছু কাজে একই item বারবার পড়া হয়: বড় কোনো খবরের article, ঈদের flash sale-এ একটা product page, একটা game-এর leaderboard setting। প্রতিটা read-এ read capacity খরচ হয়, আর একটাই খুব গরম key তার partition-কে throttle পর্যন্ত করে দিতে পারে।

Amazon DynamoDB Accelerator (DAX) হলো পুরোপুরি managed, memory-তে থাকা একটা cache cluster, শুধু DynamoDB-র জন্য বানানো। এটা cache থেকে read-কে microsecond-এ নামিয়ে আনে আর table-এর ঘাড় থেকে read-এর বোঝা সরায়।

  • এটা DynamoDB-র API-ই বোঝে। Application সাধারণ DynamoDB client-এর জায়গায় DAX client বসায় (Java, .NET, Node.js, Python, Go), আর cluster-এর endpoint দেখিয়ে দেয়; GetItem, Query, PutItem যেমন ছিল তেমনই থাকে। “Application logic বদলাতে হয় না” মানে এটাই।
  • এটা চলে তোমার VPC-র ভেতরে একটা cluster হিসেবে: একটা primary node আর সর্বোচ্চ ১০টা read replica; production-এ AWS-এর পরামর্শ তিনটা AZ-এ অন্তত তিনটা node। টাকা দিতে হয় প্রতি node-ঘণ্টায়, তাই DAX প্রতি request-এ দাম দেওয়ার service না।
  • এটা read-through আর write-through। Miss হওয়া read table-এ যায়, আর ফলটা cache-এ থেকে যায়; write আগে DAX হয়ে table-এ লেখা হয়, তারপর item cache update হয়।

#DAX-এর ভেতরে দুটো cache

Item cacheQuery cache
কী রাখেGetItem আর BatchGetItem-এর ফল, primary key দিয়েQuery আর Scan-এর result set, তাদের হুবহু parameter দিয়ে
Default TTL৫ মিনিট৫ মিনিট
DAX দিয়ে write হলে update হয়?হ্যাঁ (write-through)না: TTL শেষ না হওয়া পর্যন্ত পুরোনো result set-ই রাখে

যেগুলো DAX করে না, আর পরীক্ষা যেগুলো জিজ্ঞেস করতে ভালোবাসে:

  • এটা শুধু eventually consistent read cache করে। Strongly consistent read (বা transactional read) সোজা table-এ চলে যায়, আর তার ফল cache-এ রাখা হয় না।
  • এটা write কমায় না: প্রতিটা write-এ table-এর WCU খরচ হয়ই। Write-এর ঢল সামলাতে DAX ভুল হাতিয়ার (SQS দিয়ে buffer করো, বা on-demand capacity নাও)।
  • যে write DAX দিয়ে যায় না (অন্য কোনো app সরাসরি table-এ লিখল, বা global table-এর অন্য Region থেকে replicate হয়ে এল), সেটা cache-কে পাশ কাটিয়ে যায়, আর cache TTL শেষ না হওয়া পর্যন্ত পুরোনো থাকে।

#DAX নাকি ElastiCache?

দুটোই memory-তে থাকা cache, তাই পরীক্ষা তোমাকে বাছতে বলে।

  • DAX DynamoDB-র আলাদা আলাদা item আর Query / Scan-এর ফল cache করে, তোমার application-এ cache-এর কোনো code ছাড়াই।
  • ElastiCache (lesson 7.5) সব কাজের: কী রাখবে সেটা ঠিক করে তোমার code। এটা ব্যবহার করো হিসাব করে বের করা বা aggregate করা ফলের জন্য (হাজারো item থেকে বানানো “এই সপ্তাহে সবচেয়ে বেশি পড়া” ranking), যেকোনো database-এর জন্য, আর session রাখার জন্য। Application নিজেই এতে লেখে আর পুরোনো হলে বাতিল করে (cache-aside, write-through, lesson 7.5)।

প্রায়ই দুটো পাশাপাশি থাকে: item read-এর জন্য DynamoDB-র সামনে DAX, আর application যে ফল হিসাব করে বের করে তার জন্য ElastiCache।

DAX: যে cache DynamoDB-র ভাষা বোঝে

একটা news site তার article রাখে DynamoDB-তে। বড় কোনো খবরের সময় একই কয়েকটা article মিনিটে লাখ লাখ বার পড়া হয়। DAX বসে VPC-র ভেতরে, app আর table-এর মাঝখানে।

পরিস্থিতি

প্রথম read যায় table-এ; পরেরগুলো memory থেকে আসে microsecond-এ।

VPC
Article APIDAX client
DAX cluster৩টা nodemiss
Articles table
get a-42
ধাপ 1 / 4
1

App সাধারণ DynamoDB client-এর বদলে DAX client ব্যবহার করে: get, put, query সব call একই রকম, শুধু endpoint বদলায়। “Application logic বদলাতে হয় না” বলতে এটাই বোঝায়। খবরটার প্রথম read হলো cache miss।

#DynamoDB Streams: প্রতিটা পরিবর্তনে সাড়া দাও

অনেক কাজ হওয়া উচিত data বদলেছে বলে, যে request data বদলাল তার ভেতরে না: user register করলে welcome email, একটা search index বা summary table হালনাগাদ রাখা, audit log-এ পরিবর্তন কপি করা। এগুলো sign-up request-এর ভেতরে করলে request ধীর আর ভঙ্গুর হয়ে যায়।

DynamoDB Streams হলো একটা table-এর item পর্যায়ের পরিবর্তনের (insert, modify, remove) ক্রম মেনে চলা একটা log।

  • প্রতিটা পরিবর্তনে একটা stream record তৈরি হয়, ঠিক একবার লেখা, প্রতিটা item-এর ক্ষেত্রে ক্রম ঠিক রেখে, আর রাখা হয় ২৪ ঘণ্টা।
  • Stream view type ঠিক করে record-এ কী থাকবে: KEYS_ONLY, NEW_IMAGE, OLD_IMAGE বা NEW_AND_OLD_IMAGES (আগে আর পরে দুটোই, audit বা পার্থক্য বের করার জন্য)।
  • Stream ভাগ করা থাকে shard-এ। একই shard একসাথে দুইটার বেশি process পড়লে throttle হয়।
  • Stream চালু করলে table ধীর হয় না, আর Lambda দিয়ে পড়লে stream read-এর আলাদা কোনো খরচ নেই।

#Stream process করা

  • Lambda (সাধারণ উত্তর): একটা event source mapping shard-গুলো poll করে, আর record-এর batch নিয়ে তোমার function চালায় (invocation-এর ধরনগুলো lesson 15.1-এ)। Event filtering দিয়ে function চলার আগেই record বাদ দেওয়া যায়, যেমন “শুধু INSERT event”।
  • DynamoDB Streams Kinesis Adapter, Kinesis Client Library (KCL)-এর সাথে, যখন EC2 বা container-এ তোমার নিজের consumer application চালাও।

সাধারণ ব্যবহার: সাথে সাথে সাড়া দেওয়া (welcome email, push notification), real-time ব্যবহারের হিসাব, derived table-এ লেখা (দিন অনুযায়ী বিক্রির summary), এক Region থেকে অন্য Region-এ replication (Global Tables এভাবেই কাজ করে, নিচে আছে), audit, আর search বা analytics store-এ data পাঠানো।

#যখন একটা record বারবার fail করে

একটা shard-এর record ক্রম মেনে process হয়। Function একটা batch-এ error ছুড়লে Lambda একই batch বারবার retry করতে থাকে, আর ওই shard-এ তার পেছনের সবকিছু অপেক্ষা করে। Default-এ এটা চলে যতক্ষণ না record-টা ২৪ ঘণ্টা পরে stream থেকে মুছে যায়, আর এর মধ্যে যেসব record-এর মেয়াদ ফুরায়, সেগুলোও হারিয়ে যায়।

Event source mapping-এ ঠিক করে দাও:

  • Maximum retry attempts আর maximum record age, যাতে খারাপ batch-এর আশা ছেড়ে দেওয়া হয়।
  • Bisect batch on function error: batch-কে দুই ভাগ করে খারাপ record-টা আলাদা করো।
  • একটা on-failure destination (SQS queue বা SNS topic, বা S3), যেখানে বাদ পড়া record-এর বিবরণ জমা হয়, যাতে পরে দেখে আবার চালানো যায়।
  • অথবা function থেকে partial batch failure জানাও, যাতে শুধু ব্যর্থ record-গুলোই আবার চেষ্টা হয়।

#Kinesis Data Streams for DynamoDB

একটা table নিজের পরিবর্তনগুলো তোমার নিজের একটা Kinesis data stream-এও পাঠাতে পারে (lesson 13.5)। Slide এটাকে “newer” বলে; আসলে দুটো ব্যবস্থাই পাশাপাশি আছে, আর একটা table একসাথে দুটোই ব্যবহার করতে পারে।

DynamoDB StreamsKinesis Data Streams for DynamoDB
কতদিন রাখে২৪ ঘণ্টাসর্বোচ্চ ১ বছর (৩৬৫ দিন)
Consumerপ্রতি shard-এ একসাথে ২ জন পাঠকপ্রতি shard-এ ৫ জন, বা enhanced fan-out দিয়ে ২০ জন
কী দিয়ে processLambda trigger, DynamoDB Streams Kinesis AdapterLambda, Data Firehose, Managed Service for Apache Flink, Glue streaming ETL, KCL
Duplicateকখনো না: ঠিক একবারহতে পারে: consumer-এ duplicate বাদ দাও
ক্রমপ্রতিটা item-এ, যেভাবে লেখা হয়েছেRecord-এর আনুমানিক তৈরির timestamp দেখে আবার সাজাও
ShardDynamoDB সামলায়Kinesis stream-এর আকার তুমি ঠিক করো (বা on-demand mode)

Notes ব্যাপারটা সহজ করে বলেছে: data এক বছর পর্যন্ত রাখতে হলে, অনেক consumer থাকলে, বা ভারী analytics করতে হলে Kinesis Data Streams জুড়ে দাও; “item বদলালে কিছু একটা করো”-র জন্য DynamoDB Streams + Lambda।

DynamoDB Streams: প্রতিটা পরিবর্তনে সাড়া দাও

একটা food delivery app চায় প্রতিটা নতুন user একটা welcome email পাক, আর analytics team চায় প্রতিটা পরিবর্তন S3-এ জমা হোক। দুটোই আসে table-এর change log থেকে, sign-up-এর code-এ বাড়তি কিছু না লিখেই।

পরিস্থিতি

Insert → stream record → Lambda poll করে → email। Sign-up request এর কোনোটার জন্যই অপেক্ষা করে না।

Sign-up API
Users table
Table stream২৪ ঘণ্টা
Welcome function
Email (SES)
On-failure queue
put user
ধাপ 1 / 4
1

রহিম sign up করল। API Users table-এ একটা item লিখেই সাথে সাথে উত্তর দেয়। Request-এর ভেতরেই email পাঠালে sign-up ধীর হতো, আর mail service একটু গড়বড় করলেই ব্যর্থ হতো।

#Global Tables: এক table, অনেক Region

App যদি সারা পৃথিবীর user-কে সেবা দেয় আর সবার জন্যই কম latency-তে read আর write দরকার হয়, তাহলে এক Region-এর table দিয়ে চলবে না: ঢাকা থেকে Virginia-র table-এ write মানে পৃথিবীর অর্ধেক পার হওয়া। Global Tables তোমার বেছে নেওয়া প্রতিটা Region-এ একটা table-এর পুরো একটা replica রাখে, আর এদের মধ্যে replicate করে।

  • Active-active: প্রতিটা replica read আর write দুটোই নেয়। RDS বা Aurora-র read replica থেকে পার্থক্য এখানেই, সেগুলো শুধু পড়তে পারে (lesson 7.1 আর 7.3)।
  • Replication asynchronous, সাধারণত এক সেকেন্ডের মধ্যে। এই default mode-এর নাম multi-Region eventual consistency।
  • Conflict: একই item দুই Region-এ একই সময়ে বদলালে DynamoDB প্রতিটা item-এর ক্ষেত্রে সবচেয়ে পরের internal timestamp-ওয়ালা version রাখে: last writer wins। অন্য update-টা চুপচাপ হারিয়ে যায়। Conditional write শুধু local replica যাচাই করে, তাই এটা আটকাতে পারে না। Design দিয়েই এড়াও: একটা item-এর (এক user, এক booking) সব write একটা “home” Region-এ পাঠাও।
  • Disaster recovery: একটা Region বিকল হলে তার user-দের অন্য replica-তে পাঠাও (Route 53 health check, lesson 8.4); app পড়া-লেখা চালিয়ে যায়। যে write তখনো replicate হয়নি, সেগুলো Region ঠিক হলে পৌঁছায়, তাই recovery point কয়েক সেকেন্ড, শূন্য না। Global table-এর availability SLA ৯৯.৯৯৯% (এক Region-এর table-এর ৯৯.৯৯%)।
  • খরচ: প্রতিটা replicated write যে Region-এ নামে, সেখানে একটা write হিসেবে bill হয়, সাথে Region-এর মাঝে data transfer। তিনটা Region মানে write-এর bill মোটামুটি তিন গুণ।
  • কোনো Region-এ strongly consistent read সর্বশেষ local version দেয়; অন্য Region-এ শেষবার লেখা item সেখানে পুরোনো থাকতে পারে।

“আগে DynamoDB Streams চালু করতে হবে।” Notes আর slide এটাকে পূর্বশর্ত বলেছে, আর পুরোনো exam প্রশ্ন এখনো এই উত্তরটাই চায়। কারণটা সত্যি: replication প্রতিটা replica-র stream পড়েই চলে। তবে Global Tables-এর এখনকার version-এ (2019.11.21) replica যোগ করলে DynamoDB নিজেই stream চালু করে দেয়, আর replica-তে সেটা বন্ধ করা যায় না। Global Tables-এর পাশে কোনো option “enable DynamoDB Streams” বললে সেটা এই শর্তটার কথাই বলছে।

#পাশের কথা: multi-Region strong consistency (২০২৫)

২০২৫-এর জুন থেকে একটা global table এর বদলে multi-Region strong consistency mode-এ বানানো যায়:

  • ঠিক তিনটা Region: তিনটা replica, অথবা দুটো replica আর একটা witness (DynamoDB-র সামলানো একটা অংশ, যেটায় তুমি পড়তে বা লিখতে পারো না)।
  • একটা write ফেরত আসে অন্তত আরেকটা Region-এ synchronously replicate হওয়ার পরেই, তাই যেকোনো Region-এ strongly consistent read সর্বশেষ version দেয়, আর recovery point শূন্য।
  • Write আর strong read ধীর হয় (Region পেরিয়ে আসা-যাওয়া)। কোনো item অন্য Region-এ ঠিক সেই মুহূর্তে বদলানো হচ্ছে, এমন সময় সেটায় write করলে ReplicatedWriteConflictException আসে, যেটা আবার চেষ্টা করা যায়; চুপচাপ মুছে যায় না।
  • Mode ঠিক করতে হয় বানানোর সময়, খালি table থেকে; পরে একটা একটা করে replica যোগ বা বাদ দেওয়া যায় না, আর TTL, LSI আর transaction চলে না।

পরীক্ষা অন্য কিছু না বললে “Global Tables” মানে default mode-ই ধরে নাও।

Global Table: একটা table, অনেক Region

একটা travel booking app সেবা দেয় ঢাকা, ইউরোপ আর আমেরিকায়। প্রতিটা Region-এ bookings table-এর পুরো একটা replica আছে, আর user পড়ে-লেখে নিজের সবচেয়ে কাছেরটায়।

পরিস্থিতি

Active-active: প্রতিটা replica write নেয়, আর পরিবর্তন অন্যগুলোতে পৌঁছায় মোটামুটি এক সেকেন্ডে।

ap-southeast-1
eu-central-1
us-east-1
ঢাকার user
ইউরোপের user
আমেরিকার user
Replica
Replica
Replica
put booking
ধাপ 1 / 4
1

ঢাকার একজন user flight book করলেন। Write যায় Singapore-এর replica-তে, মাত্র কয়েক millisecond দূরে, আর সেখানেই সফল হয়। কেউ ইউরোপ বা আমেরিকার জন্য অপেক্ষা করে না।

#TTL: পুরোনো item নিজেই মুছে যাক

Session token, OTP, ফেলে রাখা shopping cart, যে log ঠিক ৯০ দিন রাখতে হবে আর একদিনও বেশি না: এদের একটা স্বাভাবিক মেয়াদ আছে। নিজের job দিয়ে এগুলো মুছলে write capacity আর code দুটোই লাগে। Time to Live (TTL) কাজটা করে বিনা পয়সায়।

  • একটা attribute-এর নাম বেছে নাও (যেমন expires_at), আর table-এ সেই নামে TTL চালু করো।
  • প্রতিটা item ওই attribute-এ একটা Number রাখে: মেয়াদ শেষের সময়, Unix epoch সেকেন্ডে (যেমন 1793170200)। যে item-এ attribute-টা নেই, বা মানটা number না, সেটা কখনো মেয়াদোত্তীর্ণ হয় না।
  • সময় পার হলে একটা background process item-টা খুঁজে মুছে দেয়, সাধারণত কয়েক দিনের মধ্যে, ঠিক সেই সেকেন্ডে না। ততক্ষণ মেয়াদ ফুরানো item read, query আর scan-এ আসতে থাকে: app-এ filter করে বাদ দাও (expires_at > now)।
  • TTL-এর delete কোনো write capacity খরচ করে না, কোনো টাকাও লাগে না। Global table-এ অন্য Region-গুলোতে replicate হওয়া delete সাধারণ write হিসেবে bill হয়।
  • প্রতিটা TTL delete table-এর stream-এ আসে service-এর করা একটা REMOVE record হিসেবে (userIdentity = dynamodb.amazonaws.com), তাই একটা Lambda function চলে যাওয়ার আগে মেয়াদ ফুরানো item-গুলো S3-এ archive করে রাখতে পারে।

#Backup: PITR আর on-demand

ভুল filter-ওয়ালা একটা script, খারাপ একটা deployment, ভুল করে চালানো DeleteItem-এর loop: replication এগুলোর কোনোটাতেই বাঁচায় না, কারণ ভুলটাও replicate হয়ে যায়। DynamoDB-তে backup-এর দুটো ব্যবস্থা আছে, আর কোনোটাই table-এর performance বা capacity ছোঁয় না।

Point-in-time recovery (PITR): অবিরাম backup।

  • চালু করার পরে recovery period-এর ভেতরে যেকোনো সেকেন্ডে restore করা যায়। Notes আর slide বলে “গত ৩৫ দিন”; ২০২৫-এর জানুয়ারি থেকে এই সময়টা ১ থেকে ৩৫ দিনের মধ্যে ঠিক করে দেওয়া যায় (৩৫ হলো default আর সর্বোচ্চ)। ছোট window data রাখার নিয়ম মানতে কাজে লাগে।
  • Bill হয় table-এর আকার ধরে।

On-demand backup: তুমি যখন চাও তখন নেওয়া পুরো backup (migration-এর আগে, মাস শেষে)।

  • থাকে তুমি delete না করা পর্যন্ত, অনেক দিন রাখা আর audit-এর জন্য।
  • Performance বা latency-তে কোনো প্রভাব নেই, বিশাল table-এও।
  • AWS Backup দিয়ে সামলানো যায় (lesson 23.2): backup plan আর schedule, অন্য Region আর অন্য account-এ কপি, অনেক দিন রাখার জন্য cold storage, আর vault lock।

দুটোর একটা নিয়ম notes জোর দিয়ে বলেছে: restore করলে সবসময় একটা নতুন table তৈরি হয়। চালু table-এর ওপর কখনো লেখে না। তারপর application-কে নতুন table-এর দিকে ঘোরাও, অথবা হারানো item-গুলো পুরোনোটায় কপি করে ফেরত আনো। Restore হওয়া table data, index আর capacity mode পায়, কিন্তু সবকিছু না: auto scaling policy, IAM policy, CloudWatch alarm, tag, stream setting, TTL setting আর PITR setting আবার দিতে হয়।

#S3-এ export আর S3 থেকে import

S3-এ export একটা table-এর data S3 bucket-এ file হিসেবে লিখে দেয়।

  • PITR লাগে, কারণ এটা পড়ে অবিরাম backup থেকে, table থেকে না: কোনো read capacity খরচ হয় না, চালু traffic-এ কোনো প্রভাবও পড়ে না।
  • Recovery window-এর যেকোনো মুহূর্তে table যেমন ছিল তার full export, অথবা দুটো সময়ের মাঝের শুধু পরিবর্তনের incremental export।
  • Format: DynamoDB JSON বা Amazon Ion। অন্য account বা Region-এর bucket-এও পাঠানো যায়।
  • ব্যবহার: Amazon Athena দিয়ে analytics (file-গুলোর ওপর SQL, lesson 17.1), audit-এর জন্য snapshot, আর data আবার DynamoDB-তে import করার আগে ETL।

S3 থেকে import file থেকে DynamoDB-তে data তোলে।

  • Format: CSV, DynamoDB JSON বা Amazon Ion, চাইলে compress করা (GZIP বা ZSTD)।
  • সবসময় নতুন একটা table বানায়; চালু table-এ import করা যায় না।
  • কোনো write capacity খরচ করে না, তাই লাখ লাখ item PutItem দিয়ে লেখার চেয়ে অনেক সস্তা।
  • যেসব row validation-এ আটকায়, সেগুলোর হিসাব থাকে CloudWatch Logs-এ।

TTL, backup আর S3: data-র জীবনকাল

একটা mobile wallet OTP session আর transaction রাখে DynamoDB-তে। পুরোনো session নিজে থেকেই মুছে যেতে হবে, ভুল script চালালে ফিরিয়ে আনার উপায় থাকতে হবে, আর finance চায় production না ছুঁয়েই report।

পরিস্থিতি

মেয়াদ ফুরানো item background-এ মুছে যায়, বিনা খরচে, কয়েক দিনের মধ্যে, আর সেই delete stream-এ দেখা যায়।

Wallet app
Wallet table
Stream function
S3 bucket
Athena
PITR backup
Restored table
put session

Session item

session_id

s-77f1

otp_hash

9c1e…

expires_at

1793170200

ধাপ 1 / 4
1

প্রতিটা OTP session item-এ একটা attribute থাকে expires_at: একটা Number, যাতে Unix epoch time সেকেন্ডে লেখা, এখানে বানানোর ১০ মিনিট পরের সময়। Table-এ ওই attribute-এর নামে TTL চালু করা থাকে।

#আরও কিছু integration যেগুলো জানা দরকার

  • Zero-ETL integration: DynamoDB নিজে থেকেই একটা table Amazon Redshift-এ (অক্টোবর ২০২৪ থেকে generally available) বা Amazon OpenSearch Service-এ (OpenSearch Ingestion দিয়ে) replicate করতে পারে, তোমাকে কোনো pipeline লিখতে হয় না। দুটোই প্রথমবার লোড করতে PITR export আর তারপর পরিবর্তনের data ব্যবহার করে। টাটকা data-র ওপর SQL analytics, বা DynamoDB item-এ full-text search-এর জন্য এগুলো নাও। Redshift আর OpenSearch আছে chapter 17-এ।
  • VPC gateway endpoint: private subnet-এর Lambda function বা EC2 instance route table-এ যোগ করা একটা বিনা পয়সার gateway endpoint দিয়ে DynamoDB-তে পৌঁছায়, NAT gateway বা internet-এর পথ ছাড়াই (lesson 22.2)। Interface endpoint (PrivateLink)-ও আছে, VPN বা Direct Connect দিয়ে on-premises থেকে ঢোকার জন্য। অন্যদিকে DAX তো আগে থেকেই তোমার VPC-র ভেতরে চলে।
  • Fine-grained access control: একটা IAM policy একজন user-কে শুধু সেই item-গুলোতে আটকে রাখতে পারে যাদের partition key তার নিজের পরিচয়ের সমান, condition key dynamodb:LeadingKeys আর ${cognito-identity.amazonaws.com:sub}-এর মতো একটা policy variable দিয়ে, আর dynamodb:Attributes দিয়ে নির্দিষ্ট কিছু attribute-এ। এভাবেই একটা university app প্রত্যেক ছাত্রকে সরাসরি DynamoDB থেকে শুধু নিজের row পড়তে দেয়; Cognito-র দিকটা lesson 15.7-এ।
{
  "Effect": "Allow",
  "Action": ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:PutItem"],
  "Resource": "arn:aws:dynamodb:ap-southeast-1:111122223333:table/StudentGrades",
  "Condition": {
    "ForAllValues:StringEquals": {
      "dynamodb:LeadingKeys": ["${cognito-identity.amazonaws.com:sub}"]
    }
  }
}

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

লক্ষণসম্ভাব্য কারণসমাধান
Table-এর WCU খালি থাকলেও write throttle হচ্ছেকম write capacity-ওয়ালা GSI (back-pressure), বা GSI-এর hot keyGSI-কে অন্তত table-এর সমান WCU, বা auto scaling / on-demand; কম রকমের মানওয়ালা GSI key shard করো
ValidationException: Consistent reads are not supported on global secondary indexesGSI-তে strongly consistent read চাওয়া হয়েছেGSI eventually consistent পড়ো, দরকার হলে তারপর table থেকে strong read দিয়ে GetItem; অথবা LSI (নতুন table)
এইমাত্র লেখা item GSI query-তে নেইGSI-তে পৌঁছানো asynchronousনিজের লেখা item table থেকে key দিয়ে পড়ো; সামান্য দেরি মেনে নাও
এক customer-এর জন্য ItemCollectionSizeLimitExceededExceptionTable-এ LSI আছে আর ওই partition key value ১০ GB ছুঁয়েছেতার বদলে GSI; key নতুন করে design; পুরোনো item archive
“Production table-এ আমাদের একটা LSI লাগবে”LSI শুধু table বানানোর সময় থেকেই থাকেGSI; অথবা LSI-সহ নতুন table, export আর import দিয়ে ভরা
DAX দিয়ে একটা query পুরোনো মান দেখাচ্ছেWrite-এ query cache update হয় না; write DAX-কে পাশ কাটিয়েছেQuery cache-এর TTL ছোট করো; টাটকা লাগবেই এমন data-র জন্য strongly consistent read
DAX বসিয়েও bill কমেনিকাজটা write-ভারী, বা read strongly consistentDAX শুধু eventually consistent read-এ সাহায্য করে; write-এর জন্য on-demand বা buffer
ঘণ্টার পর ঘণ্টা কোনো welcome email নেই, log-এ function errorএকটা poison record shard আটকে রেখেছে; ২৪ ঘণ্টায় মুছে যাওয়া পর্যন্ত retryMax retry / record age, bisect batch, on-failure destination, partial batch response
এক Region-এ confirm হওয়া booking উধাওদুই Region-এ একসাথে write: last writer winsপ্রতিটা item-এর জন্য একটা home Region; অথবা strong consistency mode
মেয়াদ ফুরানো session এখনো ফেরত আসছেTTL background-এ মোছে, সাধারণত কয়েক দিনের মধ্যেRead-এ expiry attribute দিয়ে filter
Restore করা table-এ alarm, TTL, stream কিছুই নেইএই setting-গুলো restore হয় নাRunbook-এ এগুলো আবার বানানোর ধাপ রাখো
S3-এ export শুরুই হচ্ছে নাTable-এ PITR চালু নেইআগে PITR চালু করো

#System design-এ DynamoDB-র feature-গুলো

  • আগে access pattern: app কোন কোন প্রশ্ন করবে তার তালিকা বানাও, তারপর প্রতিটাকে একটা key বা index দাও। ভালো করে বাছা কয়েকটা GSI-ওয়ালা একটা table প্রায়ই কয়েকটা table-এর কাজ করে দেয়। LSI রাখো সেই বিরল ক্ষেত্রের জন্য, যেখানে অন্য sort key-তে strong read লাগে আর প্রতি key-তে ১০ GB-তে আঁটে।
  • প্রচুর read, গরম item: table-এর সামনে DAX (product page, খবর, configuration), হিসাব করা ফলের জন্য ElastiCache, পুরো response-এর জন্য CloudFront (lesson 11.1)।
  • Event-driven back end: table হলো আসল সত্যের উৎস, আর তার stream বাকি সব চালায়: email (SES), search index, aggregate, audit trail, cache বাতিল করা। Lesson 15.8-এর blog ঠিক এটাই করে।
  • Global app: active-active-এর জন্য Global Tables, conflict হওয়া চলবে না এমন write-এর জন্য প্রতিটা item-এর একটা home Region; শূন্য RPO-র জন্য ধীর write মেনে নেওয়া গেলে strong consistency mode।
  • Data-র জীবনকাল: স্বাভাবিক মেয়াদের জন্য TTL, মেয়াদ ফুরানো item S3-এ archive করতে একটা stream function, তারপর S3 lifecycle rule (lesson 10.5)।
  • সুরক্ষা: প্রতিটা production table-এ PITR, AWS Backup দিয়ে cross-Region কপিসহ on-demand backup, table-এ deletion protection।
  • Production-কে কষ্ট না দিয়ে analytics: হঠাৎ আসা প্রশ্নের জন্য S3-এ export + Athena, dashboard-এর জন্য Redshift-এ zero-ETL, অবিরাম change feed-এর জন্য Kinesis Data Streams + Firehose।

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

LSI-সহ একটা table বানাও, পরে একটা GSI যোগ করো, দুটোতেই query চালাও, তারপর TTL, stream, PITR চালু করো, export আর restore করো। Output ছোট করে দেখানো; নামগুলো উদাহরণ।

REGION=ap-southeast-1

# 1. Orders table: key user_id + order_date, an LSI on price (only possible now, at creation)
aws dynamodb create-table --table-name Orders --region $REGION \
  --attribute-definitions AttributeName=user_id,AttributeType=S \
      AttributeName=order_date,AttributeType=S AttributeName=price,AttributeType=N \
  --key-schema AttributeName=user_id,KeyType=HASH AttributeName=order_date,KeyType=RANGE \
  --local-secondary-indexes '[{"IndexName":"by-price","KeySchema":[
      {"AttributeName":"user_id","KeyType":"HASH"},{"AttributeName":"price","KeyType":"RANGE"}],
      "Projection":{"ProjectionType":"INCLUDE","NonKeyAttributes":["status"]}}]' \
  --billing-mode PAY_PER_REQUEST
# "TableStatus": "CREATING", "LocalSecondaryIndexes": [{ "IndexName": "by-price", ... }]

# 2. Months later: a GSI on tracking_no (backfilled while the table stays online)
aws dynamodb update-table --table-name Orders \
  --attribute-definitions AttributeName=tracking_no,AttributeType=S \
  --global-secondary-index-updates '[{"Create":{"IndexName":"by-tracking",
      "KeySchema":[{"AttributeName":"tracking_no","KeyType":"HASH"}],
      "Projection":{"ProjectionType":"KEYS_ONLY"}}}]'
aws dynamodb describe-table --table-name Orders \
  --query 'Table.GlobalSecondaryIndexes[0].[IndexStatus,Backfilling]'
# [ "CREATING", true ]

# 3. Most expensive first (LSI, strongly consistent allowed)
aws dynamodb query --table-name Orders --index-name by-price \
  --key-condition-expression "user_id = :u" \
  --expression-attribute-values '{":u":{"S":"user_101"}}' \
  --no-scan-index-forward --consistent-read
# "Items": [ { "price": {"N": "45000"}, "order_date": {"S": "2026-05-18"}, ... } ], "Count": 4

# 4. Find by tracking number (GSI). Asking for a strong read here fails:
aws dynamodb query --table-name Orders --index-name by-tracking \
  --key-condition-expression "tracking_no = :t" \
  --expression-attribute-values '{":t":{"S":"trk-2202"}}' --consistent-read
# An error occurred (ValidationException) when calling the Query operation:
# Consistent reads are not supported on global secondary indexes
#   (run it again without --consistent-read)

# 5. TTL on expires_at (Unix epoch seconds, a Number)
aws dynamodb update-time-to-live --table-name Orders \
  --time-to-live-specification "Enabled=true, AttributeName=expires_at"
# { "TimeToLiveSpecification": { "Enabled": true, "AttributeName": "expires_at" } }

# 6. A stream with old and new images, and a Lambda trigger that only sees inserts
aws dynamodb update-table --table-name Orders \
  --stream-specification StreamEnabled=true,StreamViewType=NEW_AND_OLD_IMAGES
# "LatestStreamArn": "arn:aws:dynamodb:ap-southeast-1:111122223333:table/Orders/stream/2026-10-28T09:12:44.123"
aws lambda create-event-source-mapping --function-name order-welcome-example \
  --event-source-arn arn:aws:dynamodb:ap-southeast-1:111122223333:table/Orders/stream/2026-10-28T09:12:44.123 \
  --starting-position LATEST --batch-size 100 \
  --filter-criteria '{"Filters":[{"Pattern":"{\"eventName\":[\"INSERT\"]}"}]}' \
  --maximum-retry-attempts 3 --bisect-batch-on-function-error \
  --destination-config '{"OnFailure":{"Destination":"arn:aws:sqs:ap-southeast-1:111122223333:orders-failed-example"}}'

# 7. PITR with a 14-day window, then restore to a second before the accident
aws dynamodb update-continuous-backups --table-name Orders \
  --point-in-time-recovery-specification PointInTimeRecoveryEnabled=true,RecoveryPeriodInDays=14
# "PointInTimeRecoveryStatus": "ENABLED", "RecoveryPeriodInDays": 14
aws dynamodb restore-table-to-point-in-time --source-table-name Orders \
  --target-table-name Orders-restored --restore-date-time 2026-10-28T14:05:09Z
# "TableStatus": "CREATING"   (a new table; re-add alarms, TTL, stream)

# 8. An on-demand backup for the year-end audit
aws dynamodb create-backup --table-name Orders --backup-name orders-2026-year-end
# "BackupStatus": "CREATING"

# 9. Export to S3 for Athena: no read capacity used
aws dynamodb export-table-to-point-in-time \
  --table-arn arn:aws:dynamodb:ap-southeast-1:111122223333:table/Orders \
  --s3-bucket ddb-exports-example --export-format DYNAMODB_JSON
# "ExportStatus": "IN_PROGRESS"

# 10. Make it a global table: add a replica in Frankfurt (stream enabled for you)
aws dynamodb update-table --table-name Orders \
  --replica-updates '[{"Create":{"RegionName":"eu-central-1"}}]'
aws dynamodb describe-table --table-name Orders --query 'Table.Replicas'
# [ { "RegionName": "eu-central-1", "ReplicaStatus": "CREATING" } ]

DAX cluster বানাতে আর মাত্র একটা command লাগে (aws dax create-cluster --cluster-name orders-cache --node-type dax.r5.large --replication-factor 3 --iam-role-arn … --subnet-group-name …), তারপর app-এ শুধু client আর endpoint বদলাতে হয়।

#সারসংক্ষেপ

মূল কথাগুলো

  • LSI: partition key একই, sort key আলাদা; শুধু table বানানোর সময়, একা কখনো delete হয় না; table-এর capacity ভাগ করে; strong আর eventual দুই রকম read; প্রতি item collection-এ ১০ GB; প্রতি table-এ ৫টা।
  • GSI: যেকোনো attribute partition key (sort key ঐচ্ছিক); যেকোনো সময় যোগ বা delete (backfill হয়); নিজের capacity; শুধু eventual read; আকারের সীমা নেই; default প্রতি table-এ ২০টা। GSI-এর write capacity কম হলে মূল table throttle হয়।
  • Projection (KEYS_ONLY, INCLUDE, ALL) ঠিক করে index-এর আকার আর write-এর খরচ। Index দিয়ে উত্তর মিললে কখনো Scan না।
  • DAX: memory-তে থাকা, write-through cache cluster, তোমার VPC-তে, DynamoDB API, microsecond, logic বদলাতে হয় না; item আর query cache, default TTL ৫ মিনিট; শুধু eventually consistent read cache করে; write কমায় না। হিসাব করা বা aggregate ফল আর অন্য database-এর জন্য ElastiCache।
  • DynamoDB Streams: item পর্যায়ের পরিবর্তন ক্রম মেনে, ঠিক একবার, ২৪ ঘণ্টা, ৪ রকম view type, Lambda বা KCL adapter; retry-র সীমা, bisect আর on-failure destination ঠিক করো। Kinesis Data Streams for DynamoDB: ১ বছর পর্যন্ত, বেশি consumer, duplicate হতে পারে।
  • Global Tables: multi-Region active-active, প্রায় এক সেকেন্ডে async replication, last writer wins, ৯৯.৯৯৯% SLA; নিচে Streams চলে (এখন নিজেই চালু হয়, পরীক্ষা এখনো বলে “Streams চালু করো”)। Strong consistency mode (২০২৫): তিন Region, synchronous, RPO শূন্য, TTL / LSI / transaction নেই।
  • TTL: epoch সেকেন্ডের Number attribute, কয়েক দিনের মধ্যে background-এ মোছে, বিনা খরচে, Streams-এ দেখা যায়; read-এ মেয়াদ ফুরানো item filter করো।
  • PITR: ১ থেকে ৩৫ দিনের মধ্যে যেকোনো সেকেন্ডে (২০২৫ থেকে ঠিক করা যায়); on-demand backup delete না করা পর্যন্ত থাকে, cross-Region / cross-account কপির জন্য AWS Backup। প্রতিটা restore নতুন table বানায়।
  • S3-এ export (PITR লাগে, RCU লাগে না, full বা incremental, JSON বা Ion) + Athena; S3 থেকে import (CSV, JSON, Ion) নতুন table বানায়, WCU লাগে না, error যায় CloudWatch Logs-এ।
  • আরও: Redshift / OpenSearch-এ zero-ETL, private subnet থেকে বিনা পয়সার gateway endpoint, প্রতি user-এর row-এর জন্য dynamodb:LeadingKeys।