← Cloud · AWS← Cloud · AWS
Cloud · AWSCloud · AWS19 Th7, 2026Jul 19, 202621 phút đọc16 min read

DatabasesDatabases

Part of the Cloud knowledge base — AWS deep dive. Complements DevOps Roadmap.

Tổng quan

AWS cung cấp một database chuyên dụng cho mỗi hình dạng dữ liệu, và nước đi thắng cuộc là khớp database với mẫu truy cập thay vì ép mọi thứ vào một engine duy nhất. Cần giao dịch ACID và join? Một database quan hệ (RDS, Aurora). Cần tra cứu key với độ trễ vài mili-giây ở bất kỳ quy mô nào? Một kho key-value (DynamoDB). Cần cache dữ liệu nóng hoặc giữ session? Một kho in-memory (ElastiCache). Cần quét hàng tỷ dòng cho analytics? Một warehouse dạng cột (Redshift) hoặc truy vấn tại chỗ trên S3 (Athena). Bản năng quan-hệ-vs-mọi-thứ-khác từ thời on-prem chính là cái bẫy mà danh mục được quản lý của AWS cho phép bạn thoát ra.

Trang này bao gồm RDS (các engine quan hệ được quản lý, Multi-AZ vs read replica, parameter/option group), Aurora (MySQL/PostgreSQL cloud-native với Serverless v2 và Global Database), DynamoDB (partition/sort key, chế độ capacity, GSI/LSI, single-table design, DAX), ElastiCache (Redis/Memcached), và cặp analytics Redshift và Athena, cộng với DMS cho di chuyển. Nó kết thúc bằng hướng dẫn chọn một database và một bảng DynamoDB thực hành cùng các mẫu truy vấn.

Một cách nhìn hữu ích: các database được quản lý chuyển gánh nặng không tạo khác biệt — vá lỗi, backup, failover, replication — sang AWS, để bạn dành thời gian cho việc mô hình hóa dữ liệu và truy vấn, nơi có đòn bẩy thực sự. Phần khó không còn là vận hành database nữa; đó là chọn đúng cái và mô hình hóa dữ liệu cho khớp với thớ của nó. Một bảng DynamoDB được mô hình hóa tốt scale nhẹ nhàng; một bảng mô hình hóa tệ tạo hot-spot và bị throttle bất kể bạn trả bao nhiêu.

Tại sao lựa chọn database là quyết định dính chặt nhất

Trong mọi quyết định hạ tầng, database là cái khó đảo ngược nhất — dữ liệu có trọng lực, việc di chuyển rủi ro, và code ứng dụng ràng buộc chặt với mô hình dữ liệu. Một schema quan hệ bị ép lên một workload key-value sẽ bị throttle dưới tải; một kho key-value bị yêu cầu làm join tùy ý thì không làm được. Chọn đúng engine và mô hình hóa cho các thế mạnh của nó ngay từ ngày đầu tránh được lần tái nền tảng đau đớn nhất mà một đội có thể đối mặt. Đây là quyết định cần chậm lại và làm cho đúng.

Kiến thức nền tảng

RDS — Relational Database Service

RDS là các database quan hệ được quản lý: AWS lo provisioning, vá lỗi, backup và failover trong khi bạn có một SQL engine tiêu chuẩn. Các engine được hỗ trợ:

EngineGhi chú
PostgreSQLGiàu tính năng, mở rộng được; mặc định phổ biến cho app mới
MySQL / MariaDBPhổ biến khắp nơi, được hiểu rõ
Oracle / SQL ServerCác engine thương mại (BYOL hoặc bao gồm license) cho lift-and-shift
Aurora (tương thích MySQL/PostgreSQL)Bản tái thiết kế cloud-native của AWS — đề cập bên dưới

Multi-AZ vs read replica — hai tính năng khác nhau mà người ta liên tục gộp lẫn:

Multi-AZRead replica
Mục đíchTính sẵn sàng cao / failoverScale thông lượng đọc
Cách thứcStandby đồng bộ ở một AZ khácBản sao bất đồng bộ (cùng hoặc cross-Region)
Khi primary lỗiFailover tự động sang standby (~60–120s)Không failover tự động (replica là để đọc)
Đọc được?Standby không đọc được (Multi-AZ instance); Multi-AZ cluster thêm các standby đọc đượcCó — phục vụ lưu lượng đọc
Độ trễ (lag)Không (đồng bộ)Một chút (bất đồng bộ)

Dùng Multi-AZ cho tính sẵn sàng (database production luôn nên là Multi-AZ) và read replica cho scale đọc (giảm tải cho báo cáo, lưu lượng nặng đọc; promote một cái thành DB độc lập cho di chuyển hoặc DR). Chúng bổ trợ cho nhau — một database production thường vừa là Multi-AZ vừa có read replica.

Parameter group kiểm soát cấu hình engine (kích thước buffer, timeout, logging, max_connections) — tương đương được quản lý của việc sửa postgresql.conf/my.cnf. Một số thay đổi là động; số khác cần reboot. Option group bật các tính năng tùy chọn đặc thù engine (Oracle TDE, SQL Server Audit, v.v.). Cả hai đều được gắn vào instance và cho phép bạn version hóa và tái sử dụng cấu hình giữa các database.

Backup & khôi phục — RDS cho backup tự động hằng ngày với point-in-time recovery (khôi phục về bất kỳ giây nào trong cửa sổ giữ lại, tới 35 ngày) cộng với các snapshot thủ công. Storage tự động scale, và mã hóa lúc nghỉ (KMS) nên luôn được bật.

Aurora

Aurora là engine quan hệ cloud-native của AWS, tương thích ở mức giao thức với MySQLPostgreSQL nhưng được tái kiến trúc: nó tách compute khỏi một lớp storage phân tán, tự chữa lành, replicate dữ liệu sáu chiều trên ba AZ và tự động scale tới 128 TiB. Kết quả là hiệu năng và tính sẵn sàng tốt hơn hẳn RDS thường, với failover nhanh hơn và tới 15 read replica độ-trễ-thấp dùng chung cùng storage (không có lag sao chép replication theo nghĩa truyền thống).

DynamoDB — NoSQL key-value được quản lý

DynamoDB là một database key-value và document serverless, được quản lý hoàn toàn, cho độ trễ vài mili-giây ở bất kỳ quy mô nào, không có server nào phải quản lý và thông lượng gần như không giới hạn. Đây là lựa chọn hàng đầu cho các workload quy mô lớn, tốc độ cao với các mẫu truy cập đã biết.

Key định nghĩa cách dữ liệu được phân vùng và sắp xếp:

Chế độ capacity — cách bạn trả tiền cho thông lượng:

Chế độTính phí theoTốt nhất cho
On-DemandTheo requestLưu lượng biến động/chưa biết, app mới, không-muốn-quản-lý-capacity
ProvisionedRCU/WCU đã reserve (+ auto-scaling)Lưu lượng dễ dự đoán, ổn định; rẻ hơn ở khối lượng cao ổn định

Bắt đầu bằng On-Demand; chuyển sang Provisioned với auto-scaling khi lưu lượng đã dự đoán được và khối lượng khiến mức reserved rẻ hơn.

Secondary index cho phép bạn truy vấn trên các thuộc tính không phải key:

GSI (Global Secondary Index)LSI (Local Secondary Index)
Partition keyKhác với bảng gốcGiống bảng gốc
Khi tạoBất kỳ lúc nàoChỉ lúc tạo bảng
Thông lượngCapacity riêng của nóDùng chung capacity của bảng
Nhất quánChỉ nhất quán cuối cùngCó nhất quán mạnh
DùngMẫu truy vấn mới trên bất kỳ thuộc tính nàoSắp xếp thay thế trong cùng phân vùng

GSI là con ngựa kéo — bạn thêm chúng để hỗ trợ các mẫu truy cập bổ sung. LSI là ngách hẹp và phải được định nghĩa từ trước.

Single-table design — mẫu chuẩn mực của DynamoDB là lưu nhiều loại thực thể (user, order, item) trong một bảng, dùng các thuộc tính PK/SK chung chung và các GSI được nạp chồng (overloaded) để phục vụ mọi mẫu truy cập với càng ít truy vấn càng tốt. Nó cảm giác xa lạ với người mô hình hóa SQL nhưng là cách bạn có được truy cập một-request tới dữ liệu liên quan ở quy mô lớn. Kỷ luật: liệt kê mọi mẫu truy cập trước, rồi thiết kế key để thỏa mãn chúng — DynamoDB thưởng cho việc biết trước các truy vấn của bạn và phạt việc truy vấn tùy ý.

DAX (DynamoDB Accelerator) là một cache in-memory đặt trước DynamoDB cho các lần đọc ở mức micro-giây cho các workload nặng đọc, nhạy độ trễ, với một API tương thích DynamoDB (drop-in, không cần viết lại app). Dùng nó khi ngay cả vài mili-giây cũng quá chậm hoặc để giảm chi phí đọc trên các item nóng.

Các tính năng khác đáng biết: TTL (tự hết hạn item theo timestamp — tuyệt cho session/dữ liệu tạm thời), DynamoDB Streams (change-data-capture cấp cho Lambda), global table (đa Region active-active), và transaction (ACID trên nhiều item).

ElastiCache — caching in-memory

ElastiCache là các kho dữ liệu in-memory được quản lý đặt trước database của bạn để cắt độ trễ và giảm tải đọc. Hai engine:

Redis (và Valkey)Memcached
Cấu trúc dữ liệuPhong phú (string, hash, list, set, sorted set, stream, pub/sub)Chỉ key-value đơn giản
PersistenceTùy chọn (snapshot, AOF)Không (cache thuần)
Replication / HACó (replica, Multi-AZ, cluster mode)Không (sharded, không replication)
Dùng choBảng xếp hạng, session, queue, caching phong phú, nhu cầu HACache object đơn giản, scale ngang

