8 điểm bởi GN⁺ 2026-01-30 | 3 bình luận | Chia sẻ qua WhatsApp
  • PgDog, proxy mở rộng cho PostgreSQL, đã áp dụng binding trực tiếp Rust thay cho tuần tự hóa Protobuf để tăng hiệu năng phân tích cú pháp SQL
  • Thay thế kiến trúc dựa trên Protobuf trước đây bằng chuyển đổi trực tiếp C–Rust (bindgen + wrapper do Claude tạo), giúp phân tích cú pháp nhanh hơn 5,45 lần, deparse nhanh hơn 9,64 lần
  • Điểm nghẽn hiệu năng được phát hiện ở hàm pg_query_parse_protobuf, và sau khi thử cache, nhóm đã thay đổi cấu trúc để có cải thiện mang tính căn bản
  • Tận dụng Claude LLM để tự động tạo 6.000 dòng mã chuyển đổi Rust–C, áp dụng cho các hàm chính như parse, deparse, fingerprint, scan
  • Nhờ tối ưu hóa này, mức sử dụng CPU và độ trễ của PgDog giảm xuống, hiệu quả với vai trò proxy mở rộng theo chiều ngang cho PostgreSQL được cải thiện rõ rệt

PgDog và giới hạn của Protobuf

  • PgDog là một proxy để mở rộng PostgreSQL, bên trong sử dụng libpg_query để phân tích cú pháp truy vấn SQL
    • Được viết bằng Rust, và trước đây giao tiếp với thư viện C thông qua tuần tự hóa/giải tuần tự hóa Protobuf
  • Protobuf tuy nhanh, nhưng cách dùng binding trực tiếp còn nhanh hơn
    • Nhóm PgDog đã fork pg_query.rs, loại bỏ Protobuf và triển khai binding trực tiếp C–Rust
    • Kết quả là phân tích truy vấn nhanh hơn 5,45 lần, còn deparse nhanh hơn 9,64 lần

Kết quả benchmark

  • Benchmark có thể được tái hiện từ kho fork của PgDog
    • pg_query::parse (Protobuf): 613 QPS
    • pg_query::parse_raw (C–Rust trực tiếp): 3357 QPS
    • pg_query::deparse (Protobuf): 759 QPS
    • pg_query::deparse_raw (Rust–C trực tiếp): 7319 QPS

Phân tích điểm nghẽn hiệu năng và thử nghiệm cache

  • Kết quả phân tích thời gian sử dụng CPU bằng profiler samply cho thấy hàm pg_query_parse_protobuf chính là điểm nghẽn
  • Nhóm đã thử cải thiện một phần bằng cache
    • Sử dụng cache hashmap dựa trên thuật toán LRU, lưu AST với khóa là văn bản truy vấn
    • Có thể tái sử dụng trong trường hợp dùng prepared statement
  • Tuy nhiên, một số ORM tạo ra hàng nghìn truy vấn duy nhất, hoặc driver PostgreSQL cũ không hỗ trợ prepared statement, khiến hiệu quả cache thấp

Loại bỏ Protobuf với sự hỗ trợ của LLM

  • Nhóm PgDog đã tận dụng Claude LLM để tạo ra binding Rust sau khi loại bỏ Protobuf
    • AI hoạt động hiệu quả trong phạm vi công việc rõ ràng và có thể kiểm chứng
  • Dựa trên đặc tả Protobuf của libpg_query, Claude đã ánh xạ cấu trúc C sang cấu trúc Rust
    • Sau 2 ngày lặp đi lặp lại, hoàn thành 6.000 dòng mã Rust đệ quy
  • Áp dụng cho các hàm parse, deparse, fingerprint, scan và xác nhận hiệu năng tăng 25% theo pgbench

Chi tiết cấu trúc triển khai

  • Việc chuyển đổi giữa Rust và C dùng các hàm unsafe để ánh xạ trực tiếp cấu trúc
    • Truyền cấu trúc C vào API Postgres để tạo AST, rồi chuyển đổi đệ quy sang Rust
  • Mỗi node AST được xử lý bằng hàm convert_node, ánh xạ hàng trăm token của cú pháp SQL
    • Có các hàm chuyển đổi riêng cho từng loại node như SELECT, INSERT v.v.
  • Kết quả chuyển đổi tái sử dụng cấu trúc Protobuf hiện có (protobuf::ParseResult), nên có thể kiểm chứng bằng so sánh từng byte khi test
  • Thuật toán đệ quy ít cấp phát bộ nhớ hơn và tận dụng cache CPU tốt hơn, nên nhanh hơn cách triển khai dựa trên vòng lặp
    • Cách làm dựa trên vòng lặp lại chậm hơn do cấp phát bộ nhớ không cần thiết và tra cứu hashmap

Kết luận

  • Giảm overhead của parser Postgres đã giúp PgDog cắt giảm đồng thời độ trễ, bộ nhớ và mức sử dụng CPU
  • Nhờ tối ưu hóa này, PgDog tiếp tục phát triển thành proxy mở rộng PostgreSQL nhanh hơn và có chi phí vận hành thấp hơn
  • PgDog đang tuyển kỹ sư để cùng xây dựng mở rộng theo chiều ngang (next iteration) cho PostgreSQL

3 bình luận

 
a1eng0 2026-01-31

Có thể là tôi đang hiểu sai bài gốc, nhưng riêng các bài viết liên quan đến Rust thì dường như thường viết theo kiểu như thể nó nhanh hơn là vì "do là Rust", bỏ qua bản chất vấn đề.

Điểm chính của bài này là hiệu năng được cải thiện nhờ giảm phần overhead tuần tự hóa không cần thiết cơ mà.

Giờ nhìn lại thì có vẻ đây cũng không hẳn là một bài ca ngợi Rust như vậy, hay là do những bài khác đã khiến tôi hình thành sẵn ấn tượng tiêu cực rồi nhỉ?

 
xguru 2026-01-31

Tôi cũng thấy tiêu đề gốc của bài này, khác với nội dung thực tế, nghiêng về Rust quá mức nên trông như đang nhấn mạnh vào việc cải thiện hiệu năng, vì vậy tôi đã sửa nhẹ lại.
Có vẻ các bài viết về Rust khá thường có xu hướng như vậy, nên có lẽ cần đọc với một chút chọn lọc.

 
GN⁺ 2026-01-30
Ý kiến trên Hacker News
  • Tiêu đề khiến người ta tưởng như Rust mang lại mức cải thiện hiệu năng gấp 5 lần, nhưng điều mỉa mai là thực ra ban đầu nó lại làm chậm đi
    Vấn đề là phần mềm viết bằng Rust phải dùng libpg_query viết bằng C, nhưng không thể kết nối trực tiếp nên đã dùng binding Rust–C dựa trên Protobuf
    Cách này quá chậm, nên cuối cùng họ đã nhờ LLM hỗ trợ để viết lại một binding mới kém tính di động hơn nhưng được tối ưu tốt hơn nhiều
    Nếu ngay từ đầu viết bằng C thì đã không cần bước chuyển đổi đó. Nói cách khác, tiêu đề chính xác hơn phải là “giảm tổn thất hiệu năng do dùng Rust”
    Tầng chuyển đổi mang lại tính di động và bảo mật, nhưng rốt cuộc việc lặp đi lặp lại sao chép · chuyển đổi · tuần tự hóa cũng là một trong những nguyên nhân khiến ứng dụng chậm đi

    • Không phải Rust chậm, mà vấn đề nằm ở thiết kế kém hiệu quả của thư viện bên ngoài
      Trong Rust, việc gọi thư viện C rất dễ và cũng đã có nhiều wrapper an toàn
      Cấu trúc đặt Protobuf ở giữa thì gần như chưa từng thấy, và đó mới là nút thắt cổ chai
      Tiêu đề có vẻ chỉ là một kiểu meme “viết lại bằng Rust” để câu click
    • Nói rằng nếu viết bằng C thì sẽ nhanh hơn là không công bằng
      Thư viện gốc vốn có thiết kế sai khi lặp đi lặp lại serialize/deserialize, và điểm cốt lõi là họ đã loại bỏ điều đó
      Tiêu đề đúng hơn phải là “thay Protobuf bằng API thông thường thì nhanh hơn 5 lần”
    • Tò mò vì sao họ không dùng FFI ngay từ đầu
      Binding C trong Rust là loại dễ nhất, và nếu API không quá lớn thì khá đơn giản
      Tôi nghĩ Protobuf là công cụ không phù hợp để trao đổi dữ liệu trong bộ nhớ
    • Nếu đã dùng LLM để tối ưu, thì tôi tự hỏi sao không dùng nó để port hoàn toàn thư viện C sang Rust luôn
      Có vẻ trong tương lai nhờ LLM mà việc port sang nhiều ngôn ngữ khác nhau sẽ bùng nổ
    • Đặt Protobuf giữa Rust và Postgres đúng là mức ác mộng hiệu năng. Thật ngạc nhiên khi một thư viện như vậy lại được ưa chuộng
  • Tiêu đề hơi gây hiểu lầm
    Về bản chất thì chỉ là “loại bỏ bước tuần tự hóa Protobuf thì nhanh hơn”

    • Protobuf mang lại khả năng tương thích phiên bản mà sao chép đơn thuần không làm được
      Nó cho phép client và server cập nhật độc lập mà vẫn hoạt động, đồng thời giúp giao tiếp đa ngôn ngữ dễ dàng hơn
      Trong các hệ thống lớn, kiểu linh hoạt này rất quan trọng
    • Việc tuần tự hóa Protobuf chỉ chậm hơn sao chép bộ nhớ đơn thuần 5 lần thực ra còn tạo cảm giác nhanh hơn tôi tưởng
    • memcpy hay mmap nhanh hơn nhiều, nhưng phía Rust thường ngại những cách làm thiếu an toàn như vậy
    • Trong trường hợp này có lẽ nên dùng định dạng zero-copy đã được chuẩn hóa như Arrow. Nó tự xử lý các vấn đề padding theo ngôn ngữ và kiểm tra bảo mật
  • Nguyên nhân chậm có thể không phải do Rust, mà do dùng Protobuf như định dạng lưu trữ tổng quát
    Cuối cùng, điểm cốt lõi vẫn là đơn giản hóa theo đúng mục đích cụ thể

    • Tiêu đề “thay Protobuf bằng triển khai tối ưu native” chắc hẳn sẽ kém thu hút hơn
      Việc đưa Rust vào tiêu đề có vẻ là một lựa chọn để câu click
    • Tiêu đề bài viết gây tranh cãi, nhưng phần nội dung cũng có ý thức về điều đó
    • Thực tế gần như không liên quan đến Rust, nhưng nếu không có Rust thì có lẽ đã không lên được trang chính
  • Tác giả gốc của pg_query giải thích bối cảnh
    Ban đầu tại pganalyze, nó được dùng để parse truy vấn Postgres nhằm tìm tham chiếu bảng, đồng thời rewrite và format truy vấn
    Lúc đầu dùng JSON, nhưng sau đó đã chuyển sang Protobuf để dễ cung cấp binding có type safety cho nhiều ngôn ngữ khác nhau (Ruby, Go, Rust, Python...)
    Với những ngôn ngữ như Rust thì FFI tốt hơn, nhưng với các ngôn ngữ khác, gánh nặng bảo trì lại lớn hơn
    Ông ủng hộ nỗ lực của Lev, và trong tương lai dự định sẽ bổ sung các hàm cho phép truy cập trực tiếp libpg_query qua FFI
    Tuy vậy, trong những trường hợp hiệu năng không quan trọng thì Protobuf vẫn là lựa chọn thuận tiện hơn

  • Cụm “nhanh hơn 5 lần” làm tôi nhớ tới câu đùa “nhanh vô hạn” của Cap’n Proto

    • Cap’n Proto do chính tác giả của Protobuf tạo ra, và dùng cách diễn đạt đó vì cấu trúc không cần parse
    • Nhưng khi dùng thực tế thì Cap’n Proto lại khá tệ về tính tiện dụng
  • Tiêu đề có hơi cường điệu, nhưng công việc thực tế thì rất ấn tượng
    Họ không loại bỏ hoàn toàn Protobuf mà là tối ưu cách sử dụng
    Câu kiểu “đổi sang X thì nhanh hơn 5 lần” thường có nghĩa là “đã sửa một phần triển khai vốn rất tệ”
    Bài học cốt lõi là

    1. serialize/deserialize rất dễ trở thành nút thắt cổ chai ẩn
    2. Phần triển khai mặc định đa phần không được tối ưu cho mọi tình huống
    3. Cần profiling để xác định chính xác nút thắt cổ chai
      Rust FFI cũng có overhead, nên thành quả thực sự không nằm ở ngôn ngữ mà ở việc thiết kế lại luồng dữ liệu và nỗ lực tối ưu hóa
  • FlatBuffers nhanh hơn, nhưng lý do người ta dùng Protobuf là vì nó được các tập đoàn lớn bảo trì

    • Nhưng FlatBuffers cũng do Google bảo trì
      Cuối cùng, suy nghĩ kiểu “do Google làm nên an toàn” thật ra không có cơ sở
    • Tôi cũng từng có trải nghiệm thất bại khi đưa code lên nền tảng Google (code.google.com) rồi nó chết yểu
      Nếu chỉ cần một cấu trúc zero-copy đơn giản với chia sẻ bộ nhớ và trường phiên bản thì tôi thấy không có lý do gì phải dùng Protobuf
    • Google đến giờ vẫn chưa công khai tối ưu zero-copy cho trường chuỗi
  • Tôi thấy hiệu năng Protobuf ở mức như một trò đùa
    Nên dùng định dạng zero-copy nơi việc tuần tự hóa gần như miễn phí
    Ví dụ, Lite³ mà tôi tạo ra nhanh hơn FlatBuffers 242 lần

    • Nhưng thư viện đó chỉ xuất hiện sau tháng 11 năm 2025
      Lý do dùng Protobuf là vì vô số nguyên nhân thực tế như hệ sinh thái, schema, công cụ theo từng ngôn ngữ, v.v.
  • Thực ra vấn đề không nằm ở Rust hay Protobuf, mà ở triển khai tuần tự hóa kém hiệu quả của tầng trừu tượng PostgreSQL
    pgdog đã loại bỏ tầng đó và truyền dữ liệu trực tiếp qua C API
    Gỡ bỏ tính năng không cần thiết thì dĩ nhiên sẽ nhanh hơn
    Nhưng với một số người, tuần tự hóa vẫn là điều cần thiết
    Với họ, tiêu đề kiểu “hãy chuyển sang Rust” là một thông điệp sai lệch
    Rốt cuộc, trong đa số trường hợp thì JSON là đủ, còn nếu thực sự cần nhanh hơn nữa thì nên tránh bản thân việc tuần tự hóa

  • Đây là một so sánh không công bằng
    Dùng giao thức tuần tự hóa cho giao tiếp IPC đương nhiên sẽ có overhead
    Đây đúng là kiểu trường hợp phù hợp với câu nói “nhanh hơn 20% là cải thiện, nhanh hơn 10 lần nghĩa là ngay từ đầu đã làm sai”