Trang chủTác giảLiên hệ

S3 Performance Design Patterns

By Trịnh Minh Hùng
Published in AWS
December 14, 2021
6 min read

Sau khi support mấy dự án, mình thấy có vài vấn đề liên quan đến việc các bạn trong team chưa sử dụng một vài tối ưu của AWS S3 service. Nên hôm nay mình cũng note lại ở đây coi như tổng hợp lại. Bài viết được lấy từ blog cá nhân của mình để đăng lên kipalog công ty. https://minhhungtrinh.github.io/2021/08/28/S3-Performance-Design-Patterns/

Tham khảo AWS Docs: https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html Trong link có khá nhiều giải pháp, nhưng mình sẽ chỉ nói tới một vài giải pháp trong docs mà mình đang làm việc thôi, các giải pháp khác đọc thì đọc nhiều rồi nhưng khi nào có cơ hội làm việc thực tế thì sẽ bổ sung tiếp vào bài này nhé.

1. Prefix

1.1. Prefix vs Folder

Cái này tưởng chừng là quá rõ nhưng 1 số ít vẫn đang nhầm lẫn prefix và folder, mình cũng muốn nhắc lại để liên quan đến mục 2. https://aws.amazon.com/premiumsupport/knowledge-center/s3-prefix-nested-folders-difference/ Với 1 object trong S3 bucket có path là BucketName/Project/WordFiles/123.txt:

  • Prefix sẽ là: BucketName/Project/WordFiles/. Tức là sẽ là path mà k chứa object key.
  • Folder sẽ là: BucketName, Project, WordFiles. Tức là các thành phần phân tách bằng /.

Prefix liên quan đến khái niệm Partitions của S3 hay còn gọi là phân vùng dữ liệu. Ví dụ bạn có 2 file: BucketName/Project1/123.txt và BucketName/Project2/123.txt thì bạn đã có 2 prefix tức là có 2 partitions đó. Việc partitions này không áp dụng cho object key, nó chỉ áp dụng cho prefix nhé. Ví dụ BucketName/Project/123.txt và BucketName/Project/345.txt thì chỉ có 1 prefix là BucketName/Project/ thôi nhé. Khái niệm partitions này sẽ ảnh hưởng tới việc bucket có khả năng request/s ở mức nào.

####1.2. Prefix with performance

Vấn đề đầu tiên có lẽ là việc mọi người dùng prefix. Mình thấy các dự án hoặc là không dùng prefix luôn hoặc là dùng thì chỉ có đúng 1 prefix. OK, nếu dự án bạn không có nhu cầu về việc scale traffic hoặc bạn không dùng hết giới hạn mặc định tới S3 bucket của bạn thì hãy bỏ qua nhé.

Nhiều bạn cũng hiểu nhầm việc sử dụng S3 service thì vì nó là service mà nên chả phải lo lắng gì nữa. Và luôn nghĩ nó là unlimited. Điều này đúng nhưng khoan đã nhé, khi bắt đầu có nhiều traffic tới S3 bucket thì bạn nên cân nhắc sử dụng như thế nào. Mình sẽ giải thích như sau:

Ứng dụng sẽ có thể có hàng ngàn transactions trên giây tới S3 bucket (upload hoặc retrieve). Tất nhiên là AWS S3 sẽ tự động scale các request rates nhưng nó cần thời gian và trong lúc đó bạn có khả năng nhận về 503 response. Ví dụ như sau: Ứng dụng có thể đạt được ít nhất 3500 PUT/COPY/POST/DELETE or 5,500 GET/HEAD trên giây trên prefix. Tức là nếu S3 bucket của bạn chỉ có 1 prefix thì request rate ban đầu sẽ như thế. Số prefix trên S3 bucket là không giới hạn. Và việc dùng prefix cũng giúp cho việc read/write song song => best performance. Vậy nếu bạn có 10 prefix thì bạn cứ x10 lên thôi. Tức là 55k GET/HEAD trên giây đó. Có 1 luồng song song (parallelizing reads). Tương tự với khả năng PUT/COPY/POST/DELETE nhé.

Vậy, bạn hãy cố gắng thiết kế ứng dụng của mọi người để tăng số lượng prefix lên nhiều nhất có thể nhé.

Tất nhiên trên thực tế thì mọi người còn kết hợp S3 với Cloudfront để GET Object nên vấn đề này không còn ảnh hưởng với việc GET nữa, còn về PUT/COPY/POST/DELETE nếu muốn cải thiện thì hãy sử dụng prefix nhé.