Chọn Redis cho gần như mọi thứ — nó có năng lực hơn, hỗ trợ HA và persistence, và làm nhiều hơn caching (rate limiting, bảng xếp hạng, pub/sub, kho session). Memcached chỉ hợp trường hợp hẹp của một cache đơn giản, đa luồng, sharded không cần replication hay cấu trúc dữ liệu. Các mẫu phổ biến: cache-aside (app kiểm tra cache, rơi về DB khi miss, nạp cache), và dùng Redis cho lưu trữ session để các app server giữ trạng thái không có (stateless).

Redshift và Athena — analytics

Khi bạn cần phân tích khối lượng lớn thay vì phục vụ giao dịch, các database OLTP là công cụ sai.

Redshift là một data warehouse dạng cột, song song ồ ạt (MPP) được quản lý cho analytics quy mô petabyte — các phép tổng hợp và join phức tạp trên hàng tỷ dòng, cấp cho các dashboard BI. Storage dạng cột và MPP làm các lần quét phân tích nhanh. Redshift Serverless loại bỏ việc quản lý cluster (trả theo mức dùng), và Redshift Spectrum truy vấn dữ liệu trực tiếp trong S3. Dùng nó làm warehouse trung tâm cho analytics có cấu trúc với tải truy vấn liên tục.

Athenatruy vấn SQL serverless trực tiếp trên dữ liệu trong S3 — không cluster, không load. Trỏ nó vào S3 (CSV, JSON, Parquet), định nghĩa một schema (qua Glue Data Catalog), và trả theo TB đã quét. Lý tưởng cho các truy vấn tùy ý, phân tích log, và analytics không thường xuyên nơi việc dựng lên một warehouse không đáng. Dùng các định dạng cột (Parquet/ORC) và phân vùng để cắt mạnh lượng dữ liệu được quét — và do đó cả chi phí.

Redshift vs Athena: Athena cho các truy vấn thỉnh thoảng, tùy ý, hoặc khám phá trên S3 với không hạ tầng; Redshift cho warehousing liên tục, đồng thời cao, quan trọng về hiệu năng. Nhiều kiến trúc dùng cả hai — Athena trên data lake S3 thô, Redshift cho các mart đã tinh chế, được truy vấn nặng.

DMS — Database Migration Service

DMS di chuyển database sang AWS với downtime tối thiểu: nó replicate từ một nguồn (on-prem hoặc cloud, hầu hết engine) tới một đích, hỗ trợ di chuyển đồng nhất (Oracle→Oracle) và khác chủng (Oracle→Aurora PostgreSQL, ghép với Schema Conversion Tool). Replication liên tục (CDC) của nó giữ nguồn và đích đồng bộ trong lúc cutover, để bạn di chuyển các hệ thống đang sống với một cửa sổ chuyển đổi ngắn. Dùng nó cho lift-and-shift sang RDS/Aurora, hiện đại hóa xuyên engine, và replication liên tục vào một data lake.

Khái niệm chính

Chọn một database: quan hệ vs KV vs analytics

Khớp engine với mẫu truy cập, không theo thói quen.

Nhu cầuChọnTại sao
Giao dịch ACID, join, SQL tùy ý, quy mô vừaRDS / AuroraToàn vẹn quan hệ, truy vấn linh hoạt
Quan hệ cloud-native, quy mô/HA cao, serverless hoặc globalAuroraStorage phân tán, failover nhanh, serverless v2
Key-value/document ở quy mô khổng lồ, mẫu truy cập đã biết, độ trễ msDynamoDBServerless, thông lượng không giới hạn, độ trễ dễ dự đoán
Cache, session, bảng xếp hạng, rate limitingElastiCache (Redis)Truy cập in-memory micro-giây
Analytics petabyte, BI, tổng hợp phức tạpRedshiftWarehouse MPP dạng cột
SQL tùy ý trên S3, phân tích log, không hạ tầngAthenaTruy vấn tại-chỗ serverless
Tìm kiếm full-text/vector, tìm kiếm logOpenSearchSearch engine dạng inverted-index
Quan hệ đồ thị, sổ cái, chuỗi thời gianNeptune / QLDB / TimestreamCác engine chuyên biệt chuyên dụng

Kinh nghiệm ra quyết định:

  1. Bạn cần join, giao dịch, và SQL tùy ý? → Quan hệ (Aurora nếu bạn muốn scale/HA/serverless; RDS nếu không).
  2. Bạn biết các mẫu truy cập của mình và cần quy mô khổng lồ với độ trễ dễ dự đoán? → DynamoDB (và mô hình hóa cho các mẫu đó từ trước).
  3. Bạn đang giảm tải đọc hoặc lưu trạng thái nóng tạm thời? → ElastiCache đặt trước hệ thống bản ghi gốc.
  4. Bạn đang phân tích khối lượng lớn, không phục vụ giao dịch? → Athena (thỉnh thoảng, S3) hoặc Redshift (warehouse liên tục).

Cạm bẫy: dùng DynamoDB khi bạn thực sự cần join tùy ý và truy vấn linh hoạt (bạn sẽ vật lộn với nó); chạy các lần quét phân tích trên RDS OLTP của bạn (nó sẽ làm nghẹt các giao dịch — replicate sang một warehouse); provision DynamoDB trước khi bạn biết các mẫu truy cập (bạn sẽ hot-partition); và coi một cache như một hệ thống bản ghi gốc (cache được phép mất dữ liệu).

