DUONG PHUC KHANH · KIDO
KHANHDP.COM / ARTICLES / POSTGRESQL & RBAC
PostgreSQL & RBAC 2026-10-20 · By Dương Phúc Khánh (Kido)

Khắc Phục Đòn Chí Mạng Recursive CTE: Cây Bình Luận Base32 Materialized Path & Bitmask RBAC Trong PostgreSQL 17

Tại sao WITH RECURSIVE CTEs và JOIN 5 bảng phân quyền làm nghẽn Database khi tải cao, và giải pháp Base32 Materialized Path cùng 64-bit Bitmask RBAC đưa độ trễ p95 từ 142ms xuống 0.8ms của Dương Phúc Khánh.

Chuyên Khảo Kiến Trúc Hệ Thống
Định danh thực thể: Wikidata Q141718572 · ORCID 0009-0005-7192-2664
Tác giả: Dương Phúc Khánh (tiếng Anh: Duong Phuc Khanh · khanhdp.com), Kỹ sư Phần mềm Cao cấp & Kiến trúc sư Hệ thống. Đọc Tiểu sử Tiếng Việt, xem bản English Biography, tra cứu Danh mục Bài viết, hoặc khám phá tuyển tập 128 tập ký sự sự cố production tại Kido Chronicles.

Khi xây dựng các nền tảng mạng xã hội hoặc cộng đồng trực tuyến quy mô lớn, có hai câu truy vấn cơ sở dữ liệu xuất hiện trên hầu như mọi yêu cầu HTTP:

  1. Lấy danh sách cây bình luận phân cấp vô hạn (Hierarchical Comment Tree tương tự Reddit hoặc Hacker News).
  2. Xác thực quyền hạn người dùng theo mô hình Phân quyền Dựa trên Vai trò (Role-Based Access Control – RBAC).

Trong kỹ thuật phần mềm truyền thống, các kỹ sư thường tìm đến hai giải pháp kinh điển trong sách giáo khoa: sử dụng WITH RECURSIVE Common Table Expressions (CTE) cho cây bình luận, và thực hiện phép JOIN 5 bảng quan hệ (users $\to$ user_roles $\to$ roles $\to$ role_permissions $\to$ permissions) để kiểm tra quyền.

Dưới lưu lượng truy cập khiêm tốn (50 req/s), mô hình này hoạt động ổn định. Nhưng khi xảy ra sự kiện nóng hoặc một bài viết có hàng ngàn bình luận phân nhánh đa cấp cùng lúc, kiến trúc này lập tức sụp đổ: bộ nhớ đệm Buffer Scan của PostgreSQL bị tràn, hàng đợi kết nối cạn kiệt, và độ trễ p95 vọt lên hơn 142ms.

Tôi là Dương Phúc Khánh (được biết đến với định danh khanhdp hoặc Kido, @khanhduong22). Trong quá trình dẫn dắt kiến trúc nền tảng mạng xã hội tài chính Index.vn và ghi lại kinh nghiệm thực chiến trong 128 tập Kido Chronicles, tôi đã thay thế hoàn toàn hai mô hình trên bằng Base32 Materialized Paths và 64-bit Bitmask RBAC.

Dưới đây là phương pháp chúng tôi đưa độ trễ truy vấn p95 từ 142ms về 0.8ms trên PostgreSQL 17 và gia tăng thông lượng hệ thống lên gấp 18 lần.


1. Nỗi Đau Của Recursive CTE Khi Phân Cấp Sâu

Một câu lệnh WITH RECURSIVE buộc công cụ lập lịch truy vấn của cơ sở dữ liệu phải thực hiện duyệt cây đệ quy qua nhiều vòng lặp nối bảng (Iterative Joins):

-- Anti-pattern: Đệ quy duyệt cây bình luận tốn kém CPU
WITH RECURSIVE comment_tree AS (
    SELECT id, parent_id, content, created_at, 1 AS depth
    FROM comments
    WHERE post_id = 9021 AND parent_id IS NULL
    UNION ALL
    SELECT c.id, c.parent_id, c.content, c.created_at, ct.depth + 1
    FROM comments c
    INNER JOIN comment_tree ct ON c.parent_id = ct.id
)
SELECT * FROM comment_tree ORDER BY depth, created_at;

Khi bài viết có hàng trăm phản hồi lồng nhau, câu lệnh này tạo ra hàng loạt bảng tạm (WorkTable) trong bộ nhớ, ngăn cản cơ sở dữ liệu sử dụng chỉ mục B-Tree thông thường và tiêu hao lượng lớn IOPS.


2. Giải Pháp Base32 Materialized Path: Sắp Xếp Cây Bằng Một Phép Quét Chỉ Mục

