0

Bí Mật Của Discord: Làm Thế Nào Để Lưu Trữ Hàng Tỷ Tin Nhắn Mỗi Ngày?

Discord là một trong những nền tảng chat lớn nhất thế giới với hàng trăm triệu người dùng. Mỗi ngày, có hàng tỷ tin nhắn mới được tạo ra. Nếu lưu vào MySQL hay PostgreSQL, database sẽ sập chỉ sau vài tiếng.

Hành trình tiến hóa hệ thống lưu trữ của Discord là một Masterclass về thiết kế hệ thống phân tán. Hãy cùng xem họ đã giải bài toán lưu trữ khổng lồ này như thế nào.

1. Điểm xuất phát: MongoDB

Vào năm 2015, Discord bắt đầu với một single Replica Set của MongoDB. MongoDB rất tuyệt để ra mắt sản phẩm nhanh chóng, nhưng khi hệ thống đạt mốc 120 triệu tin nhắn/ngày, MongoDB bắt đầu có dấu hiệu đuối sức. Dữ liệu to lên, RAM không chứa nổi các Index, hệ thống I/O đĩa cứng bắt đầu chạm đỉnh. Họ nhận ra mình cần một Datastore sinh ra chuyên biệt cho việc GHI dữ liệu khổng lồ với độ trễ thấp.

2. Bước nhảy vọt: Chuyển sang Apache Cassandra

Discord chuyển toàn bộ tin nhắn sang Cassandra. Cassandra là một NoSQL Database phân tán kiểu Masterless (không có nút chính). Bạn có thể ghi vào bất kỳ nút nào trong cụm, và nó sẽ tự đồng bộ dữ liệu.

Cassandra giải quyết bài toán của Discord nhờ cấu trúc LSM Tree (Log-Structured Merge-Tree): Khác với B-Tree của RDBMS, LSM Tree ghi dữ liệu tuần tự (Sequential Write) cực kỳ nhanh. Nó gần như chỉ nối dữ liệu vào cuối file, không cần update các cây chỉ mục lằng nhằng. Tốc độ Ghi (Write) của Cassandra là vô đối.

Phân vùng dữ liệu (Partitioning) thông minh Cassandra bắt bạn phải chọn một Partition Key. Discord đã chọn Channel_ID. Điều này có nghĩa là: Toàn bộ tin nhắn của một Channel sẽ được gom chung vào một Node vật lý. Khi người dùng vào một kênh chat và kéo lên để đọc lịch sử, Cassandra chỉ cần nhảy vào đúng 1 Node đó và đọc ra một mạch các tin nhắn. Tốc độ Đọc (Read) vô cùng tuyệt vời.

3. Quái vật mang tên "Hot Partition"

Nhưng đời không như mơ. Discord có những Server cực kỳ lớn (như Server Genshin Impact với hơn 1 triệu thành viên). Khi có sự kiện, hàng trăm ngàn người cùng chat vào một Channel. Vì Discord đang gom chung tin nhắn của 1 Channel vào 1 Node (do dùng Channel_ID làm Partition Key), Node vật lý chứa Channel đó bị "bội thực" (Hot Partition). RAM và CPU của Node đó vọt lên 100%, trong khi các Node khác trong cụm Cassandra ngồi chơi xơi nước.

4. Cuộc đại tu lần 2: ScyllaDB và Rust

Để giải quyết tình trạng này, vào năm 2022, Discord đã thực hiện một quyết định cực kỳ táo bạo: Chuyển đổi hoàn toàn từ Cassandra sang ScyllaDB.

ScyllaDB là một clone của Cassandra nhưng được viết bằng C++ (Cassandra viết bằng Java). Tại sao lại đổi?

Hạn chế Garbage Collection của Java: Với hàng tỷ tin nhắn, bộ gom rác (GC) của Java trong Cassandra thỉnh thoảng sẽ "Stop the world" (dừng mọi tiến trình để dọn rác) mất vài giây. Vài giây đối với hệ thống real-time chat là một thảm họa (Spike Latency). Kiến trúc Thread-per-core của ScyllaDB: ScyllaDB chia nhỏ tài nguyên tới tận từng nhân CPU. Mỗi nhân xử lý một tập dữ liệu riêng, không có Lock, không có Context Switch. Tốc độ của ScyllaDB nhanh hơn Cassandra từ 5 đến 10 lần. Đồng thời, Discord viết lại các service xử lý tin nhắn bằng Rust (thay vì Golang/Python trước đó). Rust mang lại hiệu năng ngang ngửa C/C++ nhưng đảm bảo an toàn bộ nhớ, không có Garbage Collection.

5. Xử lý Hot Partition bằng Data Service Layer

Thay vì để các API chọc thẳng vào DB, Discord tạo ra một lớp trung gian (viết bằng Rust). Khi một Channel có dấu hiệu bị "Hot" (lượng tin nhắn tăng đột biến), lớp trung gian này sẽ gom các tin nhắn lại (Batching) trong khoảng vài mili-giây trước khi ném nguyên một cục lớn vào DB. Điều này giảm hàng ngàn kết nối I/O xuống đĩa thành chỉ vài chục kết nối, cứu sống hoàn toàn cụm Database.

Bài học rút ra

  • Hiểu bản chất dữ liệu: Tin nhắn Chat là dạng dữ liệu "Ghi cực nhiều, Đọc cực nhanh ở các tin mới nhất, và hiếm khi sửa/xóa". LSM Tree (như Cassandra/ScyllaDB) sinh ra để làm việc này.
  • Hot Partition là kẻ thù số 1 của hệ thống phân tán: Luôn phải tính đến trường hợp một User/Channel nào đó phình to bất thường để có cơ chế Batching hoặc Sharding hợp lý.
  • C++ và Rust vẫn là vị vua hiệu năng: Khi ứng dụng đạt đến quy mô hàng tỷ request, các ngôn ngữ có Garbage Collection (Java, Go) sẽ gặp vấn đề về Latency Spikes.

xem thêm các bài viết hay hơn tại https://www.tramcode.io.vn/bai-viet/bi-mat-cua-discord-lam-the-nao-de-luu-tru-hang-ty-tin-nhan-moi-ngay-241


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí