skip to content

In DynamoDB, what is the difference between an eventually consistent read and a strongly consistent read, and how does that choice change the read capacity a request consumes?

level: juniorimportance: must knowfreq 66%

answer

  1. default read is not the freshest read
  2. the flag is per request, not per table
  3. one unit, two half-price reads
  4. 4 KB is the read yardstick
  5. indexes never offer the strong option

basics

~20 s

Eventually consistent reads, the DynamoDB default, may return a slightly stale copy of an item and cost half a read unit per 4 KB. Strongly consistent reads always reflect the latest acknowledged write and cost a full read unit per 4 KB.

solid answer

~50 s

DynamoDB stores every item on several replicas across Availability Zones. By default a read is *eventually consistent*: it can be served by any replica, so it may not yet reflect a write that completed moments earlier. Passing `ConsistentRead=true` on `GetItem`, `Query`, `Scan` or `BatchGetItem` makes the read *strongly consistent*, routed so that it reflects all writes acknowledged before it started. The price difference is exactly a factor of two: one read capacity unit buys one strongly consistent read of an item up to 4 KB per second, or two eventually consistent reads of that size, and a transactional read costs two units. Strong consistency also gives up some availability and latency headroom, and is not available on a global secondary index. Default to eventual reads and reserve strong ones for the few paths that genuinely read-after-write.

code

bash · 5 lines
bash
aws dynamodb get-item \
  --table-name Orders \
  --key '{"orderId":{"S":"A-1001"}}' \
  --consistent-read \
  --return-consumed-capacity TOTAL

go deeper

for a junior

Recall that DynamoDB reads default to eventually consistent, that ConsistentRead=true makes a read strongly consistent, and that the strong read costs twice the capacity.

for a middle

Be able to do the arithmetic out loud: one read unit is one strong or two eventual reads of a 4 KB item, transactional reads double it again, and sizes round up to the next 4 KB.

for a senior

Show judgment about which specific code paths justify strong consistency, and explain the availability and latency cost as well as the capacity cost when a replica is degraded.

for a principal

Frame it as a system-wide default: eventual reads everywhere, strong consistency as an explicit, reviewed exception on named read-after-write paths, so the cost and availability profile stays predictable as the service grows.

## Why there is a choice at all DynamoDB replicates every item synchronously across multiple facilities in a Region before acknowledging a write. A write is durable once a quorum of replicas has it, but the remaining replicas converge a moment later — normally within milliseconds. That short window is the whole reason DynamoDB exposes two read modes: a read that can be answered by *any* replica is cheaper and more available, and a read that must see the newest data has to be routed more carefully. ## The two modes **Eventually consistent read** is the default for every read API. The request may be served by a replica that has not yet applied the most recent write, so a read immediately following a successful `PutItem` can return the previous value — or, on a brand-new item, no item at all. In practice the staleness window is very small, which is exactly what makes it dangerous: it will not show up in a hand test and will show up under load in production. **Strongly consistent read** is requested per call by setting `ConsistentRead` to true. DynamoDB then guarantees the response reflects all writes that received a successful response before the read began. You pay for that in three currencies: capacity (double), latency (slightly higher), and availability (a strongly consistent read can fail if the relevant replica is unreachable, where an eventual read would have succeeded from another copy). ```bash aws dynamodb get-item \ --table-name Orders \ --key '{"orderId":{"S":"A-1001"}}' \ --consistent-read \ --return-consumed-capacity TOTAL ``` `--return-consumed-capacity TOTAL` is the honest way to learn the cost of a call: the response carries the units actually consumed rather than your estimate of them. ## The capacity arithmetic One **read capacity unit** (RCU) provides, per second: - one **strongly consistent** read of an item up to 4 KB, - two **eventually consistent** reads of an item up to 4 KB, - one half of a **transactional** read of an item up to 4 KB — that is, a transactional read costs 2 RCU. Item size is rounded up to the next 4 KB boundary, so a 5 KB item costs two units strongly consistent and one unit eventually consistent. Writes have their own scale — a write capacity unit covers an item up to 1 KB, a transactional write costs 2 WCU — and there is no consistency choice on writes at all, which is a common point of confusion: *consistency is a read-side option*. The same arithmetic applies in on-demand mode, where the units are called read request units and write request units and are billed per request rather than reserved per second. For a `Query` or `Scan`, capacity is charged on the total size of the items **read**, not the items returned after a filter expression. A filtered scan that examines a megabyte and returns one item still bills for the megabyte — the single most common surprise on a DynamoDB bill. ## Where strong consistency is not available A global secondary index is maintained asynchronously from the base table, so reads from a GSI are **always** eventually consistent; `ConsistentRead=true` on a GSI query is rejected. If a read path genuinely needs read-after-write, it has to go to the base table. ## Choosing in practice Default to eventual reads. They halve the capacity bill, cut a little latency, and survive a replica hiccup. Reach for strong consistency on the narrow set of paths where a client will read back something it just wrote and a stale answer would be a visible bug — a checkout flow that immediately re-renders the order it just created, an idempotency check, a workflow step that gates on a status field another step just set. The better fix for many read-after-write cases is not to read at all: `PutItem` and `UpdateItem` accept `ReturnValues`, so an update can hand back the new item without a second round trip and without paying for a strongly consistent read. ```bash aws dynamodb update-item \ --table-name Orders \ --key '{"orderId":{"S":"A-1001"}}' \ --update-expression "SET orderStatus = :s" \ --expression-attribute-values '{":s":{"S":"SHIPPED"}}' \ --return-values ALL_NEW ``` ## Saying it well A crisp answer names the default (eventual), the flag (`ConsistentRead`), the exact 2:1 capacity ratio, and one concrete situation where you would pay for strong consistency. Adding that GSIs cannot serve strongly consistent reads, and that a transactional read costs double again, marks you as someone who has actually sized a table rather than read a summary of one.

  • Does the ConsistentRead setting affect DynamoDB writes as well as reads?
    No. Writes in DynamoDB are always applied consistently — there is no eventual-write option. `ConsistentRead` is a per-request read parameter on GetItem, BatchGetItem, Query and Scan only. The consistency question exists because reads may be served from a replica that has not yet applied the newest write; the write path itself already waits for durable acknowledgement.
  • How does a filter expression on a DynamoDB Query affect the read capacity consumed?
    Not at all in your favour. Capacity is charged on the data DynamoDB reads before the filter is applied, not on what comes back. A query that scans 200 KB and returns a single 2 KB item bills for the 200 KB. Filters are a convenience for trimming the response, never a cost or performance optimisation.
  • You need to read an item back immediately after writing it. What is the cheapest correct option?
    Usually not a read at all. PutItem and UpdateItem accept ReturnValues (for example ALL_NEW), which returns the item as written without a second request. If a separate read is unavoidable, use ConsistentRead=true against the base table and accept the doubled capacity; an eventually consistent read on that path is a latent bug.

saying these in an interview costs you the question

  • Eventually consistent means the data may be wrong for minutes
  • Strongly consistent reads cost the same as eventual ones
  • You can set ConsistentRead on a global secondary index query
  • Consistency is configured once per table, not per request
  • A filter expression reduces the capacity a query consumes

context