Limited Time Offer: 40% off
Back to Blog

DynamoDB vs Redis: Durable Key-Value Store or In-Memory Data Server?

JayJay

DynamoDB vs Redis looks like a contest between two key-value stores, but the two make opposite bets. DynamoDB is a managed database that writes every item to durable storage replicated across Availability Zones, then serves it in single-digit milliseconds. Redis keeps its dataset in RAM, answers reads without touching disk, and treats durability as a setting you configure.

Choose DynamoDB when the data is the system of record, you run on AWS, and your access patterns are known up front. Choose Redis when you need a cache, counters, leaderboards, rate limits, queues, or other state where speed and data structures matter more than long-term storage. Plenty of AWS applications run both, with Redis in front of DynamoDB.

If you want the Redis API with database-grade durability on AWS, look at Amazon MemoryDB or ElastiCache for Valkey with durability enabled. If you want a cache in front of DynamoDB without changing your data access code, look at DAX. Both are covered below.

DynamoDBRedis
Primary roleDurable system of recordCache and in-memory data structure server
Where data livesReplicated storage across multiple AZsRAM, with optional RDB snapshots and AOF
Typical latencySingle-digit millisecondsServed from RAM, no disk on the read path
Data modelItems with a partition key and optional sort keyStrings, hashes, lists, sets, sorted sets, streams, JSON, and more
Max value size400 KB per item512 MB per string
QueryingKey lookups and key-range queries, plus secondary indexesCommands per data type, plus Redis Query Engine in Redis 8
Dataset sizeNo practical table size limitBounded by memory
ExpiryTTL deletes items within a few daysTTL removes keys on time
DeploymentAWS only, fully managedSelf-hosted anywhere, or managed (Redis Cloud, ElastiCache, MemoryDB, others)
LicenceProprietary serviceRSALv2, SSPLv1, or AGPLv3 from Redis 8

DynamoDB vs Redis in one sentence each

DynamoDB is a durable, horizontally partitioned key-value and document store that you query by key. It removes servers, storage, replication, and patching from your job, and charges you per request or per unit of provisioned capacity.

Redis is an in-memory server that exposes data structures over a network protocol. You get atomic operations on those structures in RAM, and you decide how much durability to buy through snapshots, an append-only file, replication, or a managed service that adds a transaction log.

Durability: the deciding question

AWS describes DynamoDB as storing data on SSDs replicated across multiple Availability Zones in a Region. When PutItem returns success, the write is durable. You do not configure persistence because there is nothing to configure.

Redis durability depends on how you run it. I started the official redis image (Redis 8.10.2) and checked its defaults:

BASH
$ redis-cli CONFIG GET appendonly
appendonly
no
$ redis-cli CONFIG GET save
save
3600 1 300 100 60 10000

The default is RDB snapshotting only: save after 3,600 seconds if one key changed, after 300 seconds if 100 changed, or after 60 seconds if 10,000 changed. A write between snapshots lives only in memory. I wrote a key and killed the container with SIGKILL:

BASH
$ redis-cli SET order:1001 paid
OK
$ docker kill -s KILL redis && docker start redis
$ redis-cli GET order:1001

The reply is empty. The order is gone.

I repeated the test with redis-server --appendonly yes. The append-only file defaults to appendfsync everysec, which fsyncs once per second, so I waited a little over a second before the kill:

BASH
$ redis-cli SET order:1002 paid
OK
$ docker kill -s KILL redis && docker start redis
$ redis-cli GET order:1002
paid

With AOF on, the key survived. The trade-off is that everysec can still lose up to about a second of writes on a crash, and appendfsync always costs write throughput. Replication helps with node failure, but Redis replication is asynchronous, and the Redis Cluster documentation states plainly that Redis Cluster does not guarantee strong consistency and can lose writes it acknowledged to the client.

That is fine for a cache, a session store, or a leaderboard that can be rebuilt. It is not fine for orders, balances, or anything you cannot reconstruct.

Latency: milliseconds vs memory

AWS's own DAX documentation frames the gap well: DynamoDB response times are typically single-digit milliseconds, and an in-memory cache brings eventually consistent reads down to microseconds. Redis sits on the in-memory side of that line.

For most request paths, single-digit milliseconds is fast. A web request that calls DynamoDB three times still finishes well inside a normal latency budget. The case for Redis appears when a request touches the same keys many times, when you need hundreds of thousands of small operations per second against hot keys, or when the operation itself (increment, rank, pop, intersect) would take several DynamoDB round trips.

Be careful with published benchmark numbers for either system. Instance size, network path, item size, and consistency mode change the result. Measure your access pattern from the same network your application runs in.

Data model: items vs data structures

A DynamoDB table has a partition key and an optional sort key. Items with the same partition key form an item collection, sorted by the sort key. That design pushes you to model access patterns into keys:

BASH
aws dynamodb query --table-name sessions \
  --key-condition-expression 'pk = :u AND begins_with(sk, :p)' \
  --expression-attribute-values '{":u":{"S":"USER#42"},":p":{"S":"SESSION#"}}'

Against DynamoDB Local, this returned the two SESSION# items for USER#42 and skipped the PROFILE item in the same partition. Querying on an attribute that is not part of the key fails:

An error occurred (ValidationException) when calling the Query operation: Query condition missed key schema element

To query by device, you add a global secondary index, or you scan the table and filter. PartiQL's SELECT ... WHERE device = 'ios' will run, but without a key condition it reads the table the way a Scan does.

Redis gives you data structures instead of a table. Each one has commands that run atomically on the server:

BASH
$ redis-cli SET session:a1 '{"user":42,"device":"ios"}' EX 3600
OK
$ redis-cli TTL session:a1
(integer) 3600
$ redis-cli INCR page:home:views
(integer) 1
$ redis-cli ZADD leaderboard 3200 ada 2950 bob 4100 cy
(integer) 3
$ redis-cli ZREVRANGE leaderboard 0 1 WITHSCORES
1) "cy"
2) "4100"
3) "ada"
4) "3200"

A top-N leaderboard is one command in Redis. In DynamoDB, you would model a sort key or GSI around the score and update it on every change, and ranking a single player would take more work again.

DynamoDB has its own atomic counter through UpdateItem with ADD:

BASH
aws dynamodb update-item --table-name sessions \
  --key '{"pk":{"S":"PAGE#home"},"sk":{"S":"VIEWS"}}' \
  --update-expression 'ADD hits :one' \
  --expression-attribute-values '{":one":{"N":"1"}}' \
  --return-values UPDATED_NEW

Two calls returned "hits": {"N": "1"} then "hits": {"N": "2"}. It works, and it is durable, but each increment is a billed write against one item. A hot counter concentrates traffic on one partition key, which is the access pattern DynamoDB handles least well.

Redis 8 also ships the capabilities that used to live in Redis Stack. MODULE LIST on the 8.10.2 container showed search, ReJSON, timeseries, bf, and vectorset loaded by default. That makes JSON documents and secondary-index queries available without extra installs, though your dataset still has to fit in memory.

Size limits and what they force

DynamoDB caps an item at 400 KB, including attribute names. I wrote a 410,000-character string to DynamoDB Local:

An error occurred (ValidationException) when calling the PutItem operation: Item size has exceeded the maximum allowed size

Other limits from the DynamoDB constraints page shape design too. Query and Scan return at most 1 MB per call, so you paginate with LastEvaluatedKey. BatchWriteItem takes up to 25 put or delete requests. A transaction can touch up to 100 unique items and 4 MB. Large blobs belong in S3 with a pointer in DynamoDB.

Redis strings can hold up to 512 MB (proto-max-bulk-len reports 536870912 on the default config), but the real limit is RAM. Set maxmemory and the behaviour depends on the eviction policy. With the default noeviction, writes fail once memory is full:

BASH
$ redis-cli CONFIG SET maxmemory 2mb
OK
$ redis-cli SET big:99 "<100 KB value>"
(error) OOM command not allowed when used memory > 'maxmemory'.

Switching to allkeys-lru made the same write succeed, and INFO stats showed evicted_keys:1. That behaviour is exactly right for a cache and exactly wrong for a primary database. DynamoDB has no equivalent problem because table size has no practical limit.

DB Pro

Work With Your Databases Like A Pro

Query, explore, and manage your databases with a beautiful desktop app and built-in AI.

Download Now
DB Pro Dashboard

Expiry: TTL means different things

Both systems support time to live, and the semantics differ enough to cause bugs.

In Redis, a key with a TTL disappears when it expires. I set a key with PX 100, slept 300 ms, and GET returned nothing while EXISTS returned 0. Rate limits and session timeouts can rely on that.

DynamoDB TTL is a background cleanup job. AWS documents that expired items are deleted "within a few days of their expiration time," that expired items still show up in reads, queries, and scans until then, and that you should filter them out with a filter expression if your application must never return expired data. TTL deletes do not consume write throughput, which makes it a cheap way to age out old data. It is not a precise expiry mechanism.

If your logic depends on "this session is invalid at 14:00:00," use Redis, or check the expiry attribute yourself on every DynamoDB read.

Transactions

Both offer transactions. Neither behaves like a SQL database.

DynamoDB's TransactWriteItems is all-or-nothing across up to 100 items. I decremented stock with a condition and wrote an order in one transaction. The first ran. The second, with stock at zero, was rejected as a unit:

An error occurred (TransactionCanceledException) when calling the TransactWriteItems operation: Transaction cancelled, please refer cancellation reasons for specific reasons [ConditionalCheckFailed, None]

A follow-up GetItem for the second order returned nothing. Transactional reads and writes cost two capacity units per operation instead of one.