Có 1 suggest cho các bạn về việc đặt prefix, nhất là các dữ liệu liên quan đến logs. Thì hãy đặt prefix theo kiểu sequential date-based: /yyyy/mm/dd/object. Nó sẽ giúp bạn phân vùng dữ liệu hiệu quả hơn đó. Có thể 1 số hướng dẫn cũ bạn có thể thấy các recommend như tạo prefix với 1 chuỗi hash (ví dụ datetime hoặc random) cho các dữ liệu lấy thường xuyên. Nhưng bây giờ thay thế với sequential date-based nhé.

Một ví dụ nữa khiến bạn cần phân vùng dữ liêu (partition) với prefix. S3 có dữ liệu và bạn sử dụng Athena để SQL query dữ liệu trên S3 chẳng hạn. Việc phân vùng dữ liệu bằng sequential date-based thực sự hiệu quả, tất nhiên bạn có thể kết hợp thêm vài cách phần vùng khác như ID, Group. Nói chung nó sẽ giúp Athena không phải scan tất cả S3 mà chỉ scan prefix cần scan thôi. Mà cũng không chỉ riêng athena đâu, các Query engine khác cũng vậy.

2. Object key

Về cách đặt tên cho object. Quay về câu chuyển trước năm 2021, khi mà S3 chưa hỗ trợ strong consistency và chỉ hỗ trợ read after write consistency cho new object. Khi đó nếu bạn thực hiện Put overide 1 object key đã tồn tại, thì khả năng khi bạn get object về xem, có thể nó vẫn là object cũ. Căn nguyên thì là do việc S3 lưu dữ liệu trên nhiều Data center, việc chúng ta Put object cần thời gian đồng bộ giữa các data center, điều này gây ra khả năng vẫn lấy phải object cũ. Vậy nên giải pháp của mình là không thực hiện put object overide lên object cũ nữa mà sẽ tạo new object với keyname khác. Thường thì mình sẽ hash datetime với 1 cái key id nào đó để được object name duy nhất.

Từ 2021 thì AWS đã có Strong Consistency rồi nên bạn không cần phải lo lắng vấn đề này nữa.

3. Caching for Frequenly Access Content.

####3.1. Cloudfront Với các dữ liệu ở S3 mà bị truy cập thường xuyên thì hãy sử dụng caching nhé. Thực ra thì với tất cả các static files của Web application thì mình đều sử dụng Cloudfront để caching nó ở Edge location. Mình thấy nó thật sự hiệu quả về latency nhất là các Global App. Ngoài ra thì người sử dụng cũng không thể biết chính xác S3 bucket của bạn là gì vì không truy cập trực tiếp mà. Solution: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html

####3.2. Elasticache Redis Cũng là 1 giải pháp để giúp low latency là sử dụng in memory cache. Thay vì phải get object từ S3 thì chỉ việc get từ Cache database thôi. Logic của bạn sẽ xử lý nhanh hơn. Tất nhiên bạn cũng sẽ phải xử lý vấn đề liên quan đến consistency data. Nhưng theo mình thấy không chỉ liên quan đến S3 mà tất cả những gì liên quan đến data (như database, server) thì cache database là 1 giải pháp tốt để low latency và giảm đáng kể chi phí tài nguyên. Solution: https://aws.amazon.com/blogs/storage/turbocharge-amazon-s3-with-amazon-elasticache-for-redis/

###4. S3 Presign URL

Việc PUT, GET object bạn có thể thay thế bằng việc sử dụng 1 Presign URL nhé. GET object với Presign URL ưu điểm là bạn sẽ không cần public object và cung cấp 1 url có expire time PUT object với Presign URL thì ưu điểm cũng giống GET OBJECT, ngoài ra việc sử dụng giúp cho chúng ta upload file luôn từ Client Side 1 cách bảo mật. Từ đó giảm gánh nặng cho Server nữa. Mà việc upload từ client side lên thẳng S3 cũng giúp cho thời gian upload rất là nhanh, không phải qua 1 bước trung gian là server nữa. Cái này thì nhiều guide trên mạng lắm nên ae chịu khó search.

###5. PUT/GET Large file

