- vừa được xem lúc

Thiết kế đơn giản một hệ thống ảnh như Instagram

0 0 1

Người đăng: tecklens

Theo Viblo Asia

Tập hợp các bài viết về thiết kế hệ thống phân tán hay nhất

Instagram(IG) cho phép người dùng tải lên và chia sẻ hình ảnh và video của họ với người dùng khác. Người dùng có thể chọn chia sẻ với phạm vi (scope) công khai hoặc riêng tư. Bất kỳ nội dung được chia sẻ công khai nào đểu có thể được bất kỳ người dùng nào khác xem (thường ưu tiên theo vị trí địa lý), trong khi nội dung được chia sẻ riêng tư chỉ được một nhóm người cụ thể truy cập.

Chúng tôi dự định thiết kế một phiên bản IG đơn giản hơn, nơi người dùng có thể chia sẻ ảnh và cũng có thể theo dõi những người dùng khác.

Yêu cầu và mục tiêu của hệ thống


Yêu cầu chức năng

  1. Người dùng có thể tải lên/ tải xuống/ xem ảnh
  2. Người dùng có thể thực hiện tìm kiếm dựa trên tiêu đề ảnh/video
  3. Người dùng có thể theo dõi người dùng khác
  4. Hệ thống sẽ tạo ra newsfeed bao gồm những bức ảnh hàng đầu từ tất cả những người mà họ theo dõi

Yêu cầu phi chức năng

  1. Dịch vụ cần có tính khả dụng cao
  2. Độ trễ chấp nhận được là 200ms cho một "Hệ thống tạo newsfeed - News Feed Generation"
  3. Hệ thống phải có độ tin cậy cao; tỉ lệ mất ảnh/ video phải rất thấp.

Ước tính khả năng và ràng buộc của hệ thống


Hệ thống này có yêu cầu đọc nhiều hơn ghi, vì vậy chúng tôi sẽ tập trung vào việc xây dựng một hệ thống có thể truy xuất ảnh nhanh chóng.

  • Giả sử chúng ta có tổng cộng 500 triệu người dùng, với 1 triệu người dùng hoạt động hằng ngày.

  • 2 triệu ảnh mới mỗi ngày, 23 ảnh mới mỗi giây.

  • Kích thước tệp hình ảnh trung bình ~ 200KB

  • Vậy chúng ta sẽ có dung lượng hình ảnh cần thiết cho một ngày là

    2M * 200KB ~ 400GB

  • Tổng dung lượng trong 5 năm sẽ là:

    400GB * 365 * 5 = 730TB

Thiết kế hệ thống nâng cao


Ở cấp độ nâng cao, chúng ta cần hỗ trợ hai tình huống: tải ảnh lên và xem/ tìm kiếm ảnh.

Chúng ta cần máy chủ lưu trữ object(S3) để lưu trữ ảnh và một số máy chủ DB để lưu trữ thông tin siêu dữ liệu về ảnh.

Thiết kế cơ sở dữ liệu

Thiết kế CSDL sẽ giúp hiểu luồng dữ liệu giữa các thành phần khác nhau và định hướng trước phân vùng dữ liệu.

Về cơ bản, chúng ta cần lưu trữ những dữ liệu chính: người dùng, hình ảnh/ video của họ, những người theo dõi họ.

Table Photo

Photo
photo_id: bigint(pk)
user_id: int
photo_path: varchar(256)
photo_latitude: int
photo_longitude: int
user_latitude: int
user_longitude: int
created_at: int

Table User

User
user_id: int (pk)
name: varchar(25)
email: varchar(32)
dob: datetime
created_at: datetime
last_login: datetime

Table UserFollow

UserFollow
follower_id: int (pk)
following_id: int (pk)

Chúng ta có thể sử dụng RDBMS như MySQL vì chúng ta cần các truy vấn quan hệ. Nhưng cơ sở dữ liệu quan hệ đi kèm với những thách thức của chúng, đặc biệt là khi mở rộng quy mô. Vì vây, chúng ta có thể lưu trữ lược đồ trong kho dữ liệu NoSQL phân tán (distributed) theo column như https://github.com/apache/cassandra.

Tất cả siêu dữ liệu hình ảnh có thể chuyển đến một bảng trong đó keyphoto_idvalue sẽ là một đối tượng chứa các chi tiết liên quan đến hình ảnh. Cassandra và hầu hết các kho lưu trữ key-value duy trì một số lượng bản sao nhất định để cung cấp độ tin cậy. Ngoài ra, trong các kho lưu trữ dữ liệu này, các lệnh xóa không được áp dụng ngay lập tức, dữ liệu được giữ lại trong vài ngày để hỗ trợ khôi phục xóa, trước khi bị xóa vĩnh viễn.

