Databases¶
What You'll Learn¶
- The differences between RDS and Aurora, and how Multi-AZ and read replicas differ
- How backups, snapshots, and point-in-time recovery work — and how to restore
- How to tune, secure, upgrade, and connect to relational databases safely
- When DynamoDB or ElastiCache is the better choice, and how to design for them
What "Managed" Means¶
| AWS handles | You still handle |
|---|---|
| Hardware, OS, and database engine installation | Schema design, indexes, and query performance |
| Automated backups and patching windows | Choosing instance size, storage, and Multi-AZ |
| Replication and failover mechanics | Testing restores and failover |
| Encryption at rest with KMS | Network access, credentials, and IAM |
| Monitoring metrics | Alerting on them, and capacity planning |
| Major version availability | Planning and testing major version upgrades |
RDS vs Aurora¶
| Amazon RDS | Amazon Aurora | |
|---|---|---|
| Engines | PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, Db2 | PostgreSQL- and MySQL-compatible |
| Storage | EBS volumes attached to the instance | Distributed storage replicated six ways across three AZs, grows automatically |
| Read replicas | Up to 15, asynchronous | Up to 15 in the cluster, sharing storage with typically very low lag |
| Failover | Multi-AZ standby, usually a minute or two | Promote a replica, typically faster |
| Serverless option | No | Aurora Serverless v2 scales capacity in fine-grained steps |
| Global | Cross-region read replicas | Aurora Global Database with fast cross-region replication and managed failover |
| Cost | Lower for small, steady workloads | Higher baseline; I/O-Optimized configuration for I/O-heavy workloads |
Start with RDS PostgreSQL for small services. Choose Aurora when you need fast failover, many readers, very large storage, global databases, or Serverless v2.
Multi-AZ vs Read Replicas¶
These solve different problems and are often used together:
flowchart LR
subgraph HA["Multi-AZ: availability"]
P["Primary<br/>AZ a"] -->|"synchronous"| S["Standby<br/>AZ b<br/>(no reads in classic Multi-AZ)"]
end
subgraph RR["Read replicas: read scaling"]
P2["Primary"] -->|"asynchronous"| R1["Replica 1"]
P2 -->|"asynchronous"| R2["Replica 2<br/>(another region)"]
end
| Multi-AZ | Read replica | |
|---|---|---|
| Purpose | Survive an instance or AZ failure | Offload reads; cross-region DR |
| Replication | Synchronous | Asynchronous — replicas lag |
| Serves traffic | Standby doesn't (a Multi-AZ DB cluster deployment has two readable standbys) | Yes, reads only |
| Failover | Automatic; the endpoint DNS name moves to the standby | Manual promotion (automatic within an Aurora cluster) |
Applications must reconnect after failover. Use the cluster or instance endpoint name, keep DNS TTL caching short in the client, and make connection pools retry.
Backups and Recovery¶
| Mechanism | What it gives you |
|---|---|
| Automated backups | Daily snapshots plus transaction logs, retained 1–35 days, enabling point-in-time recovery to any second in the window |
| Manual snapshots | Kept until you delete them; copy to other regions or accounts |
| AWS Backup | Central policies, cross-account and cross-region copies, and vault lock for immutability |
Restores always create a new database instance or cluster:
aws rds restore-db-instance-to-point-in-time \
--source-db-instance-identifier orders-prod \
--target-db-instance-identifier orders-restore-20260914-1015 \
--restore-time 2026-09-14T10:15:00Z \
--db-subnet-group-name db-private \
--vpc-security-group-ids sg-0db1234567890abcd
Then verify the data, and either point the application at the new endpoint or copy the recovered rows back. Test restores on a schedule — record how long they take, because that's your real recovery time.
Deletion protection
Enable deletion_protection on every production database, and set skip_final_snapshot = false in Terraform. A mistaken terraform destroy shouldn't be able to remove production data.
Configuration and Operations¶
Parameter groups¶
Engine settings live in parameter groups, not config files:
resource "aws_db_parameter_group" "orders_pg16" {
name = "orders-pg16"
family = "postgres16"
parameter {
name = "log_min_duration_statement"
value = "500" # log queries slower than 500 ms
}
parameter {
name = "rds.force_ssl"
value = "1"
}
parameter {
name = "shared_preload_libraries"
value = "pg_stat_statements"
apply_method = "pending-reboot"
}
}
Static parameters need a reboot to apply. Always use a custom parameter group — you can't modify the default one later.
Monitoring¶
| Tool | Shows |
|---|---|
| CloudWatch metrics | CPU, FreeableMemory, FreeStorageSpace, DatabaseConnections, ReadLatency, ReplicaLag |
| Enhanced Monitoring | OS-level metrics per process, at up to 1-second granularity |
| Performance Insights / Database Insights | Database load by wait event, SQL statement, host, and user |
| Logs to CloudWatch | PostgreSQL logs, slow query logs, error logs |
Alert at least on storage space, CPU, memory, connection count near max_connections, replica lag, and failed backups.
Connections and RDS Proxy¶
Serverless functions and autoscaled containers can open more connections than a database can handle. RDS Proxy pools and multiplexes connections, speeds up failover for clients, and can authenticate with IAM while holding database credentials in Secrets Manager.
Credentials¶
- Let RDS manage the master user password in Secrets Manager (
manage_master_user_password = true), with automatic rotation. - Give applications their own least-privilege database users, not the master user.
- IAM database authentication lets roles generate short-lived connection tokens instead of passwords.
Upgrades¶
Minor versions can be applied automatically in the maintenance window. Major version upgrades (for example, PostgreSQL 16 to 17) change behavior: test them on a restored snapshot first, and use blue/green deployments, which create a synchronized copy running the new version and switch over with minimal downtime.
DynamoDB¶
A fully managed key-value and document database that scales to very high request rates with single-digit-millisecond latency — if the data model fits.
Design around access patterns¶
Unlike relational databases, you design the table from the queries you need:
| Access pattern | Key design |
|---|---|
| Get an order by ID | Partition key PK = ORDER#<id> |
| List a customer's orders, newest first | PK = CUSTOMER#<id>, sort key SK = ORDER#<timestamp> |
| Find orders by status | Global secondary index on status and created_at |
aws dynamodb query --table-name orders \
--key-condition-expression "PK = :c AND begins_with(SK, :o)" \
--expression-attribute-values '{":c":{"S":"CUSTOMER#8812"},":o":{"S":"ORDER#2026-09"}}' \
--scan-index-forward false --limit 20
- Avoid
Scanin application paths — it reads the whole table. - Choose high-cardinality partition keys so load spreads evenly and no "hot partition" throttles.
Capacity and protection¶
| Setting | Recommendation |
|---|---|
| Capacity mode | On-demand for unpredictable or spiky traffic; provisioned with auto scaling for steady, high traffic |
| Point-in-time recovery | Enable for every production table |
| Deletion protection | Enable |
| TTL | Expire temporary items (sessions, idempotency keys) automatically |
| Streams | React to changes with Lambda or replicate data |
ElastiCache¶
ElastiCache runs Valkey, Redis OSS, or Memcached for caching, sessions, rate limiting, leaderboards, and queues.
- Use cluster mode with replicas across AZs and automatic failover for production.
- Enable encryption in transit and authentication.
- Set a
maxmemory-policy(such asallkeys-lrufor pure caches), and alert on evictions, memory usage, and CPU. - Design the application to survive a cache miss or a cold cache after failover — a cache is not a database.
Common Mistakes¶
- Treating read replicas as a high-availability solution, or Multi-AZ as a way to scale reads.
- Never testing a restore, then discovering the real recovery time during an incident.
- No deletion protection on production databases managed by Terraform.
- Serverless or autoscaled applications exhausting
max_connectionswithout RDS Proxy or pooling. - Applications that cache the database IP address and never reconnect after failover.
- Major version upgrades performed in place on production without testing.
- Modeling DynamoDB like a relational schema and relying on
Scan.
Interview Questions¶
- What's the difference between Multi-AZ and a read replica in RDS?
- Walk through restoring an RDS database to a point in time before a bad migration.
- When would you choose Aurora over RDS PostgreSQL?
- How would you stop a Lambda-based API from exhausting database connections?
- How do you design a DynamoDB table? Why does the partition key matter?
- How would you upgrade PostgreSQL major versions on RDS with minimal downtime?
Next¶
Continue to Observability.