Ví dụ thực hành: bảng DynamoDB + các mẫu truy vấn

Một single-table design cho một app thương mại điện tử, phục vụ nhiều mẫu truy cập từ một bảng. Đầu tiên, các mẫu truy cập chúng ta thiết kế cho:

  1. Lấy một user theo ID.
  2. Lấy mọi order của một user (mới nhất trước).
  3. Lấy một order đơn theo ID.
  4. Lấy mọi order ở một trạng thái cho trước (khung nhìn admin).

Thiết kế keyPK/SK được nạp chồng, với một GSI cho truy vấn trạng thái:

Thực thểPKSKGSI1PKGSI1SK
UserUSER#u123USER#u123
OrderUSER#u123ORDER#2026-07-18#o987STATUS#SHIPPED2026-07-18

Tạo bảng với một GSI (CLI):

aws dynamodb create-table \
  --table-name Commerce \
  --attribute-definitions \
      AttributeName=PK,AttributeType=S \
      AttributeName=SK,AttributeType=S \
      AttributeName=GSI1PK,AttributeType=S \
      AttributeName=GSI1SK,AttributeType=S \
  --key-schema \
      AttributeName=PK,KeyType=HASH \
      AttributeName=SK,KeyType=RANGE \
  --global-secondary-indexes \
      '[{"IndexName":"GSI1",
         "KeySchema":[{"AttributeName":"GSI1PK","KeyType":"HASH"},
                      {"AttributeName":"GSI1SK","KeyType":"RANGE"}],
         "Projection":{"ProjectionType":"ALL"}}]' \
  --billing-mode PAY_PER_REQUEST

PAY_PER_REQUEST là On-Demand — không có capacity phải quản lý khi lưu lượng còn chưa biết.

Các mẫu truy vấn:

# Pattern 1 & 2: get the user AND all their orders in ONE query.
# The user item and all order items share PK=USER#u123, so a single
# partition query returns everything; sort key ordering gives newest-first.
aws dynamodb query \
  --table-name Commerce \
  --key-condition-expression "PK = :pk" \
  --expression-attribute-values '{":pk":{"S":"USER#u123"}}' \
  --scan-index-forward false            # descending: most recent orders first

# Pattern 2 only: just the orders (SK begins with ORDER#)
aws dynamodb query \
  --table-name Commerce \
  --key-condition-expression "PK = :pk AND begins_with(SK, :o)" \
  --expression-attribute-values '{":pk":{"S":"USER#u123"},":o":{"S":"ORDER#"}}'

# Pattern 4: all SHIPPED orders (admin) — served by the GSI
aws dynamodb query \
  --table-name Commerce \
  --index-name GSI1 \
  --key-condition-expression "GSI1PK = :s" \
  --expression-attribute-values '{":s":{"S":"STATUS#SHIPPED"}}'

Điều cốt lõi: bởi vì user và các order của họ dùng chung một partition key, “lấy user + các order của họ” là một truy vấn duy nhất không có join — mẫu mà SQL sẽ cần nhiều vòng round-trip hoặc một join, DynamoDB làm trong một request. GSI đánh chỉ mục lại các order theo trạng thái nên khung nhìn admin cũng hiệu quả tương đương. Đây là single-table design thu nhỏ: liệt kê các mẫu truy cập, rồi định hình key và index sao cho mỗi mẫu là một truy vấn rẻ — không bao giờ là một Scan, thứ đọc cả bảng và là anti-pattern kinh điển của DynamoDB.