Redis MULTI/EXEC runs queued commands in sequence without interleaving, but it does not roll back when one of them fails at runtime:

SET stock:sku1 10
SET name:sku1 widget
MULTI
DECR stock:sku1
INCR name:sku1
EXEC
1) (integer) 9
2) (error) ERR value is not an integer or out of range
GET stock:sku1
"9"

The decrement stuck even though the second command failed. Redis expects you to use WATCH for optimistic locking, or a Lua script or function when you need conditional logic to run atomically on the server.

Pricing model

DynamoDB bills in two modes. On-demand charges per read and write request unit, which suits spiky or unpredictable traffic. Provisioned capacity charges for the read and write capacity units you reserve per second, with auto scaling available. Storage, backups, streams, and global table replication are billed on top. One write unit covers an item up to 1 KB, and one read unit covers a strongly consistent read of up to 4 KB.

Redis cost follows memory. Self-hosted, you pay for servers sized to hold the dataset plus headroom for forks during snapshots. On ElastiCache you pay per node for node-based clusters, or for data stored and processing units on ElastiCache Serverless. Redis Cloud has its own plans. The pattern that matters: DynamoDB cost tracks request volume, and Redis cost tracks dataset size. A small, heavily read dataset favours Redis. A large dataset with modest traffic favours DynamoDB.

Running both on AWS: ElastiCache, MemoryDB, and DAX

If you are on AWS, "Redis" usually means one of three managed services, and the differences matter.

ElastiCache is the managed cache. Per the ElastiCache engine versions page, it supports Valkey (up to 9.1), Redis OSS up to 7.1, and Memcached. It does not run Redis 8, so the Redis 8 Query Engine and other bundled modules are not part of it. ElastiCache for Valkey offers its own search features, based on the open-source valkey-search project. ElastiCache version 9.0 for Valkey added optional durability through a Multi-AZ transactional log. Our AWS ElastiCache Redis guide covers setup.

MemoryDB is AWS's durable in-memory database, compatible with Valkey and Redis OSS. It stores data in memory and writes to a Multi-AZ transactional log, and AWS positions it as a primary database. If you like the Redis model and want to drop DynamoDB, this is the service to evaluate.

DAX is a cache that speaks the DynamoDB API. AWS documents it as reducing eventually consistent reads from milliseconds to microseconds, and as a poor fit for applications that need strongly consistent reads or are write-heavy. It is the least code change if your only problem is DynamoDB read latency or read cost on hot keys.

Licensing and portability

DynamoDB is an AWS service. You cannot run it elsewhere in production. DynamoDB Local (the amazon/dynamodb-local image I used for the examples here) is for development and testing only, and error messages can differ slightly from the live service.

Redis has had a turbulent licensing history. Redis 7.2 and earlier are BSD 3-Clause. In March 2024, Redis 7.4 moved to a dual RSALv2 and SSPLv1 licence, neither OSI-approved. That change prompted the Linux Foundation fork, Valkey. With the Redis 8 general availability release in 2025, Redis added AGPLv3 as a third option, which the Redis licensing page describes as returning Redis to an OSI-approved licence. Versions 7.4 to 7.8 remain RSALv2/SSPLv1 only.

For portability, Redis wins. The protocol runs on your laptop, any cloud, and several compatible servers. Our Redis alternatives roundup compares Valkey, Dragonfly, KeyDB, and others.

When to choose DynamoDB

  • The data is your system of record and loss is not acceptable.
  • You are committed to AWS and want no servers, patching, or capacity planning for storage.
  • Access patterns are known and can be expressed as key lookups and key-range queries.
  • The dataset is large or grows without a clear ceiling.
  • Traffic is spiky and on-demand pricing fits better than always-on nodes.
  • You need multi-Region replication through global tables.

When to choose Redis

  • You need a cache in front of a slower database, including DynamoDB.
  • The workload is counters, rate limits, sessions, leaderboards, queues, or pub/sub.
  • Operations on a structure (rank, pop, intersect, increment) matter more than storing records.
  • Expiry must happen on time.
  • The hot dataset fits comfortably in memory.
  • You want to run the same engine locally, on any cloud, or self-hosted.

For queues and streams, compare Redis with purpose-built tools in Redis vs Kafka and our Redis Streams guide. If you are choosing between Redis and Memcached for the cache layer, see Memcached vs Redis.

Verdict

DynamoDB and Redis only compete when you ignore durability. Put data you cannot lose in DynamoDB, or in a Redis-compatible service with a durable log such as MemoryDB. Put hot, rebuildable, structure-heavy state in Redis. When DynamoDB is too slow or too expensive for a small set of hot keys, add Redis or DAX in front of it rather than replacing it.

If you are also weighing DynamoDB against a document database, read DynamoDB vs MongoDB. If a relational model fits better, see DynamoDB vs PostgreSQL. And if Redis ends up in your stack, DB Pro works as a Redis desktop client for browsing keys and running commands.

Keep Reading