스케일러블하고 비용 효율적인 고처리량 시계열 스토어를 위한 액세스 패턴 설계
Source: Dev.to
테이블 스키마 및 기본 키
| 속성 | 유형 | 역할 |
|---|---|---|
deviceId | String | 파티션 키 |
timestamp | String (ISO‑8601, e.g., 2025-12-04T12:34:56Z) | 정렬 키 |
temperature, humidity, pressure | Number | 페이로드 |
metadata | String (JSON) | 옵션 페이로드 |
ttl | Number (epoch seconds) | 만료를 위한 TTL 속성 |
왜 이 PK인가?
디바이스별 모든 읽기값을 함께 저장하여 효율적인 범위 쿼리(deviceId = X AND timestamp BETWEEN …)를 가능하게 합니다. 최신 읽기값에 대한 단일 항목 쿼리는 ScanIndexForward=false와 Limit=1을 사용해 수행할 수 있습니다.
인덱싱 전략
| 인덱스 | 파티션 키 | 정렬 키 | 사용 사례 |
|---|---|---|---|
| Primary Table | deviceId | timestamp | 디바이스당 포인트 조회 및 범위 쿼리 |
Global Secondary Index (GSI) – DeviceLatestGSI | deviceId | timestamp (projected as DESC) | 전체 파티션을 스캔하지 않고 최신 읽기값을 직접 조회 (Limit=1, ScanIndexForward=false) |
Optional GSI – MetricGSI | metricType (e.g., "temperature" constant) | timestamp | 단일 메트릭에 대한 디바이스 간 시간 범위 쿼리 (드물게) |
Note: 기본 테이블은 이미 최신‑읽기 쿼리를 지원합니다; GSI는 선택 사항이며 동일한 deviceId에 대한 동시 “최신” 읽기가 많이 발생해 핫 파티션 읽기가 발생할 것으로 예상될 때만 비용이 추가됩니다. 대부분의 경우 Limit=1을 사용한 기본 테이블이면 충분합니다.
용량 모드 및 스케일링
| 모드 | 사용 시점 | 구성 |
|---|---|---|
| On‑Demand | 예측 불가능한 급증, 손쉬운 시작, 용량 관리 불필요. | 자동으로 초당 10 k 쓰기 처리; 요청당 비용 지불. |
| Provisioned + Auto Scaling | 예측 가능한 트래픽, 비용 제어 원함. | 시작 시 15,000 RCUs와 5,000 WCUs 사용 (각 쓰기 ≤ 1 KB당 1 WCU 소모). 자동 스케일링 목표 70 % 활용도 활성화. |
Cost comparison (approx., US East 1, Dec 2025):
- On‑Demand 쓰기: 백만 쓰기 요청 단위당 $1.25 → 초당 10 k 쓰기 기준 약 $12.5 k/월 (≈ 26 M 쓰기/일).
- Provisioned 5,000 WCUs는 WCU‑시간당 약 $0.65 → $2.3 k/월에 자동 스케일링 버퍼 추가.
On‑Demand는 운영이 간단하고, 트래픽이 안정적이면 Provisioned가 더 저렴할 수 있습니다.
핫 파티션 위험 완화
- Uniform
deviceIddistribution: 디바이스 ID가 무작위(예: UUID 또는 해시)인지 확인합니다. - If a few devices dominate traffic: Sharding을 사용합니다 –
deviceId앞에 무작위 샤드 접미사를 붙입니다(e.g.,deviceId#shard01). 작은 설정 테이블에 샤드 개수를 저장하고, 애플리케이션이 모든 샤드를 조회해 결과를 병합합니다. 이렇게 하면 파티션당 쓰기 용량이 고르게 분산됩니다.
데이터 보존 (TTL)
숫자형 속성 ttl = timestampEpoch + 30 days 를 추가합니다.
이 속성에 DynamoDB TTL를 활성화하면 DynamoDB가 만료된 항목을 자동으로 삭제합니다(보통 만료 후 48 시간 이내).
- 추가 Lambda가 필요 없으므로 비용이 낮습니다.
읽기 성능 최적화
- Projection: GSI에 필요한 속성만 보관합니다(e.g.,
temperature,humidity,pressure,timestamp). 읽기 크기와 비용을 줄일 수 있습니다. - Consistent vs. eventual reads: 대부분의 쿼리는 eventual consistency를 사용합니다(더 저렴, 4 KB당 0.5 RCU). “최신 읽기”처럼 신선도가 중요한 경우 strongly consistent 읽기를 사용합니다(4 KB당 1 RCU).
- BatchGetItem을 활용해 여러 디바이스의 최신 읽기값을 한 번에 가져올 수 있습니다.
보조 서비스 (선택 사항)
| 서비스 | 목적 |
|---|---|
| AWS Kinesis Data Streams | 들어오는 센서 데이터를 버퍼링하고, 급증하는 쓰기를 완화하며, Lambda 소비자를 통해 DynamoDB에 전달합니다. |
| AWS Lambda (TTL cleanup) | 정확히 30일에 삭제가 필요할 경우, 예약된 Lambda가 ttl이 임박한 항목을 조회·삭제할 수 있지만 보통 DynamoDB TTL만으로 충분합니다. |
| Amazon CloudWatch Alarms | ConsumedWriteCapacityUnits, ThrottledRequests, SystemErrors 등을 모니터링해 스케일링이나 알림을 트리거합니다. |
| AWS Glue / Athena | DynamoDB Streams → Lambda → S3 로 내보낸 히스토리 데이터를 S3에 저장하고, ad‑hoc 분석을 수행합니다. |
트레이드오프 요약
| 트레이드오프 | 영향 |
|---|---|
| On‑Demand vs. Provisioned | On‑Demand는 운영이 간단하지만, 지속적인 10 k writes/s에서는 약 30 % 더 비쌀 수 있습니다. Provisioned는 용량 계획이 필요하지만 자동 스케일링을 활용하면 비용이 낮아질 수 있습니다. |
| Sharding vs. Simplicity | Sharding은 트래픽이 편중된 디바이스에서 핫 파티션 위험을 없애지만, 쿼리 로직이 복잡해집니다(디바이스당 여러 샤드 조회). |
| TTL vs. Lambda cleanup | TTL는 저비용이며 최대 48 시간 지연될 수 있습니다. Lambda는 정확한 삭제 시점을 제공하지만 컴퓨팅 비용이 추가됩니다. |
| GSI for latest reading | 높은 부하에서도 O(1) 읽기 지연을 보장하지만, 각 쓰기마다 GSI 업데이트 비용이 발생합니다. 대부분의 경우 기본 테이블에 Limit=1을 사용하는 것이 충분합니다. |
| Strong vs. eventual consistency | 강력 일관성 읽기는 비용이 두 배이므로, 즉시 최신성이 필요한 경우 |