Best Practices

  1. Luôn chạy RDS/Aurora production Multi-AZ — một database single-AZ biến việc bảo trì thường lệ hoặc một cú chớp AZ thành một sự cố. Multi-AZ cho failover tự động với một khoản phụ phí khiêm tốn; đó là mốc cơ sở cho bất cứ thứ gì bạn không thể để mất.
  2. Tách các lần đọc bằng replica, đừng nhầm chúng với HA — Multi-AZ là cho tính sẵn sàng, read replica là cho thông lượng đọc. Giảm tải báo cáo và lưu lượng nặng đọc sang replica, và để dành primary cho ghi và các lần đọc nhất quán.
  3. Mã hóa lúc nghỉ và khi truyền, luôn luôn — bật mã hóa KMS khi tạo database (nó không thể được thêm vào một RDS instance chưa mã hóa hiện có mà không snapshot-restore) và ép buộc kết nối TLS. Đây là điều kiện tối thiểu cho bất kỳ kho dữ liệu nào.
  4. Không bao giờ đặt database trong public subnet — đặt RDS/Aurora/ElastiCache trong private subnet, chỉ tiếp cận được từ các security group của app. Một database mở ra internet là một vụ vi phạm đang chờ xảy ra; truy cập nó qua một bastion hoặc SSM, không bao giờ trực tiếp.
  5. Quản lý credential bằng Secrets Manager và xoay vòng chúng — lưu credential DB trong Secrets Manager với xoay vòng tự động, và ưu tiên IAM database authentication nơi được hỗ trợ để các app nhận token ngắn hạn thay vì mật khẩu tĩnh.
  6. Liệt kê các mẫu truy cập DynamoDB trước khi mô hình hóa — DynamoDB thưởng cho việc biết mọi truy vấn từ trước và phạt truy cập tùy ý. Thiết kế partition/sort key và GSI để phục vụ mỗi mẫu như một truy vấn; một Scan trên đường nóng nghĩa là mô hình sai.
  7. Chọn partition key có cardinality cao và truy cập đều — một key cardinality thấp hoặc lệch tạo ra các hot partition bị throttle bất kể bạn mua bao nhiêu capacity. Trải tải; thêm hậu tố/sharding nếu một key tự nhiên tập trung lưu lượng.
  8. Bắt đầu DynamoDB bằng On-Demand, chuyển sang Provisioned khi lưu lượng dễ dự đoán — On-Demand loại bỏ việc đoán capacity cho các workload mới/biến động; chuyển sang Provisioned với auto-scaling khi khối lượng đủ ổn định để mức reserved tiết kiệm tiền.
  9. Cache bằng ElastiCache để bảo vệ hệ thống bản ghi gốc — đặt Redis (cache-aside) trước các database nặng đọc để cắt độ trễ và giảm tải, và giữ session trong Redis để các app server giữ trạng thái không có. Thiết kế cho các lần cache miss và coi cache là có thể vứt bỏ.
  10. Ưu tiên Redis (Valkey) hơn Memcached — Redis cho HA, persistence, và cấu trúc dữ liệu phong phú; Memcached chỉ hợp trường hợp cache-sharded-đơn-giản hẹp. Mặc định dùng Redis trừ khi bạn có lý do cụ thể để không.
  11. Right-size và giám sát; bật Performance Insights — bật RDS/Aurora Performance Insights và CloudWatch để bắt các truy vấn chậm, bão hòa kết nối, và under/over-provisioning. Database thường bị over-provision về compute và under-tune về truy vấn.
  12. Tự động hóa backup và test khôi phục; sao chép các backup quan trọng cross-Region — backup tự động và PITR bật theo mặc định, nhưng một backup chưa được test là một hy vọng, không phải một kế hoạch. Định kỳ khôi phục, và sao chép các snapshot quan trọng cho DR sang một Region/tài khoản thứ hai.
  13. Dùng Aurora Serverless v2 cho các workload biến động hoặc gián đoạn — nó scale dung lượng (hướng về zero khi nhàn rỗi) nên bạn trả cho tải, không phải provision đỉnh. Lý tưởng cho dev/test, lưu lượng biến động, và hợp nhất nhiều database nhỏ.
  14. Chọn công cụ analytics theo tần suất truy vấn — Athena cho SQL tùy ý/thỉnh thoảng trên S3 với không hạ tầng; Redshift cho warehousing liên tục, đồng thời cao. Không bao giờ chạy các lần quét phân tích trên database OLTP của bạn — thay vào đó replicate sang warehouse.
  15. Tối ưu chi phí Athena và warehouse bằng định dạng cột và phân vùng — lưu dữ liệu dạng Parquet/ORC và phân vùng theo các bộ lọc phổ biến (ngày, region) để cắt số byte được quét, thứ chính xác là cái Athena tính phí. Điều này thường xuyên giảm chi phí cả một bậc độ lớn.
  16. Dùng DMS cho các di chuyển ít downtime, với CDC cho cutover — DMS cộng với Schema Conversion Tool xử lý hiện đại hóa xuyên engine, và replication liên tục giữ nguồn và đích đồng bộ để cửa sổ chuyển đổi giữ ngắn.

Tài liệu tham khảo

Part of the Cloud knowledge base — AWS deep dive. Complements DevOps Roadmap.

Overview

AWS offers a purpose-built database for every data shape, and the winning move is matching the database to the access pattern rather than forcing everything into one engine. Need ACID transactions and joins? A relational database (RDS, Aurora). Need single-digit-millisecond key lookups at any scale? A key-value store (DynamoDB). Need to cache hot data or hold sessions? An in-memory store (ElastiCache). Need to scan billions of rows for analytics? A columnar warehouse (Redshift) or query-in-place on S3 (Athena). The relational-vs-everything-else instinct from on-prem days is exactly the trap AWS’s managed catalog lets you escape.

This page covers RDS (managed relational engines, Multi-AZ vs read replicas, parameter/option groups), Aurora (cloud-native MySQL/PostgreSQL with Serverless v2 and Global Database), DynamoDB (partition/sort keys, capacity modes, GSIs/LSIs, single-table design, DAX), ElastiCache (Redis/Memcached), and the analytics pair Redshift and Athena, plus DMS for migration. It closes with guidance on picking a database and a worked DynamoDB table with query patterns.

A useful framing: managed databases move the undifferentiated toil — patching, backups, failover, replication — to AWS, so you spend your time on data modeling and queries, which is where the real leverage is. The hard part is no longer running the database; it’s choosing the right one and modeling data to fit its grain. A well-modeled DynamoDB table scales effortlessly; a badly-modeled one hot-spots and throttles no matter how much you pay.

Why the database choice is the stickiest decision

