Cách PostgreSQL Index cứu bạn khỏi thảm cảnh data rùa bò

Lý Hữu Trí
Lý Hữu Trí·4 phút đọc·29 tháng 5, 2026
Cách PostgreSQL Index cứu bạn khỏi thảm cảnh data rùa bò

Đã bao giờ bạn gặp cảnh này: lúc mới code, bảng user có vài trăm người thì query nháy mắt là xong. Nhưng đến lúc web lên 1 triệu user, mỗi lần gõ tên tìm kiếm là server "thở oxy", vòng tròn loading xoay mỏi tay chưa thấy đâu? Tiền bill AWS thì cứ thế tăng vọt.

Đó là lúc bạn nhận ra, viết lệnh SQL chạy được là chưa đủ. Để website thực sự mượt mà, bạn cần một công cụ tối ưu mạnh mẽ mang tên PostgreSQL Index.

Tại sao dữ liệu nhiều lại khiến web chậm đi?

Hãy tưởng tượng database của bạn như một cuốn bách khoa toàn thư dày 5000 trang. Nếu cuốn sách này không có mục lục, muốn tìm từ "Apple", bạn phải lật mở và dò từng trang một từ đầu đến cuối.

Trong thế giới database, hành động này gọi là Sequential Scan (quét tuần tự). Mặc định, Postgres sẽ làm y như vậy. Nó quét từ dòng đầu tiên đến dòng cuối cùng trong bảng để bới ra cục data bạn cần. Nhìn biểu đồ CPU máy chủ nhảy múa lên 100% là bạn hiểu vấn đề ngay.

Vậy làm sao để tạo ra một cái "mục lục" xịn xò cho database để tra cứu trong chớp mắt?

Cứu tinh xuất hiện: B-Tree và Index

Index chính là chiếc mục lục đó. Khi bạn tạo index cho một cột (ví dụ cột email), Postgres sẽ bí mật tạo ra một cấu trúc dữ liệu riêng biệt — thường là một cây B-Tree đã được sắp xếp gọn gàng theo thứ tự từ điển.

-- Lệnh tạo index cực kỳ đơn giản
CREATE INDEX idx_user_email ON users(email);

Nhờ có cấu trúc này, thay vì lật 1 triệu dòng, database chỉ mất vài bước dò tìm trên nhánh cây B-Tree là tóm gọn ngay dữ liệu. Tốc độ trả về đột ngột giảm từ vài giây xuống chỉ còn vài mili-giây. Tư duy lưu trữ bộ nhớ đệm này khá giống với cách React Query dẹp loạn useEffect và thoát cảnh giật lag ở phía frontend vậy, cốt lõi là làm cho đường đi của data ngắn nhất có thể.

Đừng tin tưởng mù quáng! Hãy luôn dùng thêm lệnh EXPLAIN ANALYZE trước câu query để xem Postgres có thực sự thèm xài cái index bạn vừa tạo không nhé.

Nghe ngon ăn thế này, vậy cứ table nào cũng đập chục cái index vào là auto nhanh đúng không?

Mặt tối của Index: Càng nhiều càng chậm

Cuốn từ điển càng nhồi nhét nhiều loại mục lục thì nó càng dày và nặng. Index cũng vậy, nó ăn lẹm vào dung lượng ổ cứng (Storage) của server.

Tai hại hơn, mỗi lần bạn Thêm mới, Chỉnh sửa, hay Xóa (INSERT, UPDATE, DELETE) dữ liệu, Postgres phải cất công vòng lại để cập nhật cái "mục lục" đó cho khớp. Nếu bạn gắn index bừa bãi, việc đọc thì nhanh lên, nhưng việc ghi data vào hệ thống lại chậm rề rề.

Khéo lại lặp lại thảm cảnh gián đoạn dữ liệu giống bài viết cách PostgreSQL Transaction giúp dữ liệu vững vàng và né cảnh mất tiền oan mà mình từng chia sẻ.

Vậy túm lại, khi nào mới nên rút "bí thuật" này ra xài?

Chiến lược đặt Index "chuẩn cơm mẹ nấu"

Bí quyết nằm ở chỗ: Chỉ đặt index cho những cột thường xuyên bị gọi hồn.

  • Đó là các cột hay nằm sau mệnh đề WHERE (như email, số điện thoại).
  • Đó là các khóa ngoại (Foreign Key) thường dùng để JOIN hai bảng với nhau.
  • Đó là các cột hay bị đem ra để ORDER BY (như ngày tạo, giá tiền).

Ngược lại, với những bảng dữ liệu hiếm khi search mà chủ yếu là lưu trữ dồn dập (ví dụ như bảng log lịch sử hệ thống), tuyệt đối hãy tránh xa index.

Nếu bạn tò mò muốn tìm hiểu sâu hơn về các loại index chuyên trị data JSON phức tạp như GIN hay GiST, đừng ngại ngó qua tài liệu chính thức của PostgreSQL. Và để rèn luyện thêm tư duy hệ thống cho Backend, mời bạn ghé DIA DEMY để xem thêm các trick tối ưu hiệu suất thực chiến nhé.

Tối ưu database xong, tìm kiếm dữ liệu đã mượt mà rồi. Nhưng khoan, nếu cùng lúc có hàng ngàn user bấm search thì Node.js server của bạn có gánh nổi lượng request khổng lồ đó không, hay lại nghẽn cổ chai ngay tại tầng API?

/Thảo luận

Bình luận

0