Thời buổi hiện nay, chắc hẳn ai chúng ta cũng có ít nhất 1 tài khoản facebook. Một chức năng cơ bản nhất của nó là cho phép tải lên hình ảnh. Trong bài viết chúng ta sẽ cùng đi tìm hiểu cách mà facebook lưu trữ đống ảnh khổng lồ đó.
Mở đầu
Năm 2009 số lượng ảnh người dùng tải lên Facebook là 15 triệu càng ngày. Ở thời điểm đó mỗi bức ảnh được tải lên họ sẽ tạo ra các kích thước khác nhau và lưu trữ, tương đương với 60 tỷ bức ảnh và 1.5PB dung lượng lưu trữ. Con số này vẫn tiếp tục tăng trưởng 220 triệu mỗi tuần tương ứng khoảng 25TB tiêu thụ. Lúc cao điểm có thể lên tới 550.000 bức ảnh mỗi giây. Những con số này đặt ra một thách thức cho cơ sở hạ tầng lưu trư ảnh của Facebook.
NFS photo infrastructure (Cơ sở hạ tầng ảnh NFS)
Cơ sở hạ tầng cũ của họ gồm một số tầng:
- Tầng upload: Nhận ảnh tải lên của người dùng và lưu xuống tầng lưu trữ NFS.
- Tầng phụ vụ các yêu cầu HTTP của ảnh và phục vụ chúng từ tầng lưu trữ NFS.
- Tầng lưu trữ NFS được xây dựng trên các thiết bị lưu trữ thương mại.
Vì mỗi ảnh được lưu trữ trong tệp riêng của nó, do đó sinh ra một lượng metadata khổng lồ trên tầng lưu trữ. Lượng dữ liệu metadata vượt quá khả năng lưu vào bộ nhớ đệm của tầng lưu trữ NFS, dẫn nhiều nhiều thao tác I/O cho mỗi yêu cầu đọc hoặc tải lên ảnh. Toàn bộ cơ sở hạ tầng tầng bị ảnh hưởng do chi phí metadata cao của tầng lưu trữ NFS. Đây là một trong những lý do tại sao Facebook phải phụ thuộc vào CDN để phục vụ ảnh.
Hai giải pháp tối ưu tạm thời được đưa ra để giảm thiểu vấn đề:
- Cache: Tầng máy chủ lưu các ảnh profile nhỏ vào bộ nhớ đệm.
- NFS file handle cache: Được triển khai trên tầng dịch vụ ảnh mục đích loại bỏ một số chi phí metadata của tầng lưu trữ NFS.
Đấy chỉ là những cách xử lý tạm thời họ cần một cơ sở hạ tầng mới đáp ứng được những con số khổng lồ phía trên.
Haystack Photo Infrastructure (Cơ sở hạ tầng ảnh Haystack)
Cơ sở hạ tầng ảnh mới hợp nhất tầng phục vụ ảnh và tầng lưu trữ thành một tầng vật lý. Nó triển khai một máy chủ ảnh dựa trên yêu cầu HTTP lưu trữ ảnh trong object store chung có tên là Haystack.
Yêu cầu chính là loại bỏ mọi chi phí metadata dư thừa cho các hoạt động đọc ảnh, sao cho mỗi thao tác đọc I/O chỉ đọc dữ liệu ảnh thực tế (thay vì hệ thống tệp metadata).
Haystack có thể chia thành các lớp như sau:
- HTTP server
- Photo Store
- Haystack Object Store
- Filesystem
- Storage
Filesystem
Haystack object stores được triển khai trên các tệp được lưu trữ trong một hệ thống tệp duy nhất được tạo trên ổ đĩa 10TB. Các yêu cầu đọc ảnh dẫn đến các cuộc gọi hệ thống read() ở offsets đã biết trong các tệp này, nhưng để thực hiện các lần đọc, hệ thống tệp trước tiên phải định vị dữ liệu trên ổ đĩa vật lý thực tế (sẽ nói cụ thể ở phía dưới). Và họ chọn hệ thống tệp XFS.
XFS filesystem, nó tên là XFS luôn chứ không phải viết tắt của từ gì cả, là một journaling filesystem 64 bit với hiệu năng cao. XFS đã xuất hiện ở các Linux kernel vào năm 2001, cho tới tháng 06/2014 nó đã được hỗ trợ bởi hầu hết các Linux distribution, một vài trong số đó sử dụng XFS như là filesystem mặc định.
Một đặc tính quan trọng của XFS đó là khả năng bảo đảm tốc độ truy xuất dữ liệu cho các ứng dụng (Guaranteed Rate I/O), cho phép các ứng dụng duy trì được tốc độ truy xuất dữ liệu trên disk, rất quan trọng đối với các hệ thống phân phối dịch vụ video có độ phân giải cao hoặc các ứng dụng xử lý thông tin vệ tinh đòi hỏi duy trì ổn định tốc độ thao tác dữ liệu.
Haystack Object Store
Haystack là một cấu trúc log đơn giản (append-only) nghĩa là chỉ cho phép chèn thêm vào. Object store sẽ chứa các needle nó đại diện cho các đối tượng được lưu trữ.
Haystack gồm 2 file: 1 file để lưu trữ needle và 1 file index.
8kb đầu tiên của haystack store sẽ là của superblock. Sau đó là các needle gồm các phần header, data và footer.
- Header Magic Number + Footer Magic Number: Giúp định vị needle tiếp theo trong quá trình phục hồi file index.
- Cookie: Giúp client bảo mật chống tấn công brute force.
- Key: Giành 64-bit lưu giữ key
- Alternate key: Giành 32-bit lưu trữ alternate key.
- Flags: Đánh giấu needle đã bị xóa hay chưa. Tại vì đã nói ở trên haystack chỉ chèn thêm nên muốn xóa thì ta dựa vào thằng này.
- Size: Kích thước của data.
- Data checksum: Kiểm tra checksum của needle.
- Padding: Tổng kích thước của needle được biểu diễn thành 8 bytes.
Một kim được xác định duy nhất bởi <Offset, Key, Alternate Key, Cookie> của nó, trong đó offset là vị trí của needle trong Haystack store. Haystack không đưa ra bất kỳ hạn chế nào đối với giá trị của các khóa và có thể có các khóa trùng lặp.
Các thành phần của file index:
Mỗi bản ghi trong file index tương ứng mỗi needle trong tệp lưu trữ haystack và thứ tự các bản ghi index phải khớp với thứ tự của needle liên kết trong tệp lưu trữ haystack. Tệp index cung cấp các metadata tối thiểu đề định vị 1 needle trong haystack store.
Lưu ý là tệp index không quan trọng =))). Vì nó có thể được xây dựng lại từ tệp lưu trữ haystack nếu cần. Mục đích của nó là tải nhanh needle vào bộ nhớ mà đỡ phải lặp qua cái file haystack siêu to kia một cách nhanh chóng, vì file index thường nhỏ hơn 1% kích thước file lưu trữ kia.
Thao tác viết vào Haystack
Khi tệp được tải lên, hệ thống sẽ tạo ra needle tương ứng và nối đồng bộ vào tệp haystack store. Sau khi được lưu thành công nó sẽ lưu các metadata cần thiết của needle vào file index. Vì tệp index không quan trọng nên các bản ghi sẽ được ghi bất đồng bộ làm tăng hiệu suất. Trong trường hợp xảy ra sự cố, quy trình hồi phục sẽ loại bỏ các needle lỗi. Haystack không cho phép ghi đè lên vị trí needle hiện có. Để cập nhật ta sẽ ghi chèn 1 needle mới với cùng 1 bộ <Key, Alternate Key, Cookie> với thằng needle cũ, Sau đó khi đọc nó sẽ lấy thằng có vị trí lớn nhất.
Thao tác đọc từ Haystack
Truyền các tham số <Offset, Key, Alternate Key, Cookie>, Haystack sau đó sẽ thêm header, footer và đọc toàn bộ needle từ tệp. Thao tác đọc chỉ thành công khi các tham số, checksum đều khớp và flags biểu hiện tệp chưa xóa.
Thao tác xóa trong Haystack
Nó đánh giấu needle trong haystack là đã bị xóa bằng cách đặt bit “đã xóa” trong trường flags cảu neeld. Tuy nhiên, bản ghi index được liên kết không được sửa theo bất kì cách nào nên ứng dụng có thể kết thúc bằng việc tham chiếu đến 1 needle đã xóa. Nếu thao tác đọc thấy cờ “đã xóa” và thao tác thành công với một lỗi thích hợp. Khoảng trống đã xóa của needle sẽ không được lấy lại theo bất kì cách nào.
Tổng kết
- Thay vì lưu các file tải lên thành các file riêng lẻ và tránh việc đọc file cần đọc cả metadata, thì họ tách riêng dữ liệu ảnh và metadata ra riêng và lưu vào tệp haystack store dưới dạng nhị phân.
- Việc ghi vào haystack nó sẽ lưu vị trí bắt đầu chính là size của tệp haystack store và vị trí kết thúc bằng size file haystack + size needle.
- Việc đọc thì sẽ dựa vào vị trí bắt đầu và vị trí kết thúc để lấy ra needle. Như phía trên tất cả các thành phần đều đã có size trừ data. Vậy muốn lấy data thì chỉ cần dựa vào needle size trừ khi các mấy thằng đã biết size.
- Từ bài viết này các bác hoàn toàn có thể viết cho mình 1 haystack nho nhỏ để nghịch rùi. Các bác chỉ cần chọn 1 ngôn ngữ có hỗ trợ các hàm thao tác file với byte là triển được rồi (Nếu bác nào code golang thì có hàm readAt có thể dùng để đọc, còn write thì lại dễ)
Tham khảo
Needle in a haystack: efficient storage of billions of photos
Nguồn: viblo.asia