####5.1. PUT Với các large file, chỗ này AWS suggest là file > 100MB, để tăng performance upload object với multiple parts upload nhé, sử dụng kết hợp presign url để upload từ client side cho hiệu quả, giảm gánh cho server. Guide: https://dev.to/traindex/multipart-upload-for-large-files-using-pre-signed-urls-aws-4hg4?fbclid=IwAR1oMQ05O7ico_v3yn37ZDhFAAXMlLPTCQGkiTSOOwFFXhobV62q7SLEGg4 Thấy có source Github cũng đầy đủ lắm: https://github.com/prestonlimlianjie/aws-s3-multipart-presigned-upload?fbclid=IwAR3IS6Eu-iV7i1nb36i6oI1aCnHPBHbV06V5GoS9my-3vPxVScZgMZwvnvs

Bổ sung là chỗ này thì các bạn nên áp dụng lifecycle management để xoá những file incomplete nhé. Thực tế thì sẽ có rất nhiều đó, nếu để lại thì sẽ là tốn chi phí.

####5.2 GET

Cũng tương tự như upload thôi, thì khi GET mọi người cũng có thể Parallel Download được. Ở đây mình nói tới việc download từ S3 về EC2 nhé. Thật may trong các SDK thì có support luôn Transfer Manager đó. Tất nhiên là nó hỗ trợ cả việc:

  • Parallel upload via multipart upload
  • Parallel download via byte-range fetches

Nhưng ở đây mình muốn tập chung nói về Parallel download via byte-range fetches . Vì multipart upload thì nên áp dụng với presign url để thao tác ở clientside hơn, nếu dùng cách này là phải thao tác với Server rồi. Link ở dưới là Java SDK. https://aws.amazon.com/blogs/developer/introducing-amazon-s3-transfer-manager-in-the-aws-sdk-for-java-2-x/

Kết

Cảm ơn mọi người đã đọc bài. Vui lòng đóng góp ý kiến giúp mình bổ sung và chỉnh sửa nội dung nhé. Mong là sẽ update thêm nội dung cho bài này => Tức là áp dụng được nốt mấy cái S3 Performance Design Patterns còn lại :D


Tags

AWS
Khóa học AWS cơ bản

Trịnh Minh Hùng

Related Posts

AWS SAP - Security - KMS & CloudHSM
December 14, 2021
12 min

S3 Performance Design Patterns

Published in AWS
December 14, 2021
6 min read

Sau khi support mấy dự án, mình thấy có vài vấn đề liên quan đến việc các bạn trong team chưa sử dụng một vài tối ưu của AWS S3 service. Nên hôm nay mình cũng note lại ở đây coi như tổng hợp lại. Bài viết được lấy từ blog cá nhân của mình để đăng lên kipalog công ty. https://minhhungtrinh.github.io/2021/08/28/S3-Performance-Design-Patterns/

Tham khảo AWS Docs: https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance.html Trong link có khá nhiều giải pháp, nhưng mình sẽ chỉ nói tới một vài giải pháp trong docs mà mình đang làm việc thôi, các giải pháp khác đọc thì đọc nhiều rồi nhưng khi nào có cơ hội làm việc thực tế thì sẽ bổ sung tiếp vào bài này nhé.

1. Prefix

1.1. Prefix vs Folder

Cái này tưởng chừng là quá rõ nhưng 1 số ít vẫn đang nhầm lẫn prefix và folder, mình cũng muốn nhắc lại để liên quan đến mục 2. https://aws.amazon.com/premiumsupport/knowledge-center/s3-prefix-nested-folders-difference/ Với 1 object trong S3 bucket có path là BucketName/Project/WordFiles/123.txt:

  • Prefix sẽ là: BucketName/Project/WordFiles/. Tức là sẽ là path mà k chứa object key.
  • Folder sẽ là: BucketName, Project, WordFiles. Tức là các thành phần phân tách bằng /.

Prefix liên quan đến khái niệm Partitions của S3 hay còn gọi là phân vùng dữ liệu. Ví dụ bạn có 2 file: BucketName/Project1/123.txt và BucketName/Project2/123.txt thì bạn đã có 2 prefix tức là có 2 partitions đó. Việc partitions này không áp dụng cho object key, nó chỉ áp dụng cho prefix nhé. Ví dụ BucketName/Project/123.txt và BucketName/Project/345.txt thì chỉ có 1 prefix là BucketName/Project/ thôi nhé. Khái niệm partitions này sẽ ảnh hưởng tới việc bucket có khả năng request/s ở mức nào.

