Tóm tắt:
- OpenAI đang xử lý hàng triệu QPS và 800 triệu người dùng với một Primary duy nhất và khoảng 50 Read Replica (Azure Flexible Server).
- Để phân tán tải ghi, các workload có thể sharding đã được chuyển sang Azure Cosmos DB, đồng thời tối ưu ghi ở cấp ứng dụng bằng các kỹ thuật như
Lazy Write. - Việc đưa PgBouncer vào sử dụng đã rút ngắn độ trễ kết nối từ 50ms xuống 5ms, đồng thời triển khai cơ chế Cache Locking để ngăn bão cache miss.
- Độ ổn định được đảm bảo thông qua việc loại bỏ các truy vấn join phức tạp, áp dụng timeout thay đổi schema nghiêm ngặt dưới 5 giây, và cô lập workload theo mức ưu tiên lưu lượng.
Tóm tắt chi tiết:
1. Bối cảnh và hiện trạng kiến trúc
Lưu lượng PostgreSQL của OpenAI đã tăng hơn 10 lần trong một năm qua, hiện xử lý 800 triệu người dùng và hàng triệu QPS (số truy vấn mỗi giây). Đáng chú ý, quy mô này đang được vận hành với cấu trúc gồm một Primary duy nhất và khoảng 50 Read Replica phân tán trên toàn cầu. Để ngăn các vết nứt trong thiết kế ban đầu bộc lộ rõ hơn, OpenAI đã thực hiện tối ưu hóa lớn ở cả tầng hạ tầng lẫn tầng ứng dụng.
2. Giải quyết các nút thắt chính và chiến lược tối ưu hóa
-
Phân tán tải ghi (Write):
- Cấu trúc single-writer của PostgreSQL có giới hạn về khả năng mở rộng, và còn gặp vấn đề write amplification do MVCC (kiểm soát tương tranh đa phiên bản).
- Giải pháp là chuyển các workload ghi cường độ cao có thể phân mảnh ngang (sharding) sang các hệ thống như Azure Cosmos DB.
- Với PostgreSQL hiện có, OpenAI cấm tạo bảng mới, đồng thời giảm tải cho Primary bằng cách sửa lỗi ứng dụng và áp dụng Lazy Write (trì hoãn ghi để làm phẳng các đợt tăng vọt lưu lượng).
-
Tối ưu truy vấn và quản lý ORM:
- Trước đây từng có trường hợp một truy vấn join 12 bảng gây ra sự cố (SEV), nên họ tránh các phép multi-join phức tạp và tách phần đó sang logic ứng dụng.
- Các truy vấn kém hiệu quả do ORM sinh ra được rà soát liên tục, đồng thời dùng thiết lập
idle_in_transaction_session_timeoutđể các truy vấn nhàn rỗi không chặn Autovacuum.
-
Connection pooling (PgBouncer):
- Để tránh giới hạn kết nối của Azure PostgreSQL (5.000 kết nối) và tình trạng bùng nổ kết nối, OpenAI triển khai PgBouncer làm lớp proxy.
- Họ sử dụng chế độ pooling theo transaction/statement để tăng khả năng tái sử dụng kết nối, và rút ngắn thời gian kết nối trung bình từ 50ms xuống 5ms.
-
Ngăn cache miss (Cache Locking):
- Để tránh vấn đề 'Thundering Herd', khi cache hết hạn khiến rất nhiều request đồng thời đổ vào DB, OpenAI đã đưa vào cơ chế cache lock (leasing).
- Khi xảy ra cache miss với một key cụ thể, chỉ một request duy nhất được cấp quyền truy cập DB (Lock) để làm mới dữ liệu, còn các request còn lại sẽ chờ, qua đó chặn tải đổ dồn lên DB.
3. Độ ổn định và chính sách vận hành
- Giảm thiểu điểm lỗi đơn (SPOF): Ngay cả khi Primary gặp sự cố, các yêu cầu đọc vẫn được xử lý qua Replica để hạ mức độ ảnh hưởng; Primary cũng duy trì Hot Standby trong chế độ HA để bảo đảm failover nhanh.
- Cô lập workload: Để ngăn vấn đề 'Noisy Neighbor', lưu lượng được định tuyến tới các instance tách biệt theo mức độ quan trọng (Low/High Priority).
- Quản lý schema nghiêm ngặt: Các thay đổi gây ra full table rewrite bị cấm, và khi thay đổi schema sẽ áp dụng timeout nghiêm ngặt 5 giây để ngăn dịch vụ bị chậm.
4. Kế hoạch sắp tới (The Road Ahead)
Dù kiến trúc hiện tại đã đạt đủ khả năng mở rộng, OpenAI đang thử nghiệm Cascading Replication để mở rộng thêm Read Replica trong tương lai, thay vì để Primary gửi WAL tới toàn bộ Replica; theo đó, các Replica trung gian sẽ chuyển tiếp WAL xuống cấp dưới. Về dài hạn, họ cũng đang cân nhắc sharding ngay trong PostgreSQL.
2 bình luận
Tóm tắt thảo luận trên Hacker News: https://news.ycombinator.com/item?id=46725300
Sức mạnh của một instance đơn lẻ: Có nhiều phản ứng cho rằng việc xử lý lưu lượng ở quy mô 800 triệu người dùng bằng một Postgres (ghi) duy nhất mà không cần sharding một lần nữa khẳng định rằng “máy chủ DB khổng lồ đúng là bạn của chúng ta (Big DB servers are your friend)” và cho thấy tính hiệu quả của vertical scaling.
Sự mỉa mai của sharding so với refactoring: Về đoạn trong bài viết nói rằng “họ không chọn sharding vì việc refactor ứng dụng hiện có quá phức tạp”, đã có những lời đùa châm biếm lẫn phê bình rằng “thật mỉa mai khi một công ty bán AI hỗ trợ lập trình lại nói họ không làm được vì refactor quá khó”. (Mặt khác, cũng có ý kiến bênh vực rằng đây là lựa chọn hợp lý nếu xét đến độ phức tạp vận hành và chi phí di chuyển mà sharding mang lại.)
Sự tiếc nuối về chiều sâu kỹ thuật: Vì bài viết chủ yếu xoay quanh những nội dung khá chung chung như caching, connection pooling, v.v., nên cũng có một số ý kiến phê bình cho rằng thiếu chi tiết kỹ thuật cụ thể và “giống một bài viết mang tính quảng bá hơn”.
Thảo luận liên quan đến Rust: Trong phần bình luận, ngoài nội dung chính của bài viết, còn có những trao đổi kỹ thuật đi sâu hơn khi mọi người chia sẻ cách tận dụng kiểm tra tại thời điểm biên dịch của Rust để ngăn chặn tận gốc vấn đề
Idle Transaction.Cá nhân tôi thấy những điểm như áp dụng cấu trúc kiểu Cascading Replication hoặc vượt qua giới hạn kỹ thuật bằng vận hành là rất thú vị. Về phần này, tôi đã ghi lại suy nghĩ của mình dài hơn một chút trên Facebook. https://www.facebook.com/share/p/1Kp8V917bL/