DynamoDB's two capacity modes solve the same problem — turning read/write throughput into a bill — with opposite failure modes. Provisioned capacity set too high pays for headroom nobody uses; set too low throttles real traffic. On-demand sidesteps the sizing question entirely, at a materially higher per-request price for the same throughput.
Provisioned capacity is a bet on a stable, known shape
Provisioned capacity is priced per hour for whatever read/write capacity units are reserved, regardless of how much of it gets used. Auto scaling helps, but it reacts to sustained utilization over a period of minutes — it doesn't eliminate the base reservation, and a table sized for a launch-day traffic estimate that never materialized just sits there billing for capacity nothing calls.
On-demand trades peace of mind for a multiplier
On-demand bills per request at a rate that's meaningfully higher than the equivalent provisioned capacity, in exchange for never having to size anything or think about throttling. For spiky, unpredictable, or low-volume tables, that trade is usually worth it. For a table with a steady, well understood request pattern, the same traffic on provisioned capacity — sized correctly — is usually the cheaper choice once auto scaling is configured with sane bounds.
The multiplier nobody accounts for: GSIs
Every Global Secondary Index maintains its own copy of whatever attributes it projects, and every write to the base table that touches a projected attribute writes to every GSI that projects it too. A table with two GSIs isn't paying for one table's worth of writes — it's paying for up to three, and that multiplier applies whether the table is provisioned or on-demand.
Every write to the base table repeats to every GSI that projects the changed attributes — one write becomes a write per index.
What to actually check
- CloudWatch's
ConsumedReadCapacityUnits/ConsumedWriteCapacityUnitsagainstProvisionedReadCapacityUnits/ProvisionedWriteCapacityUnitsshows the gap directly for provisioned tables. - For on-demand tables, Cost Explorer's DynamoDB request-unit usage type shows whether request volume is steady enough that provisioned capacity — with auto scaling — would be cheaper.
- Audit whether every GSI is still queried anywhere in the codebase; an index that was added for a feature that shipped differently is pure write multiplier with no read benefit.