Guide · AWS MemoryDB · Performance & Cost

MemoryDB Data Tiering for MCP Servers

MemoryDB data tiering on r6gd nodes extends usable cluster capacity by 2–8x by offloading cold data to NVMe SSD while keeping hot data in DRAM. For MCP server probe archives, historical uptime sorted sets, and rate-limit counter histories, data tiering provides operational Redis access to weeks of data at a fraction of DRAM-only cost. This guide covers how the tiering engine works, maxmemory-policy interactions, CloudWatch metrics to watch, the cost model, and the access patterns where tiering hurts rather than helps.

How MemoryDB data tiering works

Data tiering is an internal feature of MemoryDB on r6gd node types. The node has both DRAM and NVMe SSD storage. The tiering engine manages data placement automatically:

The tiering decision is LRU-based and transparent to application code. Your Redis client sends the same commands whether a key is in DRAM or SSD — the difference is read latency, not API behavior.

r6gd vs r7g node types

Data tiering is only available on r6gd.* node types. r7g.* nodes are DRAM-only.

Node typeArchitectureDRAMNVMe SSDTieringUse case
db.r7g.largeGraviton313.1 GBNoneNoHot data only, lowest latency
db.r7g.xlargeGraviton326.3 GBNoneNoMedium hot data, lowest latency
db.r7g.2xlargeGraviton352.6 GBNoneNoLarge hot data, lowest latency
db.r6gd.largeGraviton213.1 GB118 GB NVMeYesHot + warm/cold data, cost-optimized
db.r6gd.xlargeGraviton226.3 GB237 GB NVMeYesLarge archive, cost-optimized
db.r6gd.2xlargeGraviton252.6 GB474 GB NVMeYesVery large archive, cost-optimized
db.r6gd.4xlargeGraviton2105 GB950 GB NVMeYesEnterprise-scale probe archives

The SSD:DRAM ratio is approximately 9:1 — for every GB of DRAM, there is ~9 GB of SSD. In practice, the effective SSD capacity for customer data is somewhat lower due to internal metadata overhead, but the ratio enables significant cost savings for workloads with a hot/cold access pattern.

Enabling data tiering in CDK

const cluster = new memorydb.CfnCluster(this, 'MemoryDbCluster', {
  clusterName: 'mcp-probe-archives',
  nodeType: 'db.r6gd.xlarge',      // r6gd = data tiering enabled
  dataTiering: 'true',              // must explicitly enable; 'false' on r7g is default
  numShards: 2,
  numReplicasPerShard: 1,
  // ...other parameters...
});

dataTiering: 'true' is required even on r6gd nodes — MemoryDB will not tier automatically without the explicit flag. If you provision an r6gd node without dataTiering: 'true', only DRAM is used and the SSD capacity is wasted.

maxmemory-policy interaction with data tiering

The maxmemory-policy parameter controls what MemoryDB does when DRAM is full. With data tiering, this interaction has non-obvious consequences.

Policies to avoid with data tiering

Recommended policies with data tiering

# CDK custom parameter group for data tiering workloads
const paramGroup = new memorydb.CfnParameterGroup(this, 'DataTieringParamGroup', {
  family: 'memorydb_redis7',
  parameterGroupName: 'mcp-probe-archives-params',
  description: 'Tuned for data tiering probe archive workload',
  parameters: {
    'maxmemory-policy': 'volatile-ttl',   // protect archive keys (no TTL) from eviction
    'hz': '100',                           // faster TTL expiry for short-lived probe windows
    'lazyfree-lazy-eviction': 'yes',       // async eviction to avoid latency spikes
    'lazyfree-lazy-expire': 'yes',         // async TTL expiry
  },
});

MCP server access patterns for data tiering

Data tiering works well when the access pattern is naturally hot/cold over time — recent data is accessed frequently, older data rarely.

Good fit: probe result archives

AliveMCP stores probe results as sorted sets with Unix timestamps as scores:

// Write probe result: ZADD with timestamp score
await redis.zadd(
  `{${serverSlug}}:probes:history`,
  Date.now() / 1000,   // score = Unix timestamp
  JSON.stringify({ status: 'up', latencyMs: 42, ts: Date.now() })
);

// Query last 24 hours: ZRANGEBYSCORE (recent = DRAM tier = fast)
const recent = await redis.zrangebyscore(
  `{${serverSlug}}:probes:history`,
  Date.now() / 1000 - 86400,  // 24 hours ago
  '+inf'
);

// Query 30-day history: ZRANGEBYSCORE (old data = SSD tier = 40-100µs per key)
const monthly = await redis.zrangebyscore(
  `{${serverSlug}}:probes:history`,
  Date.now() / 1000 - 2592000,  // 30 days ago
  '+inf'
);

The last-24-hours query hits DRAM; the 30-day query promotes keys from SSD. For a monitoring dashboard that primarily shows "last hour" and "last day" views, data tiering keeps frequently queried recent data in DRAM while the historical archive stays accessible on SSD.