Of all infrastructure decisions, the database is the hardest to reverse — data has gravity, migrations are risky, and application code binds tightly to the data model. A relational schema forced onto a key-value workload throttles under load; a key-value store asked to do ad-hoc joins can’t. Picking the right engine and modeling for its strengths on day one avoids the most painful re-platforming a team can face. This is the decision to slow down and get right.

Fundamentals

RDS — Relational Database Service

RDS is managed relational databases: AWS handles provisioning, patching, backups, and failover while you get a standard SQL engine. Supported engines:

EngineNotes
PostgreSQLFeature-rich, extensible; the common default for new apps
MySQL / MariaDBUbiquitous, well-understood
Oracle / SQL ServerCommercial engines (BYOL or license-included) for lift-and-shift
Aurora (MySQL/PostgreSQL-compatible)AWS’s cloud-native re-engineering — covered below

Multi-AZ vs read replicas — two different features people constantly conflate:

Multi-AZRead replicas
PurposeHigh availability / failoverScale read throughput
HowSynchronous standby in another AZAsynchronous copies (same or cross-Region)
On primary failureAutomatic failover to standby (~60–120s)No automatic failover (replicas are for reads)
Readable?Standby is not readable (Multi-AZ instance); Multi-AZ cluster adds readable standbysYes — serve read traffic
LagNone (synchronous)Some (asynchronous)

Use Multi-AZ for availability (production databases should always be Multi-AZ) and read replicas for read scaling (offload reporting, read-heavy traffic; promote one to a standalone DB for migration or DR). They are complementary — a production database is often Multi-AZ and has read replicas.

Parameter groups control engine configuration (buffer sizes, timeouts, logging, max_connections) — the managed equivalent of editing postgresql.conf/my.cnf. Some changes are dynamic; others need a reboot. Option groups enable engine-specific optional features (Oracle TDE, SQL Server Audit, etc.). Both are attached to instances and let you version and reuse configuration across databases.

Backups & recovery — RDS gives automated daily backups with point-in-time recovery (restore to any second within the retention window, up to 35 days) plus manual snapshots. Storage is auto-scaling, and encryption at rest (KMS) should always be on.

Aurora

Aurora is AWS’s cloud-native relational engine, wire-compatible with MySQL and PostgreSQL but re-architected: it separates compute from a distributed, self-healing storage layer that replicates data six ways across three AZs and auto-scales to 128 TiB. The result is markedly better performance and availability than stock RDS, with faster failover and up to 15 low-lag read replicas that share the same storage (no replication copy lag in the traditional sense).

DynamoDB — managed NoSQL key-value

DynamoDB is a fully managed, serverless key-value and document database delivering single-digit-millisecond latency at any scale, with no servers to manage and virtually unlimited throughput. It is the go-to for high-scale, high-velocity workloads with known access patterns.

Keys define how data is partitioned and sorted:

Capacity modes — how you pay for throughput:

ModeBills byBest for
On-DemandPer requestSpiky/unknown traffic, new apps, spdon’t-want-to-manage-capacity
ProvisionedReserved RCUs/WCUs (+ auto-scaling)Predictable, steady traffic; cheaper at high steady volume

Start On-Demand; switch to Provisioned with auto-scaling once traffic is predictable and volume makes the reserved rate cheaper.

Secondary indexes let you query on non-key attributes:

GSI (Global Secondary Index)LSI (Local Secondary Index)
Partition keyDifferent from base tableSame as base table
When createdAnytimeOnly at table creation
ThroughputIts own capacityShares table capacity
ConsistencyEventually consistent onlyStrong consistency available
UseNew query patterns on any attributeAlternate sort within same partition

GSIs are the workhorse — you add them to support additional access patterns. LSIs are niche and must be defined up front.

Single-table design — the DynamoDB-idiomatic pattern of storing multiple entity types (users, orders, items) in one table, using generic PK/SK attributes and overloaded GSIs to serve all access patterns with as few queries as possible. It feels alien to SQL modelers but is how you get one-request access to related data at scale. The discipline: enumerate every access pattern first, then design keys to satisfy them — DynamoDB rewards knowing your queries in advance and punishes ad-hoc querying.

DAX (DynamoDB Accelerator) is an in-memory cache in front of DynamoDB delivering microsecond reads for read-heavy, latency-critical workloads, with a DynamoDB-compatible API (drop-in, no app rewrite). Use it when even single-digit-ms is too slow or to shave read costs on hot items.

Other features worth knowing: TTL (auto-expire items by timestamp — great for sessions/ephemeral data), DynamoDB Streams (change-data-capture feeding Lambda), global tables (multi-Region active-active), and transactions (ACID across items).

ElastiCache — in-memory caching

ElastiCache is managed in-memory data stores fronting your databases to cut latency and offload reads. Two engines:

Redis (and Valkey)Memcached
Data structuresRich (strings, hashes, lists, sets, sorted sets, streams, pub/sub)Simple key-value only
PersistenceOptional (snapshots, AOF)None (pure cache)
Replication / HAYes (replicas, Multi-AZ, cluster mode)No (sharded, no replication)
Use forLeaderboards, sessions, queues, rich caching, HA needsSimple, horizontally-scaled object cache

