LearnWithShobhan

DynamoDB data modeling explained: partition key, sort key and GSI (with diagrams)

Shobhan Biswas··6 min read
>_From the course VidShare: 109 lessons, lifetime access.

Most developers come to DynamoDB from SQL, design tables the SQL way, and end up fighting the database. DynamoDB isn't hard. It just asks you a different first question: not "what is my data?" but "how will my app read it?"

Here's the mental model, with the diagrams from the VidShare course and the real table we build in it.

SQL normalises; NoSQL stores what you read

Take an e-commerce app. In SQL you split data into tables (customers, orders, addresses) and link them with foreign keys. To show a customer with their orders, the database joins those tables on the fly.

That design comes from an era when storage was expensive: normalise to save space, pay with CPU on every query. Today storage is cheap and apps need to be fast, so NoSQL flips the trade-off: store the document the way you'll read it.

Left: one JSON document with a customer, nested addresses and orders. Right: separate SQL tables Customer, Order and Address linked by IDs
SQL joins normalised tables at query time. NoSQL stores the whole document as it will be read.

The catch: you can't freely query inside nested data later. You must know your access patterns (every way your app will fetch data) before you design the table.

Partition key: which box your item lives in

DynamoDB spreads data across many partitions. When you write an item, it takes the item's partition key value, runs it through a hash function, and the result decides the partition.

Diagram: the partition key user-1 goes through a hash function f and lands in partition 3
The partition key is hashed to pick a partition. Same key, same partition, every time.

Reads work the same way. Ask for user-3 and DynamoDB hashes user-3, goes straight to the right partition, and returns the item. No scanning. That's why key lookups stay fast at any scale.

Three partitions holding user items: partition 1 has user-3, partition 2 has user-2, partition 3 has user-1, user-4 and user-5
Items spread across partitions by the hash of their partition key.

Sort key: order inside the box

Now design a songs table with album as the partition key. Two songs from the same album produce the same hash, so they land in the same partition. To keep them apart and ordered, DynamoDB lets you add a sort key, for example the song name.

Partitions holding songs grouped by album, ordered by song name within each album
Same partition key, same partition. The sort key orders items inside it.

Primary key = partition key + (optional) sort key

Primary key made of a partition key and a sort key
The primary key uniquely identifies an item.
  • The partition key is required on every item.
  • The sort key is optional. You need it whenever the partition key alone isn't unique.
  • Together they form the primary key. "Album 4, song 5": hash album 4 to find the partition, then the sort key finds song 5.

Secondary indexes: a second way to query

Say the songs table uses artist as the partition key. "All songs by an artist" is easy. But "all romantic songs"? Genre isn't a key, so the table can't answer that efficiently.

Add a global secondary index (GSI): DynamoDB keeps a copy of the data with a different primary key, here genre.

Song table keyed by artist, and a second copy keyed by genre, supporting the access patterns get songs by artist and get songs by genre
One access pattern per key design. A GSI adds another.
Global secondary index (GSI)Local secondary index (LSI)
Partition keyDifferent from the tableSame as the table
Sort keyAny attribute (optional)Different from the table
Use it forA completely new way to look items upA different ordering within the same partition

GSIs copy data, so they cost storage and write capacity, and a table has a default quota of 20 of them. Add one per real access pattern, not "just in case".

Design a real table: VidShare videos

In the VidShare course we build a video platform. The video entity has an id, user id, title, upload time, description, tags, the transcoded file sizes, and a status that moves from NOT_UPLOADED to UPLOADED to PROCESSING to READY.

Video entity with id, user-id, title, uploaded-date, description, tags, sizes, status; linked to a User entity; and the list of video statuses
The VidShare video entity and its status lifecycle.

Start from the access patterns, not the fields:

Access patternKey design
Get one video by its IDTable partition key: id (no sort key, IDs are unique)
Get all videos uploaded by a user, newest firstGSI byUserId: partition key userId, sort key uploadedTime
Search videos by title, description or tagsNot DynamoDB's job. We index them in OpenSearch

Here is the actual table from the course's CDK stack:

const table = new dynamodb.Table(this, "VideoTable", {
  partitionKey: { name: "id", type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
  removalPolicy: cdk.RemovalPolicy.DESTROY,
});
 
table.addGlobalSecondaryIndex({
  indexName: "byUserId",
  partitionKey: { name: "userId", type: dynamodb.AttributeType.STRING },
  sortKey: { name: "uploadedTime", type: dynamodb.AttributeType.NUMBER },
});

And querying the GSI with the AWS SDK v3 document client:

await docClient.send(new QueryCommand({
  TableName: process.env.TABLE_NAME,
  IndexName: "byUserId",
  KeyConditionExpression: "#userId = :userId",
  ExpressionAttributeNames: { "#userId": "userId" },
  ExpressionAttributeValues: { ":userId": userId },
  ScanIndexForward: false, // newest first
}));

Key takeaways

  • ✓Design from access patterns first. Every query needs a key that supports it.
  • ✓The partition key is hashed to pick a partition; the sort key orders items inside it.
  • ✓Primary key = partition key + optional sort key, and it must be unique.
  • ✓Add a GSI for each extra access pattern; use a search engine for full-text search.

Build the whole thing, not just the table

VidShare is 109 lessons where you build a complete serverless video platform on AWS: API Gateway and Lambda, DynamoDB, S3 presigned uploads, MediaConvert transcoding, EventBridge, OpenSearch, all tested with Jest and deployed with CDK. Watch the free architecture lesson to see the whole system first.

Free download

Linux command line cheat sheet

One printable page: navigation, files, permissions, pipes, processes, scripts and SSH.

Occasional emails with new tutorials and courses. Unsubscribe any time.

Keep reading