Item size, caching and cost control

Amazon DynamoDB · Expert · 14 min

At scale the bill is driven by item size and request patterns, and design choices can change it by an order of magnitude.

Item size multiplies cost: every write is billed per 1 KB and every read per 4 KB. Patterns that keep items lean:

  • put large blobs (documents, images) in S3 and store only the object key
  • vertical partitioning: split a big entity into several items under one partition key (SK = PROFILE, SK = SETTINGS, SK = HISTORY#...) so frequent updates touch small items
  • compress large text attributes (gzip into a binary attribute) when you must keep them inline
  • keep attribute names short in huge tables; names count toward item size

The hard limit is 400 KB per item, including attribute names.

DAX (DynamoDB Accelerator) is an in-memory, write-through cache cluster with a DynamoDB-compatible API, giving microsecond reads for read-heavy, repetitive access. It serves eventually consistent reads only and adds a cluster to run; for many apps an application-level cache or simply reading less is enough.

Other levers: use eventually consistent reads where possible (half price), project only needed attributes into GSIs, delete unused GSIs, use the Standard-IA table class for rarely accessed data (cheaper storage, pricier requests), switch steady workloads to provisioned capacity with auto scaling or reserved capacity, and watch ConsumedReadCapacityUnits per operation using ReturnConsumedCapacity.

Example

import gzip
import boto3

table = boto3.resource("dynamodb").Table("App")
doc = ("lorem ipsum " * 4000).encode()          # ~48 KB of text

table.put_item(Item={"PK": "DOC#1", "SK": "BODY",
                     "body_gz": gzip.compress(doc)})   # binary attribute
r = table.put_item(Item={"PK": "DOC#1", "SK": "META", "title": "Notes", "views": 0},
                   ReturnConsumedCapacity="TOTAL")
print(r["ConsumedCapacity"]["CapacityUnits"])   # 1.0  small item, cheap updates

body = gzip.decompress(table.get_item(
    Key={"PK": "DOC#1", "SK": "BODY"})["Item"]["body_gz"].value)
print(len(body) // 1000, "KB raw,", len(gzip.compress(doc)), "bytes stored")
# 48 KB raw, 142 bytes stored  (repetitive text compresses well)

Remember

  • Item size drives cost: writes per 1 KB, reads per 4 KB; max item 400 KB
  • Store blobs in S3 and split big entities into several items
  • DAX caches eventually consistent reads in microseconds
  • Eventual reads, lean GSI projections and table classes cut cost

Watch out for

  • Incrementing a view counter inside a 300 KB item, paying 300 write units per click.

Check yourself

  1. A 200 KB item has a counter updated 100 times per second. What is the cheapest fix?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.aws.amazon.com/amazondynamodb/latest/developerguide/DAX.html

Builds on

Unlocks

Nothing builds on this yet.