RDS, Aurora, and ElastiCache in AWS: Complete Developer Guide
Aug 14, 2026
RDS, Aurora, and ElastiCache in AWS: Complete Developer Guide
Introduction
When we build an application, we usually need a database. In AWS, you can run a database manually on an EC2 instance, but then you are responsible for installing the database, patching the OS, taking backups, scaling storage, monitoring, and handling failures. This is possible, but it becomes extra work very quickly.
This is where managed database services come in. Amazon RDS gives us managed relational databases, Amazon Aurora gives us a cloud-optimized relational database engine, and Amazon ElastiCache gives us managed in-memory caching with Redis or Memcached. These services are very important for AWS Developer because they appear in questions about scalability, high availability, failover, performance, caching, and database connections.
Prerequisites
Before reading this blog, you should know the basic idea of SQL databases, Availability Zones, VPC networking, security groups, and why applications need both reads and writes. You do not need to be a database expert, but you should understand that reads mean fetching data, and writes mean creating, updating, or deleting data.
Amazon RDS Overview
RDS stands for Relational Database Service. It is a managed database service for databases that use SQL as the query language. Instead of manually installing a database on an EC2 server, AWS lets you create and run relational databases directly through RDS.
RDS supports common database engines like PostgreSQL, MySQL, MariaDB, Oracle, Microsoft SQL Server, IBM Db2, and Aurora. Aurora is also shown together with RDS because it works like a relational database engine, but it is AWS proprietary.
The important thing is this: RDS manages a lot of database operations for you. You still choose the database engine and configure your application, but AWS handles many operational tasks that would normally take time.
Why Use RDS Instead Of A Database On EC2?
If you install a database on EC2, you control the server fully, but you also manage everything. With RDS, AWS manages the database infrastructure. RDS gives automated provisioning, OS patching, backups, monitoring dashboards, read replicas, Multi-AZ deployment, maintenance windows, and scaling capability.
RDS also supports continuous backups and point-in-time restore. This means you can restore the database to a specific timestamp, which is very useful if bad data is inserted or something goes wrong in production.
RDS storage is backed by EBS. You can scale vertically by using a bigger instance and horizontally in read-heavy workloads by using read replicas. The tradeoff is that you cannot SSH into the RDS database instance. Because it is managed by AWS, you do not get direct server access like EC2.
RDS Storage Auto Scaling
RDS Storage Auto Scaling helps increase database storage automatically when the database is running out of free space. This is useful when the workload is unpredictable and you do not know exactly how much storage you will need in the future.
You need to set a maximum storage threshold. This is the maximum limit up to which RDS can increase storage. RDS automatically modifies storage when free storage is less than 10 percent of allocated storage, the low-storage condition lasts at least 5 minutes, and 6 hours have passed since the last storage modification.
This helps avoid manual storage scaling. Instead of waiting for the database to become full and then increasing storage yourself, RDS can handle it dynamically. Storage Auto Scaling supports all RDS database engines.
RDS Read Replicas
Read Replicas are used for read scalability. In a normal application, one database receives writes and reads. But if read traffic becomes too high, the main database can become overloaded. With read replicas, the main RDS database can replicate data to separate replica databases, and the application can send read queries to those replicas.
RDS supports up to 15 read replicas. These replicas can be in the same Availability Zone, across Availability Zones, or across Regions. Replication is asynchronous, so reads from replicas are eventually consistent. This means the replica may be slightly behind the main database.
Read replicas can also be promoted to their own standalone database. This is useful when you want to split a workload or recover from certain situations. But your application must update the connection string to use read replicas. AWS will not automatically send your read queries there unless your application is configured for it.
Read Replica Use Case
Imagine you have a production database that is handling normal application traffic. Now you want to run a reporting application to perform analytics. If that reporting application sends heavy SELECT queries to the production database, it can slow down the main application.
The better approach is to create a read replica and run the reporting workload on the replica. The production application remains unaffected because reporting queries are separated from the main database.
Read replicas are only for read operations like SELECT statements. They are not used for INSERT, UPDATE, or DELETE statements. Writes still go to the main database.
Read Replicas And Network Cost
In AWS, data transfer between Availability Zones can have a network cost. But for RDS read replicas within the same Region, AWS does not charge that cross-AZ replication fee. This means a read replica in another AZ but still in the same Region can be used without paying that specific replication network cost.
Cross-Region replication is different. If the RDS database is in one Region and the read replica is in another Region, then data is crossing Regions, and that can create network cost.
RDS Multi-AZ
RDS Multi-AZ is mainly for disaster recovery and high availability. It is not used for scaling reads. In a Multi-AZ setup, RDS creates a standby database in another Availability Zone and keeps it synchronized with the primary database.
The replication is synchronous. The application uses one DNS name, and if the primary database fails, RDS automatically fails over to the standby database. This helps when there is an Availability Zone failure, network failure, instance failure, or storage failure.
The important point is that Multi-AZ gives availability, not read scaling. The standby database is not used by the application for normal reads. It is there for failover.
Moving From Single-AZ To Multi-AZ
You can convert an RDS database from Single-AZ to Multi-AZ with zero downtime. You do not need to stop the database. You modify the database setting, and AWS handles the internal work.
Internally, AWS takes a snapshot of the database, restores a new standby database from the snapshot in a different Availability Zone, and then establishes synchronization between the primary database and the standby database.
This is one reason managed services are powerful. You get a production-grade availability setup without manually building replication between servers.
Amazon Aurora
Aurora is an AWS proprietary database technology. It is not open source, but it is compatible with PostgreSQL and MySQL. That means if your application already uses PostgreSQL or MySQL drivers, it can usually work with Aurora without major driver changes.
Aurora is optimized for the AWS cloud. It claims around 5x performance improvement over MySQL on RDS and more than 3x the performance of PostgreSQL on RDS. Aurora storage automatically grows in increments of 10 GB and can scale very large, up to 128 TB in the slide architecture and up to 256 TB in the Aurora overview.
Aurora can have up to 15 replicas, and replication is faster than normal MySQL replication, with replica lag usually under 10 milliseconds. Failover is very fast because Aurora is designed with high availability as a native feature. Aurora usually costs more than standard RDS, around 20 percent more, but it can be more efficient for demanding workloads.
Aurora High Availability And Read Scaling
Aurora stores 6 copies of your data across 3 Availability Zones. For writes, Aurora needs 4 copies out of 6. For reads, Aurora needs 3 copies out of 6. This design gives Aurora strong availability and durability.
Aurora storage is self-healing with peer-to-peer replication. The storage is also striped across hundreds of volumes. This means the storage layer is distributed and can repair itself when there is a failure.
In an Aurora cluster, one Aurora instance handles writes. This is the writer or master instance. The writer endpoint points to this master instance. Aurora can also have up to 15 read replicas, and these replicas serve read traffic. The reader endpoint provides connection load balancing across the read replicas.
Aurora supports automated failover for the master in less than 30 seconds. It also supports cross-Region replication.
Aurora Features
Aurora includes automatic failover, backup and recovery, isolation and security, industry compliance, push-button scaling, automated patching with zero downtime, advanced monitoring, and routine maintenance.
Aurora also has a feature called Backtrack. Backtrack lets you restore data to a previous point in time without using backups. This can be useful when you want to undo a mistake quickly without doing a full restore from backup.
RDS And Aurora Security
RDS and Aurora support encryption at rest using AWS KMS. The database master and replicas can be encrypted, but encryption must be defined at launch time. If the master database is not encrypted, then the read replicas cannot be encrypted.
If you already have an unencrypted database and you want to encrypt it, the process is to create a DB snapshot and restore that snapshot as an encrypted database. You cannot just turn on encryption directly on the existing unencrypted database.
For encryption in flight, RDS and Aurora are TLS-ready by default. The application should use AWS TLS root certificates on the client side. IAM Authentication is also supported, which means applications can use IAM roles to connect to the database instead of only using a username and password.
Security groups control network access to RDS and Aurora databases. There is no SSH access to these databases, except for RDS Custom. Audit logs can be enabled and sent to CloudWatch Logs for longer retention.
Amazon RDS Proxy
Amazon RDS Proxy is a fully managed database proxy for RDS and Aurora. It allows applications to pool and share database connections. This is important because opening too many database connections can put stress on CPU, memory, and database resources.
RDS Proxy improves database efficiency by reducing the number of open connections and minimizing connection timeouts. It is serverless, auto scaling, highly available, and Multi-AZ. It can also reduce RDS and Aurora failover time by up to 66 percent.
RDS Proxy supports RDS MySQL, PostgreSQL, MariaDB, and Microsoft SQL Server. It also supports Aurora MySQL and Aurora PostgreSQL. For most applications, no code changes are required.
RDS Proxy can enforce IAM Authentication and securely store credentials in AWS Secrets Manager. One important security point is that RDS Proxy is never publicly accessible. It must be accessed from inside a VPC.
Amazon ElastiCache Overview
The same way RDS gives managed relational databases, ElastiCache gives managed Redis or Memcached. ElastiCache is used for in-memory databases, which means data is stored in memory for very fast performance and low latency.
ElastiCache helps reduce load from databases for read-intensive workloads. Instead of reading the same data from RDS again and again, the application can read it from the cache. This makes the application faster and reduces pressure on the main database.
ElastiCache can also help make applications stateless. For example, instead of storing user session data inside one application server, you can store the session in ElastiCache. Then any application instance can retrieve the session.
AWS handles OS maintenance, patching, optimizations, setup, configuration, monitoring, failure recovery, and backups for ElastiCache. But using ElastiCache usually requires heavy application code changes because the application must know how to read from and write to the cache.
ElastiCache Database Cache Architecture
In a database cache architecture, the application first checks ElastiCache. If the data exists in the cache, it is a cache hit, and the application returns the data quickly. If the data does not exist in the cache, it is a cache miss. The application reads the data from RDS, writes it into ElastiCache, and then returns the result.
This helps relieve load on RDS. But the cache must have an invalidation strategy. If the database changes but the cache still has old data, users may receive stale data. So caching is not just about speed; it is also about correctness.
ElastiCache User Session Store
Another common architecture is a user session store. A user logs in through one application instance. That application writes the session data into ElastiCache. Later, if the user request goes to another application instance, that second instance can retrieve the session from ElastiCache, and the user remains logged in.
This is how ElastiCache helps applications become stateless. The application instances do not need to keep local session state. The session is stored in a shared cache layer.
Redis Vs Memcached
ElastiCache supports Redis and Memcached, but they are not the same.
Redis supports Multi-AZ with auto failover. It supports read replicas, so it can scale reads and provide high availability. Redis also supports data durability using AOF persistence, backup and restore features, sets, and sorted sets.
Memcached supports multi-node partitioning of data, also called sharding. It does not provide high availability through replication in the same way Redis does. It is non-persistent, supports backup and restore for serverless, and uses a multi-threaded architecture.
A simple way to remember it is this: choose Redis when you need richer data structures, replication, high availability, and durability options. Choose Memcached when you need a simpler, multi-threaded, sharded cache and persistence is not important.
Caching Implementation Considerations
Before adding caching, you need to ask if the data is safe to cache. Cached data may become out of date because caches are often eventually consistent with the database. If your application cannot tolerate stale data, you need a careful invalidation strategy or maybe caching is not the right fit.
Caching works best when data changes slowly and a small number of keys are requested frequently. For example, user profiles, blog posts, configuration values, leaderboards, comments, and activity streams can be good candidates depending on the application.
Caching is not effective when data changes rapidly or when the application frequently needs a very large key space. If almost every request asks for different data, the cache may not help much because there will be too many cache misses.
You also need to think about data structure. Some data works well as simple key-value caching. Some data works better as cached aggregation results. After that, choose the right caching design pattern.
Lazy Loading, Cache-Aside, Or Lazy Population
Lazy loading, cache-aside, and lazy population describe the same common caching pattern. The application checks the cache first. If the value is present, it uses the cached value. If the value is missing, it reads from the database, stores the result in the cache, and then returns the data.
Here is a simple Python-style example:
def get_user(user_id):
cache_key = f"user:{user_id}"
user = cache.get(cache_key)
if user is not None:
return user
user = database.get_user(user_id)
cache.set(cache_key, user, ttl=3600)
return userThe main benefit is that only requested data is cached. The cache is not filled with unused data. Also, node failures are not fatal. If the cache node fails, the application can still go to the database, but the first requests will be slower while the cache warms up again.
The downside is the cache miss penalty. A miss can require multiple round trips: check the cache, read from the database, and write back to the cache. Another downside is stale data. The database can be updated while the cache still contains the old value.
Write-Through Caching
In write-through caching, the application updates the database and the cache when data changes. So when a user is updated, the application writes to the database first and then writes the updated value to the cache.
Here is a simple Python-style example:
def update_user(user_id, changes):
user = database.update_user(user_id, changes)
cache_key = f"user:{user_id}"
cache.set(cache_key, user, ttl=3600)
return userThe benefit is that data in the cache is less likely to be stale, and reads are quick because the updated value is already in the cache. The tradeoff is write penalty. Every write now needs two calls: one to the database and one to the cache.
Write-through also has a problem with missing data. If a value has never been added or updated, it may not exist in the cache yet. A common mitigation is to combine write-through with lazy loading. Another downside is cache churn, because you may cache data that is written often but never actually read.
Cache Evictions And TTL
Cache eviction means removing an item from the cache. This can happen in three ways. You can delete the item explicitly, the item can be evicted because memory is full and it was not recently used, or the item can expire because you set a time-to-live.
TTL means time-to-live. It defines how long an item should stay in the cache before it expires automatically. TTL can be a few seconds, hours, or days depending on the application.
TTL is useful for many types of data, such as leaderboards, comments, and activity streams. Usually setting a TTL is a good idea because it gives cached data a natural expiry time. But when using write-through, TTL needs careful thought because the cache is being actively updated when the database changes.
If too many evictions happen because memory is full, then the cache is too small for the workload. In that case, you should scale up or scale out.
Practical Caching Advice
Lazy loading or cache-aside is easy to implement and works well as a foundation, especially for read-heavy workloads. It is usually the first caching pattern developers use because it is simple and does not require writing every possible value into the cache early.
Write-through is usually combined with lazy loading. Use write-through for the specific queries or workloads that benefit from having fresh cached data immediately after a write.
Only cache data that makes sense. User profiles, blog data, repeated aggregation results, comments, leaderboards, and activity streams can be useful cache candidates. Data that changes too often or is rarely requested may not be worth caching.
Cache invalidation is one of the hardest parts of system design. The difficult part is not storing data in the cache. The difficult part is knowing when cached data is no longer correct.
Amazon MemoryDB For Redis
Amazon MemoryDB for Redis is a Redis-compatible, durable, in-memory database service. It is different from using Redis only as a cache because MemoryDB is designed as a durable database with in-memory speed.
MemoryDB provides ultra-fast performance with over 160 million requests per second. It stores data in memory across many nodes for fast performance, and it uses a Multi-AZ transactional log for durability and fast recovery.
MemoryDB can scale from tens of GBs to hundreds of TBs of storage. Common use cases include web applications, mobile applications, online gaming, media streaming, microservices, retail, banking, finance, media, and entertainment.
The important idea is this: ElastiCache is usually used as a cache layer, while MemoryDB for Redis can be used when you want Redis-compatible speed with durable in-memory data storage.
Best Practices
- Use RDS when you want a managed relational database and do not want to manage the database server yourself.
- Use RDS Read Replicas for read scaling, reporting workloads, and read-heavy traffic.
- Use RDS Multi-AZ for high availability and disaster recovery, not read scaling.
- Use Aurora when you need higher performance, faster replication, fast failover, and cloud-optimized relational database behavior.
- Use RDS Proxy when your application opens many database connections, especially with serverless workloads like Lambda.
- Encrypt RDS and Aurora at launch time if encryption is required.
- Use security groups to strictly control database network access.
- Use ElastiCache for read-heavy workloads, session storage, and low-latency cached data.
- Choose Redis when you need high availability, replicas, richer data structures, backups, and durability options.
- Choose Memcached when you need a simple multi-threaded cache with sharding and no persistence requirement.
- Always design a cache invalidation strategy before relying on cached data.
- Set TTL values based on how fresh the data must be and how expensive the database query is.
Common Pitfalls
- Confusing Read Replicas with Multi-AZ. Read Replicas are for read scaling. Multi-AZ is for failover and disaster recovery.
- Expecting read replicas to be strongly consistent. RDS read replicas use asynchronous replication, so they are eventually consistent.
- Forgetting to update the application connection string to use read replicas.
- Assuming cross-Region replication is free. Same-Region read replica replication has a special cost behavior, but cross-Region traffic can cost money.
- Trying to SSH into RDS or Aurora. These are managed services, so SSH is not available except with RDS Custom.
- Creating an unencrypted database and expecting to turn on encryption directly later. You need snapshot and restore.
- Using ElastiCache without changing application code. The application must be designed to use the cache.
- Caching rapidly changing data without a proper invalidation strategy.
- Setting cache memory too low and then seeing too many evictions.
Summary
RDS, Aurora, and ElastiCache solve different but connected problems. RDS gives managed SQL databases. Aurora gives a cloud-optimized relational database with strong high availability, read scaling, and fast failover. ElastiCache gives fast in-memory caching with Redis or Memcached.
For real projects, the most important thing is knowing when to use each service. Use Read Replicas when reads are too heavy. Use Multi-AZ when availability matters. Use Aurora when you need better performance and fast failover. Use RDS Proxy when connection management becomes a problem. Use ElastiCache when repeated reads, sessions, or expensive queries need low latency.
Caching can make an application very fast, but it also creates the problem of stale data. So the best cache is not the one that stores everything. The best cache stores the right data, expires it at the right time, and keeps the database load lower without breaking correctness.