---
title: "12 — ElastiCache: Putting Memory Between the Database and the App"
uid: elasticache-caching
tags: ["aws", "roadmap:aws", "memcached", "redis", "caching", "performance", "elasticache"]
excerpt: "ElastiCache is an in-memory layer that absorbs the reads your database shouldn't serve. The engine choice — Redis or Memcached — decides whether you get a cache or a fast data structure server."
date: 2026-08-13T03:28:29+0000
source: https://www.aveshina.my.id/en/blog/elasticache-caching
---

"Managed Redis, for when the database is slow" was my entire ElastiCache story, and it mixed up two different jobs. The split that landed: **ElastiCache is a managed in-memory layer I put between the application and the database to absorb the reads the database has no business serving, and the engine choice — Redis or Memcached — decides whether I get a cache or something closer to a fast data structure server** [1]. Once I separated those two uses, picking the engine and sizing the cluster stopped being guesswork.

## Why an in-memory layer exists at all

The problem ElastiCache solves is straightforward: disk-based databases are fast, but memory is faster — microseconds versus milliseconds. The workloads that hurt are the ones hitting the same rows thousands of times a second (a hot config value, a session, a leaderboard), each paying the disk round-trip when the data barely changes. A cache absorbs those reads; the database handles writes and the occasional cold read. The win is latency on the hot path and load off the primary.

The trade-off is the usual one: memory is more expensive per GB than disk, so the cache only earns its keep on data that's read far more than it's written. Cache everything, and the bill outpaces the benefit; cache nothing, and the database groans.

## The two engines

ElastiCache supports two open-source engines, and they sit at genuinely different spots [1]:

- **Memcached** — a simple, multi-threaded key-value cache. Strings in, strings out, with a TTL. No persistence, no replication, no data structures. It is *just* a cache, and for pure caching workloads it's simple and fast.
- **Redis** — a single-threaded (mostly) in-memory data store with strings, lists, sets, sorted sets, hashes, pub/sub, persistence, replication, and clustering. It's a cache *and* a session store *and* a leaderboard engine *and* a lightweight queue.

The practical rule I use: **reach for Redis unless there's a specific reason not to.** Memcached's simplicity is real, but Redis's extra features (persistence, replication, atomic operations on data structures) cover almost every caching-adjacent need without giving up much raw throughput. Most new projects default to Redis, and the roadmap's coverage of Memcached is mostly for completeness.

## The two jobs Redis does

Beyond "make reads faster," Redis-on-ElastiCache shows up in two specific shapes:

- **Database read cache** — the app checks Redis first; on a hit it returns, on a miss it queries the database and back-fills. Reduces load on RDS/DynamoDB for read-heavy patterns.
- **Session store** — user sessions live in Redis because it's fast, can be replicated across AZs, and survives instance restarts better than local memory. Multiple app instances share one session store, which is the enabling trick for stateless, horizontally-scaled web tiers.

The cache-aside pattern is the most common:

```figure
<svg viewBox="0 0 680 220" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Cache-aside pattern. An app receives a read request. Step 1: it asks ElastiCache. Step 2a (HIT): return immediately. Step 2b (MISS): query the RDS database, then step 3: back-fill the cache, then return. Redis and Memcached badges sit on the cache node.">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">
    <defs><marker id="eca" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto"><path d="M0,0 L10,5 L0,10 z" fill="#64748b"/></marker></defs>

    <!-- app -->
    <rect x="30" y="80" width="110" height="60" rx="8" fill="#e0e7ff" stroke="#6366f1"/>
    <text x="85" y="105" font-size="11" font-weight="700" fill="#1e1b4b" text-anchor="middle">Application</text>
    <text x="85" y="122" font-size="9" fill="#1e1b4b" text-anchor="middle">read request</text>

    <!-- cache -->
    <rect x="240" y="40" width="160" height="140" rx="8" fill="#dcfce7" stroke="#16a34a"/>
    <text x="320" y="62" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">ElastiCache</text>
    <text x="320" y="82" font-size="10" fill="#052e16" text-anchor="middle">Redis · Memcached</text>
    <rect x="260" y="100" width="120" height="30" rx="5" fill="#fef9c3" stroke="#ca8a04"/><text x="320" y="120" font-size="9" font-weight="700" fill="#422006" text-anchor="middle">HIT → return now</text>
    <rect x="260" y="138" width="120" height="30" rx="5" fill="#fee2e2" stroke="#dc2626"/><text x="320" y="158" font-size="9" font-weight="700" fill="#7f1d1d" text-anchor="middle">MISS → ask DB</text>

    <!-- db -->
    <rect x="500" y="80" width="140" height="60" rx="8" fill="#fce7f3" stroke="#db2777"/>
    <text x="570" y="105" font-size="11" font-weight="700" fill="#500724" text-anchor="middle">RDS / DynamoDB</text>
    <text x="570" y="122" font-size="9" fill="#500724" text-anchor="middle">source of truth</text>

    <!-- arrows -->
    <path d="M140,100 L240,75" stroke="#64748b" stroke-width="1.3" marker-end="url(#eca)"/>
    <text x="180" y="78" font-size="9" fill="#475569" text-anchor="middle">1. ask</text>
    <path d="M380,115 L500,100" stroke="#dc2626" stroke-width="1.3" stroke-dasharray="4 3" marker-end="url(#eca)"/>
    <text x="440" y="100" font-size="9" fill="#7f1d1d" text-anchor="middle">2b. on MISS</text>
    <path d="M500,130 L380,160" stroke="#16a34a" stroke-width="1.3" marker-end="url(#eca)"/>
    <text x="440" y="160" font-size="9" fill="#052e16" text-anchor="middle">3. back-fill</text>
  </g>
</svg>
```