Poor fit: random access across all keys

If your access pattern is uniformly random across your entire dataset (e.g., random key lookup for any server at any time), the LRU tiering never converges on a stable hot set. Every access has a 50% chance of hitting cold SSD, and the constant promotion/demotion cycle causes CPU overhead and unpredictable latency.

Poor fit: write-heavy sorted set updates to cold ranges

ZADD to a member already in the SSD tier triggers a promotion (SSD read → DRAM write). If your sorted set architecture requires frequent updates to historical members (e.g., incrementing a score for an old entry), each update reads from SSD and then writes back. On a high-throughput MCP server this becomes a significant SSD read amplification bottleneck. Prefer immutable, append-only writes to the archive (new ZADD entries only, no score updates) to avoid this pattern.

Monitoring tier health

MemoryDB emits CloudWatch metrics specific to data tiering. Monitor these to understand whether your cluster is sized correctly:

CloudWatch metricWhat it measuresAlert threshold
StorageBytesUsedTotal SSD tier usage in bytesAlert at 85% of SSD capacity
DatabaseMemoryUsagePercentageDRAM usage as % of usable DRAMAlert at 75% (leaves headroom for promotions)
EngineCPUUtilizationRedis engine thread CPU %Alert at 80% — tiering promotion CPU adds overhead
GetTypeCmdsLatencyGET/HGET/ZRANGEBYSCORE p99 latency in µsBaseline on DRAM ~500µs; spike to 5,000–10,000µs indicates SSD tier thrashing
CurrItemsTotal key count across DRAM + SSDTrack rate of growth to project when SSD capacity will be exhausted
// CDK: alert on SSD tier usage approaching capacity
new cloudwatch.Alarm(this, 'MemoryDbSsdCapacityAlarm', {
  metric: new cloudwatch.Metric({
    namespace: 'AWS/MemoryDB',
    metricName: 'StorageBytesUsed',
    dimensionsMap: { ClusterName: 'mcp-probe-archives' },
    statistic: 'Maximum',
    period: cdk.Duration.minutes(15),
  }),
  // r6gd.xlarge: 237 GB SSD. Alert at 85% = ~202 GB
  threshold: 202 * 1024 * 1024 * 1024,
  evaluationPeriods: 2,
  alarmDescription: 'MemoryDB SSD tier >85% full — scale up or add shards',
  treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
});

// Alert on elevated GET latency (SSD tier thrashing indicator)
new cloudwatch.Alarm(this, 'MemoryDbGetLatencyAlarm', {
  metric: new cloudwatch.Metric({
    namespace: 'AWS/MemoryDB',
    metricName: 'GetTypeCmdsLatency',
    dimensionsMap: { ClusterName: 'mcp-probe-archives' },
    statistic: 'p99',
    period: cdk.Duration.minutes(5),
  }),
  threshold: 5000,   // 5ms p99 — 10x above healthy DRAM baseline
  evaluationPeriods: 3,
  alarmDescription: 'MemoryDB GET p99 >5ms — SSD tier thrashing or DRAM pressure',
  treatMissingData: cloudwatch.TreatMissingData.NOT_BREACHING,
});

Cost model: r6gd vs r7g

Data tiering is not always cheaper. The cost comparison depends on your working set size and total data volume:

Scenarior7g configurationr6gd configurationCost comparison
50 GB working set, 50 GB total2x r7g.2xlarge (52.6 GB DRAM)2x r6gd.large (13 GB DRAM + 118 GB SSD)r7g ~20% cheaper — all data fits in DRAM, no tiering benefit
20 GB working set, 200 GB total4x r7g.2xlarge (210 GB DRAM)2x r6gd.xlarge (26 GB DRAM + 237 GB SSD)r6gd ~60% cheaper — hot set fits DRAM, cold data in SSD
5 GB working set, 500 GB total10x r7g.2xlarge (526 GB DRAM)3x r6gd.2xlarge (158 GB DRAM + 1.4 TB SSD)r6gd ~75% cheaper — extreme ratio of cold to hot

The break-even point is roughly when total data is 3–4x larger than your working set. Below that ratio, DRAM-only (r7g) is cheaper per GB of usable storage and delivers lower, more predictable latency.

Sizing for the working set

The DRAM tier must be large enough to hold your working set — the keys accessed within a typical request burst window. For AliveMCP's probe archive pattern:

Size DRAM to hold the working set with 25% headroom. Size SSD to hold the full archive plus the same headroom. If both fit in DRAM without the SSD multiple, use r7g for better latency characteristics.

How AliveMCP monitors data-tiering MemoryDB clusters

When your MCP server uses a data-tiering MemoryDB cluster for probe archives or session history, AliveMCP monitors the tiering health as part of the infrastructure stack:

Data-tiering monitoring is available on the Team tier alongside the standard MCP endpoint health probes.