amazon S3

- Published on
- /9 mins read/
Trong hệ sinh thái điện toán đám mây Amazon Web Services (AWS), Amazon S3 (Simple Storage Service) là một trong những dịch vụ nền tảng lâu đời nhất nhưng cũng đóng vai trò huyết mạch của mọi kiến trúc phần mềm: từ Data Lake, Backup/Disaster Recovery, đến lưu trữ media và tài liệu cho hàng triệu người dùng.
Với cam kết độ bền dữ liệu (Durability) lên đến 99.999999999% (11 số 9), S3 được xem là kho lưu trữ gần như không thể bị phá hủy.
Tuy nhiên, khi đưa S3 vào một hệ thống phân tán chịu tải cao (High Throughput Enterprise System), các kỹ sư phần mềm thường đối mặt với những thách thức kiến trúc nghiêm trọng:
- Backend bị nghẽn mạng và sập bộ nhớ RAM vì để client upload trực tiếp file qua máy chủ ứng dụng thay vì dùng Direct-to-S3 Presigned URLs.
- File upload dở dang ngốn hàng nghìn USD chi phí rác mỗi tháng do không cấu hình
AbortIncompleteMultipartUpload. - Gặp lỗi nghẽn hạn ngạch KMS (KMS Throttling Quota) khi mã hóa hàng triệu objects bằng SSE-KMS.
- Không hiểu mô hình Strong Read-After-Write Consistency và giới hạn 3,500 PUT / 5,500 GET per prefix, dẫn đến thiết kế cấu trúc Key sai lầm gây thắt cổ chai IOPS.
Bài viết này sẽ phân tích toàn diện các quy chuẩn kiến trúc của Amazon S3 từ cơ chế lưu trữ nội tại đến code mẫu Spring Boot 3 chuẩn production.
# bản chất lưu trữ: object vs block vs file storage
Để lựa chọn đúng giải pháp lưu trữ trên AWS, Kiến trúc sư giải pháp cần phân biệt rạch ròi 3 mô hình lưu trữ:
# mô hình strong consistency của S3
Trước tháng 12/2020, S3 hoạt động theo mô hình Eventual Consistency (sau khi ghi hoặc xóa, việc đọc lại ngay có thể nhận dữ liệu cũ).
Hiện nay, AWS đã nâng cấp toàn bộ hệ thống lên Strong Read-After-Write Consistency:
- Ngay khi một lệnh
PUThoặcDELETEtrả về200 OK, bất kỳ lệnhGETtiếp theo trên toàn cầu đều ngay lập tức đọc được dữ liệu mới nhất mà không có bất kỳ độ trễ nào.
# kiến trúc direct-to-s3 presigned upload
Sai lầm kiến trúc phổ biến nhất của các lập trình viên nghiệp dư là tạo một endpoint: POST /api/v1/files/upload nhận MultipartFile.
# presigned URL hoạt động thế nào?
- Backend sử dụng cặp khóa IAM bí mật của mình để tính toán chữ ký số (AWS Signature Version 4 - SigV4) đính kèm vào URL.
- URL này cấp quyền
PUT(hoặcGET) tạm thời cho đúng một object key cụ thể với thời hạn sống ngắn (ví dụ: 15 phút). - Client dùng URL đó đẩy trực tiếp dữ liệu lên máy chủ S3. Máy chủ Backend của bạn không tốn một byte RAM hay băng thông nào cho quá trình truyền tải file!
# presigned multipart upload cho file lớn
Khi người dùng upload video dung lượng hàng trăm Megabytes hoặc Gigabytes qua mạng di động, mạng có thể bị chập chờn. Nếu upload một khối duy nhất, khi đứt mạng ở 99%, người dùng phải upload lại từ con số 0!
Multipart Upload chia nhỏ file thành nhiều phần (Parts, từ 5MB - 5GB mỗi part) và upload song song:
# triển khai thực chiến: Spring Boot 3 & AWS SDK v2
# cấu hình S3 client bất đồng bộ
package com.company.storage.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
@Configuration
public class AwsS3Configuration {
@Value("${aws.region:ap-southeast-1}")
private String awsRegion;
@Bean
public S3AsyncClient s3AsyncClient() {
// Tự động nạp Credentials từ IAM Role (EKS IRSA) mà không hardcode AccessKey!
return S3AsyncClient.builder()
.region(Region.of(awsRegion))
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
}
@Bean
public S3Presigner s3Presigner() {
return S3Presigner.builder()
.region(Region.of(awsRegion))
.credentialsProvider(DefaultCredentialsProvider.create())
.build();
}
}# service tạo presigned upload URL an toàn
package com.company.storage.service;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;
import java.time.Duration;
import java.util.UUID;
@Service
@RequiredArgsConstructor
public class S3PresignedUploadService {
private final S3Presigner s3Presigner;
@Value("${aws.s3.bucket}")
private String bucketName;
public PresignedUploadResponse generateSecureUploadUrl(String originalFileName, String contentType, long fileSize) {
// 1. Validate MIME type và kích thước tối đa (ví dụ max 20MB cho ảnh)
if (!contentType.startsWith("image/") && !contentType.equals("application/pdf")) {
throw new IllegalArgumentException("Định dạng file không được phép!");
}
// 2. Tạo Storage Key an toàn chống trùng lặp: prefixes/YYYY/MM/UUID.ext
String uniqueKey = String.format("documents/%s/%s", UUID.randomUUID(), originalFileName);
// 3. Khởi tạo PutObjectRequest với Content-Type ràng buộc
PutObjectRequest objectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(uniqueKey)
.contentType(contentType)
.build();
// 4. Ký Presigned Request với thời hạn sống 10 phút
PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10))
.putObjectRequest(objectRequest)
.build();
PresignedPutObjectRequest presigned = s3Presigner.presignPutObject(presignRequest);
return new PresignedUploadResponse(uniqueKey, presigned.url().toString(), 10 * 60);
}
}TIP
Tối ưu hóa Key Prefix cho High-Throughput: S3 tự động chia partition dựa trên Key Prefix. Nếu bạn ghi hàng chục nghìn file/giây, hãy tránh đặt prefix theo định dạng ngày tháng tuần tự như logs/2026-04-12/ mà hãy dùng hash prefix hoặc UUID phía trước (logs/a1b2-2026-04-12/) để phân tán đều tải trọng qua nhiều partition S3.
# bảo mật zero-trust & bẫy hiệu năng SSE-KMS
Khi lưu trữ dữ liệu nhạy cảm trên S3, bảo mật tại chỗ (Encryption at Rest) là yêu cầu bắt buộc:
CAUTION
Hiểm Họa Nghẽn KMS Quota: Nếu bạn lưu trữ hàng triệu objects với SSE-KMS mà quên bật tính năng S3 Bucket Keys, mỗi thao tác GET/PUT object sẽ bắn một request sang AWS KMS. Khi đạt lưu lượng cao, hệ thống sẽ bị chặn bởi lỗi KMS.ThrottlingException! Bật bucket_key_enabled = true sẽ gom hàng nghìn request dùng chung một data key ngắn hạn, hóa giải hoàn toàn điểm nghẽn này.
# tối ưu chi phí: cạm bẫy AbortIncompleteMultipartUpload
Trong quá trình sử dụng Multipart Upload, nếu client đang upload được một nửa thì bị mất mạng hoặc tắt trình duyệt, các Parts đã upload lên S3 sẽ không bao giờ tự biến mất!
- Các parts này nằm ẩn dưới dạng mảnh vỡ (in-flight parts) mà không hiển thị trên danh sách object thông thường.
- AWS vẫn tính phí lưu trữ cho toàn bộ các mảnh vỡ này mỗi tháng!
- Trong các dự án lớn, chi phí "rác" từ các upload dở dang có thể lên tới hàng nghìn USD mỗi tháng mà không ai hay biết.
# chính sách lifecycle bắt buộc
{
"Rules": [
{
"ID": "AutoAbortIncompleteUploadsAfter7Days",
"Status": "Enabled",
"Filter": {},
"AbortIncompleteMultipartUpload": {
"DaysAfterInitiation": 7
}
}
]
}Mọi bucket S3 trong doanh nghiệp bắt buộc phải có rule tự động hủy các Multipart Upload chưa hoàn thành sau 7 ngày để tránh rò rỉ chi phí ngân sách đám mây.
# tổng kết & checklist thực chiến
| Hạng mục kiểm tra | Tiêu chuẩn production bắt buộc | Hậu quả nếu bỏ qua |
|---|---|---|
| Upload Pattern | Luôn dùng Presigned URLs để Client đẩy thẳng lên S3. | Backend nghẽn băng thông, tràn RAM và sập server. |
| Large Files | Dùng Presigned Multipart Upload cho file > 100MB. | Người dùng phải upload lại từ đầu khi đứt mạng. |
| Credentials | Dùng IAM Roles for Service Accounts (IRSA) trên EKS. | Nguy cơ lộ AccessKey/SecretKey nếu hardcode vào source code. |
| Cost Hygiene | Bật Lifecycle Rule AbortIncompleteMultipartUpload (7 ngày). | Rò rỉ hàng nghìn USD chi phí lưu trữ mảnh vỡ ẩn. |
| KMS Throttling | Luôn bật S3 Bucket Keys khi sử dụng SSE-KMS. | Sập request do vượt quá hạn ngạch gọi AWS KMS. |
| Public Safety | Bật cờ Block All Public Access ở mức độ Account/Bucket. | Lộ lọt toàn bộ dữ liệu nội bộ ra ngoài Internet. |
Chỉ là những ghi chép cá nhân với hy vọng mang lại chút giá trị. Nếu thấy hữu ích, đừng ngại chia sẻ cho bạn bè & đồng nghiệp nhé!
Happy coding 😎 👍🏻 🚀 🔥.
On this page
- # bản chất lưu trữ: object vs block vs file storage
- # mô hình strong consistency của S3
- # kiến trúc direct-to-s3 presigned upload
- # presigned URL hoạt động thế nào?
- # presigned multipart upload cho file lớn
- # triển khai thực chiến: Spring Boot 3 & AWS SDK v2
- # cấu hình S3 client bất đồng bộ
- # service tạo presigned upload URL an toàn
- # bảo mật zero-trust & bẫy hiệu năng SSE-KMS
- # tối ưu chi phí: cạm bẫy AbortIncompleteMultipartUpload
- # chính sách lifecycle bắt buộc
- # tổng kết & checklist thực chiến