Chúng ta có thể lưu trữ hình ảnh của người dùng dưới dạng hệ thống lưu trữ tệp phân tán như https://vi.wikipedia.org/wiki/Apache_Hadoop hoặc https://en.wikipedia.org/wiki/Amazon_S3.

Ước tính kích thước dữ liệu


Hãy ước tính chúng ta sẽ cần bao nhiêu dung lượng lưu trữ trong 5 năm tới.

User

Giả sử rằng mỗi int và datetime là 4 bytes, bigint là 8 bytes, mỗi hàng trong bảng user sẽ có:

user_id(4 bytes) + name(25 bytes) + email(32 bytes) + dob(4 bytes) + created_at(4 bytes) + last_login(4 bytes) = 73 bytes

Như vậy, chúng ta sẽ có tổng dung lượng với 500 triệu người dùng:

500 triệu * 73 bytes ~ 36,5 GB

Photo

Mỗi hàng(record) trong bảng photo sẽ có:

photo_id(8 bytes) + user_id(4 bytes) + photo_path(256 bytes) + photo_latitude(4 bytes) + photo_longtitude(4 bytes) + user_latitude(4 bytes) + user_longtitude(4 bytes) + created_at(4 bytes) = 288 bytes

Hệ thống sẽ nhận được 2 triệu bức ảnh mỗi ngày, vì vậy trong một ngày hệ thống cần:

2M * 288 bytes ~ 0,57 GB Với 5 năm dữ liệu chúng ta cần:
0,57 GB * 365 * 5 ~ 1 TB

UserFollow

