Giaosucan's blog - Chia sẻ kiến thức theo cách bá đạo

Ticker

20/recent/ticker-posts

Kiến trúc FAANG 4 - Giải pháp lưu trữ data của Discord

 


Ngày nay, ứng dụng chat rất nhiều từ Zalo, Telegram, Slack, MS Team đến Facebook messenger, Line, Whatsapp.. Mỗi công cụ lại được sử dụng với nhiều mục đích khác nhau, vì dụ như Slack hay MS Team được các công ty sử dụng cho công việc, Facebook messeger cho các em gái sống ảo, Telegram được giới checker ưa chuộng. Một ứng dụng mà game thủ rất thích xài đó là discord. Ứng dụng này được tạo ra giúp mọi người kết nối với nhau dễ dàng khi đang chơi game, giao diện đơn giản, đượt thiết kế tối ưu hiệu năng cho game thủ vì ko ăn RAM CPU

Do tiện lợi như vậy nên số lượng user của discord rất lớn

2021 Discord có 150 triệu user active, lúc cao điểm khoảng
8.2 triệu user online đồng thời
Năm 2017, số lượng messages 1 ngày là 40 triệu. Nhưng đến tháng 12 thì đã đạt 120 triệu messages 1 ngày. Hiện nay là 530 triệu message được gửi trong 1 ngày
19 triệu server hoạt động
 
Vậy discord làm thế nào để giải quyết bài toán trên?

Câu chuyện Discord

Cách tiếp cận của discord cũng giống như những startup của Sillion Valley. Đó là phải tạo ra sản phẩm thật nhanh để release ra thị trường. Do đó lúc đầu họ sẽ chọn những giải pháp đơn giản để dễ implement, code cho nhanh. Sau khi release, tùy vào tình hình của thị trường sẽ điều chỉnh giải pháp để đáp ứng được nhu cầu người dùng

Phiên bản đầu tiên của discord chỉ làm trong 2 tháng, mọi message được lực trên 1 single MongoDB relicase set . MongoDB là gì bạn có thể search trên Google , còn hiểu cách đọc ghi của Discord như thế này 


Discord ghi message vào một primary node , node này sẽ record lại những thay đổi vào operation logs, secondary node sẽ replicate log này và apply vào secondary node. Lúc đầu thì discord chỉ dùng 1 node duy nhất để làm việc này

Data lưu vào mongo dạng collection với 2 khóa chính là channel_id và created_at
ID này được gen như thế nào

Đối với mấy dev code app thông thường thì ID hay xài là kiểu GUID, Globally Unique Identifier. Tuy nhiên Discord muốn ID cần chứa thông tin. Do đó họ sử dụng Twitter's snowflake format để tạo ra ID. Hiểu này SnowFlex ID là một chuỗi 64 bit string để mô tả thông tin


  Lúc đầu dữ liệu nhỏ nên MongoDB chạy căng đét, nhưng khi dữ liệu lên tới 120T    /ngày thì mới lòi ra vấn đề là dữ liệu không fit đầy vào RAM 
  Vấn đề này được hiểu như thế nào
  Ví dụ thế này , giả sử index data của bạn vào khoảng 4.3 GB

> db.collection.totalIndexSize()
4617080000
  
Như vậy RAM của bạn phải có ít nhất là 4.3G để fit vừa lượng dữ liệu này, nếu  không thì mongo sẽ chuyển từ ram sang disk thì performance sẽ giảm đi nhiều. Tuy nhiên cái trên chỉ mới là 1 collection, còn trường hợp multi collection thì phải tính gộp lại

Với trường hợp của Discord thì 120T message/ngày thì thì ta cứ xcmnd là không bao giờ fit đủ RAM, nên discord bị chậm như rùa. Nếu là user khi bạn vào room rồi kéo scrollbar để view message sẽ thấy load chậm vcl. Nguyên nhân chính là như trên

Ngoài vấn đề trên thì discord có gặp nhiều vấn đề như sau

Việc read data thì randoom còn tỉ lệ đọc ghi ở discord là 50/50
Các loại private message ( kiểu anh A gửi cho em B, các group chat private, group ẩn) lên tới triệu message làm server quá tải
Public group thì gửi số lượng message khá lớn
Việc đọc message history ( kiểu một thanh niên vào group đi tìm message cách đây cả tháng) thì hoàn toàn random, giời mới biết

