Vì sao query PostgreSQL chạy chậm: 7 lỗi index ai cũng gặp · Pattern Quest
#postgresql
#performance
#sql
#spring-boot
#java
Vì sao query PostgreSQL chạy chậm: 7 lỗi index ai cũng gặpWhy PostgreSQL Queries Are Slow: 7 Common Index Mistakes
Giải thích 7 lỗi index thường gặp khiến query PostgreSQL chậm và cách khắc phục thực tiễn với SQL và Spring Boot.Explains 7 typical index mistakes that slow down PostgreSQL queries and shows practical fixes with SQL and Spring Boot.
Bạn có bao giờ chạy một truy vấn trong Spring Boot, thấy thời gian phản hồi tăng từ 20ms lên vài giây chỉ vì một thay đổi nhỏ trong bảng? Ở phần này mình sẽ liệt kê 7 lỗi index mà hầu hết dev Java/Go gặp, kèm ví dụ thực tế và cách sửa ngay trên PostgreSQL.
Khi câu lệnh SELECT … WHERE user_id = ? chạy trên bảng có hàng triệu bản ghi mà không có index, PostgreSQL phải thực hiện full table scan. Kết quả: p99 latency tăng mạnh.
SQL
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 12345;-- Seq Scan on orders (cost=0.00..12345.00 rows=1 width=123) (actual time=0.015..250.123 rows=0 loops=1)
Fix: Tạo index đơn trên cột thường dùng trong WHERE.
SQL
CREATE INDEX idx_orders_user_id ON orders(user_id);
Trong Spring Data JPA, không cần thay đổi code, chỉ cần chạy migration.
Nếu cột chỉ chứa một vài giá trị (ví dụ status với ('NEW','PAID','CANCELLED')), index sẽ không giúp gì vì nhiều hàng cùng giá trị.
SQL
CREATE INDEX idx_orders_status ON orders(status);EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'NEW';-- vẫn sẽ dùng Seq Scan vì selectivity thấp.
Fix: Kết hợp cột khác tạo composite index hoặc không tạo index.
SQL
CREATE INDEX idx_orders_status_created ON orders(status, created_at);
Giờ planner có thể lọc theo status rồi sắp xếp theo created_at hiệu quả hơn.
WHERE → index đơn. 2. Nếu selectivity < 5 % → cân nhắc composite. 3. Tránh hàm trên cột, dùng expression index. 4. IN tốt hơn OR. 5. Định nghĩa hướng (ASC/DESC) phù hợp với ORDER BY. 6. Chạy ANALYZE sau thay đổi dữ liệu. 7. Giảm số index để tối ưu ghi.
Áp dụng những bước này, bạn sẽ thấy thời gian query giảm từ vài giây xuống dưới 100ms trong hầu hết các trường hợp.
Bạn có gặp lỗi index nào chưa? Chia sẻ trong phần bình luận nhé!
Ever run a Spring Boot endpoint and watch the response time jump from 20 ms to several seconds after a tiny schema change? Most of the time it’s an index mistake in PostgreSQL. Below are the 7 most common index pitfalls you’ll encounter, with concrete SQL snippets and the exact fix you can apply right now.
If you query SELECT … WHERE user_id = ? on a table with millions of rows and there is no index, PostgreSQL performs a full table scan. The p99 latency can explode.
SQL
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 12345;-- Seq Scan on orders (cost=0.00..12345.00 rows=1 width=123) (actual time=0.015..250.123 rows=0 loops=1)
Fix: Create a simple index on the frequently filtered column.
SQL
CREATE INDEX idx_orders_user_id ON orders(user_id);
With Spring Data JPA you don’t need to change any Java code – just run the migration.
An index on a column that only holds a few distinct values (e.g. status with ('NEW','PAID','CANCELLED')) provides little benefit because many rows share the same value.
SQL
CREATE INDEX idx_orders_status ON orders(status);EXPLAIN ANALYZE SELECT * FROM orders WHERE status = 'NEW';-- Planner still prefers Seq Scan due to low selectivity.
Fix: Either drop the index or build a composite index that adds a more selective column.
SQL
CREATE INDEX idx_orders_status_created ON orders(status, created_at);
Now the planner can filter by status and immediately order by created_at.
After bulk inserts, updates, or deletes, PostgreSQL needs fresh statistics for the planner to choose the right plan. Out‑of‑date stats lead to sub‑optimal index usage.
Shell
VACUUM ANALYZE orders;
In CI/CD pipelines, run ANALYZE after migrations or tune autovacuum settings appropriately.