####1.2. Prefix with performance

Vấn đề đầu tiên có lẽ là việc mọi người dùng prefix. Mình thấy các dự án hoặc là không dùng prefix luôn hoặc là dùng thì chỉ có đúng 1 prefix. OK, nếu dự án bạn không có nhu cầu về việc scale traffic hoặc bạn không dùng hết giới hạn mặc định tới S3 bucket của bạn thì hãy bỏ qua nhé.

Nhiều bạn cũng hiểu nhầm việc sử dụng S3 service thì vì nó là service mà nên chả phải lo lắng gì nữa. Và luôn nghĩ nó là unlimited. Điều này đúng nhưng khoan đã nhé, khi bắt đầu có nhiều traffic tới S3 bucket thì bạn nên cân nhắc sử dụng như thế nào. Mình sẽ giải thích như sau:

Ứng dụng sẽ có thể có hàng ngàn transactions trên giây tới S3 bucket (upload hoặc retrieve). Tất nhiên là AWS S3 sẽ tự động scale các request rates nhưng nó cần thời gian và trong lúc đó bạn có khả năng nhận về 503 response. Ví dụ như sau: Ứng dụng có thể đạt được ít nhất 3500 PUT/COPY/POST/DELETE or 5,500 GET/HEAD trên giây trên prefix. Tức là nếu S3 bucket của bạn chỉ có 1 prefix thì request rate ban đầu sẽ như thế. Số prefix trên S3 bucket là không giới hạn. Và việc dùng prefix cũng giúp cho việc read/write song song => best performance. Vậy nếu bạn có 10 prefix thì bạn cứ x10 lên thôi. Tức là 55k GET/HEAD trên giây đó. Có 1 luồng song song (parallelizing reads). Tương tự với khả năng PUT/COPY/POST/DELETE nhé.

Vậy, bạn hãy cố gắng thiết kế ứng dụng của mọi người để tăng số lượng prefix lên nhiều nhất có thể nhé.

Tất nhiên trên thực tế thì mọi người còn kết hợp S3 với Cloudfront để GET Object nên vấn đề này không còn ảnh hưởng với việc GET nữa, còn về PUT/COPY/POST/DELETE nếu muốn cải thiện thì hãy sử dụng prefix nhé.

Có 1 suggest cho các bạn về việc đặt prefix, nhất là các dữ liệu liên quan đến logs. Thì hãy đặt prefix theo kiểu sequential date-based: /yyyy/mm/dd/object. Nó sẽ giúp bạn phân vùng dữ liệu hiệu quả hơn đó. Có thể 1 số hướng dẫn cũ bạn có thể thấy các recommend như tạo prefix với 1 chuỗi hash (ví dụ datetime hoặc random) cho các dữ liệu lấy thường xuyên. Nhưng bây giờ thay thế với sequential date-based nhé.

Một ví dụ nữa khiến bạn cần phân vùng dữ liêu (partition) với prefix. S3 có dữ liệu và bạn sử dụng Athena để SQL query dữ liệu trên S3 chẳng hạn. Việc phân vùng dữ liệu bằng sequential date-based thực sự hiệu quả, tất nhiên bạn có thể kết hợp thêm vài cách phần vùng khác như ID, Group. Nói chung nó sẽ giúp Athena không phải scan tất cả S3 mà chỉ scan prefix cần scan thôi. Mà cũng không chỉ riêng athena đâu, các Query engine khác cũng vậy.

2. Object key

Về cách đặt tên cho object. Quay về câu chuyển trước năm 2021, khi mà S3 chưa hỗ trợ strong consistency và chỉ hỗ trợ read after write consistency cho new object. Khi đó nếu bạn thực hiện Put overide 1 object key đã tồn tại, thì khả năng khi bạn get object về xem, có thể nó vẫn là object cũ. Căn nguyên thì là do việc S3 lưu dữ liệu trên nhiều Data center, việc chúng ta Put object cần thời gian đồng bộ giữa các data center, điều này gây ra khả năng vẫn lấy phải object cũ. Vậy nên giải pháp của mình là không thực hiện put object overide lên object cũ nữa mà sẽ tạo new object với keyname khác. Thường thì mình sẽ hash datetime với 1 cái key id nào đó để được object name duy nhất.