Chọn Database


Tóm lại với tính huống trên thì monodb không đáp ứng được, nên phải chọn giải pháp database khác theo tiêu chí

Dễ mở rộng, chịu lỗi tốt, không phải bảo trì nhiều và phải open source ( để nếu cần thì tự sửa customize lại cho nó dễ). Đặc biệt là phải uy tín, được nhiều ông to xài trước và cho review tốt. Mục đích là tránh hàng đểu công nghiệp rồi cuối cùng tiền mất tật mang.

NoSQL thì Google BigTable và AWS DynamoDB là 2 anh dẫn đầu. Tuy nhiên nó không opensource và phải phụ thuộc vào GG và AWS. Cuối cùng thì Cassdandra được lựa chọn. Đây cũng là DB NOSQL được Netflix Facebook sử dụng trước đó rồi nên bao uy tín. Platform này được Facebook phát triển và sau đó thì Open source cho Apache 
Cái hay của Cassdandra là kiến trúc của nó ko có Primary node như MongoDB, mà là kiến trúc theo kiểu vòng ring, distrubited, multi nodes, masterless architect , các node chức năng giống như nhau
Muốn mở rộng thì chỉ việc thêm node là xong

Data modelling 

Discord lưu message vào Cassadanra theo kiểu KKV (Key Key Value)

Data được chia theo các partion, đánh định danh là partion key , trong mỗi partion sẽ gồm nhiều row, mỗi row id gọi là Clustering Key, gộp 2 key này vào thì mới thành Primary Key , cuối cùng là Value 

Primary Key = Partition Key + [Clustering Columns]

Ví dụ về một message của Discord 
<code>CREATE TABLE messages (
  channel_id bigint,
  message_id bigint,
  author_id bigint,
  content text,
  PRIMARY KEY (channel_id, message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);</code>
channel_id là partion key, message_id là cluster key, còn lại là data value
Một partition size của Cassandar trong khoảng 10 đến 100MB để đảm báo tối đa hiệu năng. Nếu partition size quá lớn sẽ gặp GC Pressuregarbage collector pressure
Khái niệm này liên quan đến việc quản lý bộ nhớ trong coding. Ngày xưa khi code C/C++ bạn phải tự quản lý việc cấp phát giải phóng bộ nhớ. Nhất là thời kỳ code máy PenIII ram 128MB thì việc tối ưu bộ nhớ cứ gọi là vãi đạn. Ngày nay thì JAVA, .NET có GC, nó sẽ tự  tìm kiếm những object ko còn dùng nữa mà free nó đi, tránh được memory leak. Thế nhưng GC chạy quá tải thì nó không gánh nổi việc thu dọn bộ nhớ nữa, performance đi xuống 

Do đó khi import data từ MongoDB sang nếu gặp data vượt quá 100M sẽ bị warning, nên phải package data của Discord thành những bucket dưới 100MB đặt vừa vào các partition
Hiểu đơn giản thế này
Bạn có 1 channel discord có vài triệu message từ thời ông bà cố, mỗi lần kéo kéo scrollbar để xem message là discord thực hiện query message trong DB. Discord sẽ lấy những message trong một khoảng thời gian nhất định (tầm 10 ngày) đóng gói thành bucket, vừa đủ 100MB rồi import vào cassdanra partition , mỗi bucket được định danh bucket_id
ví dụ từ 1/1/2023 - 10/1/2023 là bucket 1, từ 10 đến 20/1 là bucket 2

Giờ message trong cassdanra sẽ thành

CREATE TABLE messages (
   channel_id bigint,
   bucket int,
   message_id bigint,
   author_id bigint,
   content text,
   PRIMARY KEY ((channel_id, bucket), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);

Và search theo khóa (channel_id, bucket_id) là ra dc mesasge theo thời gian

Đấy là cách discord lưu hàng tỉ message bằng Cassadanra. Tuy nhiên đó là năm 2017, giờ là 2023, số lượng message của discord giờ là trillion messages. Thế là đến lượt cassdanra gặp vấn đề. 

Vậy vấn đề là gì cách giải quyết ra sao, đón đọc phần sau

Đăng nhận xét

2 Nhận xét