Choose Redis for almost everything — it’s more capable, supports HA and persistence, and does far more than caching (rate limiting, leaderboards, pub/sub, session store). Memcached fits only the narrow case of a simple, multi-threaded, sharded cache with no need for replication or data structures. Common patterns: cache-aside (app checks cache, falls back to DB on miss, populates cache), and using Redis for session storage so app servers stay stateless.

Redshift and Athena — analytics

When you need to analyze large volumes rather than serve transactions, OLTP databases are the wrong tool.

Redshift is a managed, columnar, massively-parallel data warehouse for petabyte-scale analytics — complex aggregations and joins over billions of rows, feeding BI dashboards. Columnar storage and MPP make analytical scans fast. Redshift Serverless removes cluster management (pay per usage), and Redshift Spectrum queries data directly in S3. Use it as the central warehouse for structured analytics with sustained query load.

Athena is serverless SQL query directly on data in S3 — no cluster, no loading. Point it at S3 (CSV, JSON, Parquet), define a schema (via Glue Data Catalog), and pay per TB scanned. Ideal for ad-hoc queries, log analysis, and infrequent analytics where standing up a warehouse isn’t worth it. Use columnar formats (Parquet/ORC) and partitioning to slash the data scanned — and thus cost.

Redshift vs Athena: Athena for occasional, ad-hoc, or exploratory queries on S3 with zero infrastructure; Redshift for sustained, high-concurrency, performance-critical warehousing. Many architectures use both — Athena on the raw S3 data lake, Redshift for the curated, heavily-queried marts.

DMS — Database Migration Service

DMS migrates databases to AWS with minimal downtime: it replicates from a source (on-prem or cloud, most engines) to a target, supporting homogeneous (Oracle→Oracle) and heterogeneous (Oracle→Aurora PostgreSQL, paired with the Schema Conversion Tool) migrations. Its continuous replication (CDC) keeps source and target in sync during cutover, so you migrate live systems with a short switchover window. Use it for lift-and-shift to RDS/Aurora, cross-engine modernization, and ongoing replication into a data lake.

Key Concepts

Picking a database: relational vs KV vs analytics

Match the engine to the access pattern, not to habit.

NeedChooseWhy
ACID transactions, joins, ad-hoc SQL, moderate scaleRDS / AuroraRelational integrity, flexible querying
Cloud-native relational, high scale/HA, serverless or globalAuroraDistributed storage, fast failover, serverless v2
Key-value/document at massive scale, known access patterns, ms latencyDynamoDBServerless, unlimited throughput, predictable latency
Cache, sessions, leaderboards, rate limitingElastiCache (Redis)Microsecond in-memory access
Petabyte analytics, BI, complex aggregationsRedshiftColumnar MPP warehouse
Ad-hoc SQL on S3, log analysis, no infraAthenaServerless query-in-place
Full-text/vector search, log searchOpenSearchInverted-index search engine
Graph relationships, ledger, time-seriesNeptune / QLDB / TimestreamPurpose-built specialty engines

Decision heuristics:

  1. Do you need joins, transactions, and ad-hoc SQL? → Relational (Aurora if you want scale/HA/serverless; RDS otherwise).
  2. Do you know your access patterns and need huge scale with predictable latency? → DynamoDB (and model for those patterns up front).
  3. Are you reducing read load or storing ephemeral hot state? → ElastiCache in front of the system of record.
  4. Are you analyzing large volumes, not serving transactions? → Athena (occasional, S3) or Redshift (sustained warehouse).

Pitfalls: using DynamoDB when you actually need ad-hoc joins and flexible querying (you’ll fight it); running analytical scans on your OLTP RDS (it’ll choke your transactions — replicate to a warehouse); provisioning DynamoDB before you know your access patterns (you’ll hot-partition); and treating a cache as a system of record (caches are allowed to lose data).

Worked example: DynamoDB table + query patterns

A single-table design for an e-commerce app, serving multiple access patterns from one table. First, the access patterns we design for:

  1. Get a user by ID.
  2. Get all orders for a user (most-recent first).
  3. Get a single order by ID.
  4. Get all orders in a given status (admin view).

Key design — overloaded PK/SK, with a GSI for the status query:

EntityPKSKGSI1PKGSI1SK
UserUSER#u123USER#u123
OrderUSER#u123ORDER#2026-07-18#o987STATUS#SHIPPED2026-07-18

Creating the table with a GSI (CLI):

aws dynamodb create-table \
  --table-name Commerce \
  --attribute-definitions \
      AttributeName=PK,AttributeType=S \
      AttributeName=SK,AttributeType=S \
      AttributeName=GSI1PK,AttributeType=S \
      AttributeName=GSI1SK,AttributeType=S \
  --key-schema \
      AttributeName=PK,KeyType=HASH \
      AttributeName=SK,KeyType=RANGE \
  --global-secondary-indexes \
      '[{"IndexName":"GSI1",
         "KeySchema":[{"AttributeName":"GSI1PK","KeyType":"HASH"},
                      {"AttributeName":"GSI1SK","KeyType":"RANGE"}],
         "Projection":{"ProjectionType":"ALL"}}]' \
  --billing-mode PAY_PER_REQUEST