Mỗi hàng có 8 bytes dữ liệu. Giả sử trung bình mỗi người dùng theo dõi 500 người dùng, chúng ta sẽ cần 1,82TB dung lượng lưu trữ cho bảng `user_follow

8 bytes * 500 followers * 500M users ~ 1,82TB

Tổng dung lượng cần thiết cho các bảng dữ liệu trong 5 năm sẽ là:

36,5 GB + 1 TB + 1,82 TB ~ 2,8 TB

Thiết kế các thành phần hệ thống

Việc tải ảnh lên (hoặc ghi) có thể chậm vì chúng phải được lưu vào đĩa, trong khi việc đọc sẽ nhanh hơn, đặc biệt nếu chúng được phục vụ từ bộ nhớ đệm.

Người dùng tải ảnh lên có thể sử dụng tất cả các kết nối khả dụng vì quá trình tải lên chậm, nghĩa là không thể phục vụ các yêu cầu đọc nếu hệ thống bận xử lý các yêu cầu ghi.

Chúng ta nên nhớ rằng tất cả các máy chủ web đều có giới hạn kết nối. Nếu chúng ta cho rằng một máy chủ web có thể có tối đa 500 kết nối đồng thời, thì có không thể có hơn 500 lần xử lý đọc/ghi đồng thời. Để xử lý tình trạng tắc nghẽn này, chúng ta có thể chia các api đọc và ghi thành các dịch vụ riêng biệt (CQRS), server chuyên dụng cho các api đọc và các máy chủ khác nhau cho các api ghi/tải lên để đảm bảo không chiếm dụng hệ thống.

Ngoài ra, việc tách biệt api đọc và ghi sẽ cho phép chúng ta mở rộng quy mô và tối ưu hóa từng hoạt động một cách độc lập.

Độ tin cậy và dự phòng

Việc mất tập tin là một sự độc hại đối với hệ thống này. Nó có thể làm rất nhiều người dùng rời bỏ hệ thống.

Chúng tôi sẽ lưu trữ nhiều bản sao của mỗi tệp để nếu một máy chủ lưu trữ bị hỏng, thì vẫn có thể lấy lại một bản sao trên một máy chủ lưu trữ khác.

Nguyên tắc này cũng áp dụng cho phần còn lại của hệ thống. Nếu chúng ta muốn tính khả dụng cao, chúng ta cần có nhiều bản sao của các dịch vụ đang chạy, để nếu một vài dịch vụ ngừng hoạt động, hệ thống vẫn khả dụng.

Nếu có hai phiên bản của cùng một dịch vụ đang chạy trên môi trường sản xuất, khi một phiên bản bị lỗi, hệ thống có thể chuyển đổi dự phòng sang phiên bản còn lại đang hoạt động. Quá trình chuyển đổi dự phòng có thể diễn ra tự động hoặc thực hiện thủ công.

Phân chia dữ liệu (Data Sharding)

  1. Phân vùng dựa trên user_id

Chúng ta có thể phân chia (shard) dựa trên user_id, để có thể giữ tất cả hình ảnh của người dùng trên cùng một phân vùng. Nếu một phân vùng DB là 1TB, chúng ta cần 3 phân vùng để lưu trữ 2,8 TB dữ liệu. Giả sử để có hiệu suất và khả năng mở rộng tốt hơn, chúng ta có thể tạo 10 phân vùng.

Chúng ta sẽ tìm số phân vùng bằng các thực hiện user_id % 10 (hash) và lưu dữ liệu ở phân vùng đó. Để xác định duy nhất từng ảnh trong hệ thống, chúng ta có thể thêm số phân vùng vào mỗi photo_id.

Chúng ta tạo photo_id như thế nào? Mỗi phân vùng DB có thể có trình tự tăng tự động riêng cho photo_id và vì chúng ta sẽ thêm shard_id vào mỗi photo_id nên nó sẽ trở thành duy nhất trên toàn bộ hệ thống.

Các vấn đề đối với cách tiếp cận này:

  • Chúng ta sẽ xử lý những người dùng nổi tiếng như thế nào? Những người nổi tiếng trên IG có rất nhiều người theo dõi, nghĩa là nhiều người sẽ nhìn thấy bất kỳ bức ảnh nào mà họ tải lên.
  • Một số người dùng sẽ có nhiều ảnh hơn những người khác, do đó dữ liệu sẽ được phân bổ không đều giữa các vùng.
  • Việc lưu trữ tất cả ảnh của người dùng trên một phân vùng có thể gây ra các vấn đề như không khả dụng nếu phân vùng đó ngừng hoạt động hoặc có độ trễ cao hơn nếu phân vùng đó đang phục vụ lượng tải lớn.
  1. Phân vùng dựa trên photo_id

Nếu chúng ta tạo ra photo_id duy nhất trước, sau đó tìm số phân vùng bằng cách dùng hàm hash photo_id % 10, các vấn đề trên sẽ được giải quyết. Chúng ta sẽ không cần phải thêm shard_id vào photo_id vì photo_id sẽ tự nó là duy nhất trên toàn bộ hệ thống.

Làm thế nào để tạo photo_id? Chúng ta có thể dành riêng một phiên bản DB để tạo ID tự động tăng. Nếu photo_id của chúng ta có thể vừa 8 bytes, chúng ta có thể định nghĩa một bảng chỉ chứa trường ID 8 bytes này. Vì vậy, bất cứ khi nào chúng ta muốn thêm ảnh, thì chỉ cần chèn một hàng mới vào bảng photo và lấy ID đó làm photo_id cho ảnh mới.

DB tạo khóa cho ảnh sẽ là bottleneck duy nhất

Một giải pháp thay thế cho vấn đề này là xác định hai DB như vậy:

  • một tạo ra các ID có số chẵn
  • một tạo ra các ID có số lẻ
KeyGeneratingServer1:
auto-increment-increment = 2
auto-increment-offset = 1 KeyGeneratingServer2:
auto-increment-increment = 2
auto-increment-offset = 2

Sau đó chúng ta có thể đặt một bộ cân bằng tải trước cả 2 DB để đảm bảo sự sẵn sàng của hệ thống.

Ngoài ra, chúng ta có thể có một dịch vụ tạo khóa độc lập (KGS) tạo ra chuỗi sáu chữ cái ngẫu nhiên trước và lưu trữ chúng trong cơ sở dữ liệu key-DB.

Xếp hạng và tạo Newsfeed

Chúng tôi cần lấy những bức ảnh mới nhất và phổ biến nhất của những người mà người dùng theo dõi.

  • Hãy lấy danh sách những người mà người dùng theo dõi và lấy thông tin siêu dữ liệu của 100 bức ảnh mới nhất cho mỗi người.
  • Gửi tất cả ảnh cho thuật toán xếp hạng để xác định 100 ảnh hàng đầu (dựa vào mức độ mới, mức độ giống nhau, v.v).
  • Trả lại cho người dùng dưới dạng nguồn cấp tin tức.

Để nâng cao hiệu quả, chúng ta có thể tạo trước Newsfeed và lưu trữ trong một bảng riêng.

Tạo trước nguồn cấp tin tức:

  • Dành riêng các máy chủ liên tục tạo nguồn cấp tin tức của người dùng và lưu trữ chúng trong một bảng newsfeed. Khi bất kỳ người dùng nào cần ảnh mới nhất, chúng tôi chỉ cần truy vấn bảng này.
  • Khi máy chủ cần tạo lại, trước tiên chúng sẽ truy vấn bảng newsfeed để tìm nguồn cung mới nhất được tạo. Sau đó, dữ liệu tin tức mới sẽ được tạo từ thời điểm đó trở đi.

Chúng tôi gửi nội dung newsfeed tới người dùng như thế nào?

  1. Pull: Máy khách kéo nội dung từ máy chủ theo định kỳ hoặc thủ công Nó có các vấn đề:
  • Dữ liệu mới không hiển thị cho đến khi khách hàng gửi api yêu cầu.
  • Hầu hết các api lấy dữ liệu sẽ dẫn đến phản hồi trống nếu không có dữ liệu. (Làm người dùng cuối khó chịu)
  1. Push: Máy chủ đẩy dữ liệu mới đến người dùng ngay khi có. Người dùng yêu cầu long poll (giữ kết nối dài) với máy chủ. Một vấn đề có thể xảy ra là người dùng theo dõi nhiều người hoặc người dùng nổi tiếng có hàng triệu người theo dõi; máy chủ sẽ phải ẩy nhiều bản cập nhật khá thường xuyên, gây nên quá tải cho máy chủ.
  2. Hybrid (Hỗn hợp):
  • Chúng ta có thể áp dụng phương pháp kết hợp của hai cách tiếp cận. Người duùng có nhiều người theo dõi sẽ sử dụng mô hình pull. Hệ thống sẽ chỉ đẩy (push) dữ liệu đến những người dùng có < 1000 người theo dõi.
  • Máy chủ đẩy các bản cập nhật tới tất cả ngời dùng với tần suất nhất định và cho phép những người dùng có nhiều bản cập nhật thường xuyên lấy dữ liệu.

Bộ nhớ đệm và cân bằng tải

Dịch vụ sẽ cần một hệ thống phân phối ảnh quy mô lớn để phục vụ người dùng trên toàn cầu.

Hệ thống sẽ đưa nội dung đến gần người dùng hơn bằng cách sử dụng một số lượng lớn các CDN phân bổ theo địa lý.

Hệ thống cũng có thể có bộ nhớ đệm cho máy chủ siêu dữ liệu để lưu trữ đệm các hàng DB lưu lượng cao. Memcache có thể lưu trữ dữ liệu và máy chủ ứng dụng trước khi gọi vào DB thực.

Làm thế nào chúng ta có thể xây dựng một bộ nhớ đệm thông minh hơn? Nếu chúng ta áp dụng quy tắc 80%-20% lượt đọc ảnh tạo ra 80% lưu lượng truy cập. Điều này có nghĩa là một số ảnh nhất định rất phổ biến đến mức phần lớn mọi người xem/ tìm kiếm chúng. Do đó, chúng ta có thể thử lưu trữ đệm 20% khối lượng đọc ảnh và siêu dữ liệu hằng ngày.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Mô hình quan hệ - thực thể (Entity – Relationship Model)

Mô hình quan hệ thực thể (Entity Relationship model - E-R) được CHEN giới thiệu vào năm 1976 là một mô hình được sử dụng rộng rãi trong các bản thiết kế cơ sở dữ liệu ở mức khái niệm, được xây dựng dựa trên việc nhận thức thế giới thực thông qua tập các đối tượng được gọi là các thực thể và các mối

0 0 133

- vừa được xem lúc

[Embulk #1] Công cụ giúp giảm nỗi đau chuyển đổi dữ liệu

Embulk là gì. Embulk là một công cụ open source có chức năng cơ bản là load các record từ database này và import sang database khác.

0 0 58

- vừa được xem lúc

Window Functions trong MySQL, Nâng cao và cực kì hữu dụng (Phần II).

Chào mọi người, lại là mình đây, ở phần trước mình đã giới thiệu với mọi người về Window Functions Phần I. Nếu chưa rõ nó là gì thì mọi người nên đọc lại trước nha, để nắm được định nghĩa và các key words, tránh mắt chữ O mồm chứ A vì phần này mình chủ yếu sẽ thực hành với các Window Functions.

0 0 110

- vừa được xem lúc

Window Functions trong MySQL, Nâng cao và cực kì hữu dụng (Phần I).

Chào mọi người, mình mới tìm hiểu đc topic Window Functions cá nhân mình cảm thấy khá là hay và mình đánh giá nó là phần nâng cao. Vì ít người biết nên Window Functions thấy rất ít khi sử dụng, thay vì đó là những câu subquery dài dằng dặc như tin nhắn nhắn cho crush, và người khác đọc hiểu được câu

0 0 981

- vừa được xem lúc

Disable và Enable trigger trong Oracle

Origin post: https://www.tranthanhdeveloper.com/2020/12/disable-va-enable-trigger-trong-oracle.html.

0 0 41

- vừa được xem lúc

Lưu trữ dữ liệu với Data Store

. Data Store là một trong những componet của bộ thư viện Android JetPack, nó là một sự lựa chọn hoàn hảo để thay thế cho SharedPreferences để lưu trữ dữ liệu đơn giản dưới dạng key-value. Chúng ta cùng làm một so sánh nhỏ để thấy sự tối ưu của Data Store với SharedPreferences nhé.

0 0 74