## Quotas and the operational shape

ElastiCache has account-level **quotas** on clusters, nodes, parameter groups, and subnet groups, varying by region and raisable via support [2]. The practical operational concern is **eviction policy** — when the cache is full, what gets dropped? Redis options include noeviction (writes fail when full), allkeys-lru (drop least-recently-used, the usual cache choice), and volatile-lru (drop least-recently-used among keys with a TTL). The right policy depends on whether the cache is allowed to lose data; for a pure read cache, allkeys-lru is almost always correct.

The other decision is **node size and replication**. Redis supports cluster mode with sharding and replicas across AZs, which is what makes it usable as a session store that survives a node loss. Memcached scales horizontally too but without replication — a node loss there is a cache miss storm until the data is reloaded.

## How I use this

ElastiCache earns its line in the architecture when the same data is read many times more than it's written. My standing patterns: a Redis read cache in front of RDS for hot read-heavy queries, with allkeys-lru and a TTL that matches how stale the app can tolerate; Redis as the session store for any horizontally-scaled web tier (so any app instance can serve any user); and Memcached essentially never, because Redis covers its use cases and adds replication. The discipline is measurement, not vibes — I add the cache only after I can name the query that's slow and confirm the read/write ratio favors caching. Adding a cache to a write-heavy or random-read workload just adds a failure surface and a bill without removing load from the database.

## References

[1] Amazon Web Services, "What is Amazon ElastiCache?," ElastiCache User Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/WhatIs.html](https://docs.aws.amazon.com/AmazonElastiCache/latest/dg/WhatIs.html)

[2] Amazon Web Services, "ElastiCache quotas," ElastiCache Management Guide, 2024. [Online]. Available: [https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/quota-limits.html](https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/quota-limits.html)

```quiz
Q: Why put an in-memory cache like ElastiCache in front of a database?
- Memory is faster than disk, so hot-path reads skip the database round-trip and reduce its load
- It increases the durability of the data
correct: 0
explain: A cache absorbs reads the database would otherwise serve, lowering latency and taking load off the primary. It does not add durability — caches are volatile by design.

Q: Redis is generally preferred over Memcached for new projects because…
- Redis adds persistence, replication, and data structures (lists, sets, sorted sets) on top of caching
- Redis is always faster at every workload
correct: 0
explain: Redis covers Memcached's caching use and adds features — sessions, leaderboards, pub/sub, atomic ops — with little throughput penalty. Memcached stays relevant for simple, pure-cache workloads.

Q: In the cache-aside pattern, what happens on a cache MISS?
- The app returns an error to the user
- The app queries the database, back-fills the cache, then returns the data
correct: 1
explain: On a miss the app falls through to the source of truth, writes the result back into the cache, and returns. The next read of the same key is a hit.

Q: Which eviction policy is usually right for a pure read cache?
- allkeys-lru (drop least-recently-used when full)
- noeviction (writes fail when full)
correct: 0
explain: A read cache is allowed to lose data. allkeys-lru keeps the hottest keys and drops the coldest, which is the desired behavior. noeviction is for when data loss isn't acceptable.

Q: Redis works well as a shared session store for a horizontally-scaled web tier because…
- it can be replicated across AZs and accessed by every app instance
- it stores sessions on each app instance's local disk
correct: 0
explain: A shared Redis cluster lets any app instance read any user's session, which is what enables stateless, elastically-scaled web servers.
```