Thay vì lưu trữ liên kết cha-con phân tán, chúng tôi mã hóa toàn bộ đường dẫn phả hệ của nút vào một cột chuỗi ký tự theo chuẩn Base32 (bao gồm các chữ số và chữ cái không gây nhầm lẫn: 0-9, A-V):

Nhờ tính chất sắp xếp tự nhiên theo thứ tự từ điển (Lexicographical Order) của chuỗi ký tự Base32, toàn bộ cây phân nhánh từ trên xuống dưới được định vị chính xác chỉ bằng một lệnh quét chỉ mục tiền tố (Prefix Index Scan):

-- Truy vấn toàn bộ nhánh con của bình luận 0001 với độ phức tạp cực thấp
SELECT id, path, content, author_id
FROM comments
WHERE post_id = 9021 AND path LIKE '0001.%'
ORDER BY path ASC;

Trong PostgreSQL 17, chúng tôi sử dụng lớp chỉ mục đặc biệt text_pattern_ops trên cột path:

CREATE INDEX idx_comments_path_ops ON comments (post_id, path text_pattern_ops);

Chỉ mục này cho phép PostgreSQL tìm kiếm trực tiếp vị trí bắt đầu của nhánh và đọc tuần tự các nút con kế tiếp mà không cần đệ quy, không cần bảng tạm, đưa tốc độ duyệt cây về mức sub-millisecond (dưới 1ms).


3. Khử Sạch 5 Bảng RBAC Bằng Toán Tử Bitwise 64-Bit

Bài toán phân quyền thường tiêu tốn tài nguyên hệ thống do liên tục phải kiểm tra vai trò người dùng trong các bảng trung gian.

Để tối ưu hóa, chúng tôi gán cho mỗi quyền hạn một vị trí bit duy nhất trong một số nguyên 64-bit (BIGINT):

Toàn bộ quyền hạn của một người dùng được gộp thành một trường duy nhất: permissions BIGINT.

Khi kiểm tra người dùng có quyền xóa bài viết hay không, cơ sở dữ liệu hoặc mã backend chỉ cần thực hiện một phép tính logic bit đơn giản:

-- Kiểm tra quyền bằng phép toán bitwise AND: O(1)
SELECT id, title 
FROM posts 
WHERE post_id = 9021 
  AND (user_permissions & 8) = 8;

Phép toán này được thực thi ngay trong thanh ghi của vi xử lý chỉ trong 1 chu kỳ xung nhịp, loại bỏ hoàn toàn 5 câu lệnh JOIN và xóa sạch độ trễ phân quyền.


4. Bảng So Sánh Hiệu Năng Thực Tế (PostgreSQL 17 Benchmark)

Chỉ Số Đánh Giá Recursive CTE + 5-Table RBAC Base32 Path + 64-Bit Bitmask Mức Độ Cải Thiện
Độ trễ P50 (Cây 500 comments) 34.2 ms 0.32 ms Nhanh hơn 106 lần
Độ trễ P95 (Dưới tải cao) 142.5 ms 0.81 ms Nhanh hơn 175 lần
Thông lượng tối đa (Throughput) 280 req/sec 5,150 req/sec Tăng 18.4 lần
Bộ nhớ đệm quét (Buffer Shared Hit) 14,200 blocks 18 blocks Tiết kiệm 99.8% I/O
Độ phức tạp thuật toán $O(N \cdot \text{depth})$ $O(\log N)$ (B-Tree) Tối ưu triệt để

5. Kết Luận

Kiến trúc Base32 Materialized Path và Bitmask RBAC minh chứng rõ ràng cho quan điểm: Sự tinh tế của một hệ thống không nằm ở việc thêm thắt các công nghệ phức tạp, mà ở việc chọn đúng cấu trúc dữ liệu cơ bản để máy chủ vận hành mượt mà nhất có thể.

Biên Niên Sử Liên Quan (Kido Chronicles)

View all 128 episodes ↗

Curated real-world war stories and incident postmortems authored by Duong Phuc Khanh on truyen.khanhdp.com related to this topic:

Duong Phuc Khanh (Dương Phúc Khánh · Kido)

Về Tác Giả: Dương Phúc Khánh (khanhdp · Kido)

Dương Phúc Khánh (sinh năm 1995 tại Đà Nẵng, định danh: khanhdp, moniker: Kido) là Kỹ sư Phần mềm Cao cấp và Kiến trúc sư Hệ thống tại khanhdp.com. Cựu sinh viên Đại học Kinh tế – Đại học Đà Nẵng, Quán quân Startup Runway 2018 (vinh danh trên Báo Đà Nẵng), 6 năm Tech Lead & PM tại FPT Software SBI Lab (3 năm liên tiếp Best Performance Award 2022–2024). Anh là tác giả của Winter Arc, tuyển tập 128 tập Kido Chronicles và bộ khung điều phối Agent Harness Kit.