Từ 2021 thì AWS đã có Strong Consistency rồi nên bạn không cần phải lo lắng vấn đề này nữa.

3. Caching for Frequenly Access Content.

####3.1. Cloudfront Với các dữ liệu ở S3 mà bị truy cập thường xuyên thì hãy sử dụng caching nhé. Thực ra thì với tất cả các static files của Web application thì mình đều sử dụng Cloudfront để caching nó ở Edge location. Mình thấy nó thật sự hiệu quả về latency nhất là các Global App. Ngoài ra thì người sử dụng cũng không thể biết chính xác S3 bucket của bạn là gì vì không truy cập trực tiếp mà. Solution: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Introduction.html

####3.2. Elasticache Redis Cũng là 1 giải pháp để giúp low latency là sử dụng in memory cache. Thay vì phải get object từ S3 thì chỉ việc get từ Cache database thôi. Logic của bạn sẽ xử lý nhanh hơn. Tất nhiên bạn cũng sẽ phải xử lý vấn đề liên quan đến consistency data. Nhưng theo mình thấy không chỉ liên quan đến S3 mà tất cả những gì liên quan đến data (như database, server) thì cache database là 1 giải pháp tốt để low latency và giảm đáng kể chi phí tài nguyên. Solution: https://aws.amazon.com/blogs/storage/turbocharge-amazon-s3-with-amazon-elasticache-for-redis/

###4. S3 Presign URL

Việc PUT, GET object bạn có thể thay thế bằng việc sử dụng 1 Presign URL nhé. GET object với Presign URL ưu điểm là bạn sẽ không cần public object và cung cấp 1 url có expire time PUT object với Presign URL thì ưu điểm cũng giống GET OBJECT, ngoài ra việc sử dụng giúp cho chúng ta upload file luôn từ Client Side 1 cách bảo mật. Từ đó giảm gánh nặng cho Server nữa. Mà việc upload từ client side lên thẳng S3 cũng giúp cho thời gian upload rất là nhanh, không phải qua 1 bước trung gian là server nữa. Cái này thì nhiều guide trên mạng lắm nên ae chịu khó search.

###5. PUT/GET Large file

####5.1. PUT Với các large file, chỗ này AWS suggest là file > 100MB, để tăng performance upload object với multiple parts upload nhé, sử dụng kết hợp presign url để upload từ client side cho hiệu quả, giảm gánh cho server. Guide: https://dev.to/traindex/multipart-upload-for-large-files-using-pre-signed-urls-aws-4hg4?fbclid=IwAR1oMQ05O7ico_v3yn37ZDhFAAXMlLPTCQGkiTSOOwFFXhobV62q7SLEGg4 Thấy có source Github cũng đầy đủ lắm: https://github.com/prestonlimlianjie/aws-s3-multipart-presigned-upload?fbclid=IwAR3IS6Eu-iV7i1nb36i6oI1aCnHPBHbV06V5GoS9my-3vPxVScZgMZwvnvs

Bổ sung là chỗ này thì các bạn nên áp dụng lifecycle management để xoá những file incomplete nhé. Thực tế thì sẽ có rất nhiều đó, nếu để lại thì sẽ là tốn chi phí.

####5.2 GET

Cũng tương tự như upload thôi, thì khi GET mọi người cũng có thể Parallel Download được. Ở đây mình nói tới việc download từ S3 về EC2 nhé. Thật may trong các SDK thì có support luôn Transfer Manager đó. Tất nhiên là nó hỗ trợ cả việc:

  • Parallel upload via multipart upload
  • Parallel download via byte-range fetches

Nhưng ở đây mình muốn tập chung nói về Parallel download via byte-range fetches . Vì multipart upload thì nên áp dụng với presign url để thao tác ở clientside hơn, nếu dùng cách này là phải thao tác với Server rồi. Link ở dưới là Java SDK. https://aws.amazon.com/blogs/developer/introducing-amazon-s3-transfer-manager-in-the-aws-sdk-for-java-2-x/

Kết

Cảm ơn mọi người đã đọc bài. Vui lòng đóng góp ý kiến giúp mình bổ sung và chỉnh sửa nội dung nhé. Mong là sẽ update thêm nội dung cho bài này => Tức là áp dụng được nốt mấy cái S3 Performance Design Patterns còn lại :D


Tags

© 2021, All Rights Reserved.

Quick Links

Liên hệ quảng cáoThông tinLiên hệ

Social Media