PAY_PER_REQUEST is On-Demand — no capacity to manage while traffic is unknown.

Query patterns:

# Pattern 1 & 2: get the user AND all their orders in ONE query.
# The user item and all order items share PK=USER#u123, so a single
# partition query returns everything; sort key ordering gives newest-first.
aws dynamodb query \
  --table-name Commerce \
  --key-condition-expression "PK = :pk" \
  --expression-attribute-values '{":pk":{"S":"USER#u123"}}' \
  --scan-index-forward false            # descending: most recent orders first

# Pattern 2 only: just the orders (SK begins with ORDER#)
aws dynamodb query \
  --table-name Commerce \
  --key-condition-expression "PK = :pk AND begins_with(SK, :o)" \
  --expression-attribute-values '{":pk":{"S":"USER#u123"},":o":{"S":"ORDER#"}}'

# Pattern 4: all SHIPPED orders (admin) — served by the GSI
aws dynamodb query \
  --table-name Commerce \
  --index-name GSI1 \
  --key-condition-expression "GSI1PK = :s" \
  --expression-attribute-values '{":s":{"S":"STATUS#SHIPPED"}}'

The insight: because the user and their orders share a partition key, “get user + their orders” is a single query with no join — the pattern SQL would need multiple round-trips or a join, DynamoDB does in one request. The GSI re-indexes orders by status so the admin view is equally efficient. This is single-table design in miniature: enumerate access patterns, then shape keys and indexes so each pattern is one cheap query — never a Scan, which reads the whole table and is the classic DynamoDB anti-pattern.

Best Practices

  1. Always run production RDS/Aurora Multi-AZ — a single-AZ database turns routine maintenance or an AZ blip into an outage. Multi-AZ gives automatic failover for a modest premium; it is the baseline for anything you can’t afford to lose.
  2. Separate reads with replicas, don’t confuse them with HA — Multi-AZ is for availability, read replicas are for read throughput. Offload reporting and read-heavy traffic to replicas, and reserve the primary for writes and consistent reads.
  3. Encrypt at rest and in transit, always — enable KMS encryption at database creation (it can’t be added to an existing unencrypted RDS instance without a snapshot-restore) and enforce TLS connections. This is table stakes for any data store.
  4. Never put databases in public subnets — place RDS/Aurora/ElastiCache in private subnets, reachable only from app security groups. A database open to the internet is a breach waiting to happen; access it through a bastion or SSM, never directly.
  5. Manage credentials with Secrets Manager and rotate them — store DB credentials in Secrets Manager with automatic rotation, and prefer IAM database authentication where supported so apps get short-lived tokens instead of static passwords.
  6. Enumerate DynamoDB access patterns before modeling — DynamoDB rewards knowing every query in advance and punishes ad-hoc access. Design partition/sort keys and GSIs to serve each pattern as one query; a Scan in the hot path means the model is wrong.
  7. Choose partition keys for high cardinality and even access — a low-cardinality or skewed key creates hot partitions that throttle no matter how much capacity you buy. Spread load; add suffixes/sharding if a natural key concentrates traffic.
  8. Start DynamoDB On-Demand, move to Provisioned when traffic is predictable — On-Demand removes capacity guesswork for new/spiky workloads; switch to Provisioned with auto-scaling once volume is steady enough that the reserved rate saves money.
  9. Cache with ElastiCache to protect the system of record — front read-heavy databases with Redis (cache-aside) to cut latency and offload load, and keep sessions in Redis so app servers stay stateless. Design for cache misses and treat the cache as expendable.
  10. Prefer Redis (Valkey) over Memcached — Redis offers HA, persistence, and rich data structures; Memcached fits only the narrow simple-sharded-cache case. Default to Redis unless you have a specific reason not to.
  11. Right-size and monitor; enable Performance Insights — turn on RDS/Aurora Performance Insights and CloudWatch to catch slow queries, connection saturation, and under/over-provisioning. Databases are usually over-provisioned on compute and under-tuned on queries.
  12. Automate backups and test restores; copy critical backups cross-Region — automated backups and PITR are on by default, but an untested backup is a hope, not a plan. Periodically restore, and copy DR-critical snapshots to a second Region/account.
  13. Use Aurora Serverless v2 for spiky or intermittent workloads — it scales capacity (toward zero when idle) so you pay for load, not peak provisioning. Ideal for dev/test, variable traffic, and consolidating many small databases.
  14. Pick analytics tools by query frequency — Athena for ad-hoc/occasional SQL on S3 with zero infra; Redshift for sustained, high-concurrency warehousing. Never run analytical scans against your OLTP database — replicate to the warehouse instead.
  15. Optimize Athena and warehouse cost with columnar formats and partitioning — store data as Parquet/ORC and partition by common filters (date, region) to cut the bytes scanned, which is exactly what Athena bills for. This routinely reduces cost by an order of magnitude.
  16. Use DMS for low-downtime migrations, with CDC for cutover — DMS plus the Schema Conversion Tool handles cross-engine modernization, and continuous replication keeps source and target in sync so the switchover window stays short.

References