Thứ Tư, 11 tháng 3, 2026

Tối ưu câu lệnh SQL trong PostgreSQL

Tối ưu câu lệnh SQL trong PostgreSQL

1. VẤN ĐỀ


2. PHÂN TÍCH

1️⃣ Cost 0.14..88754.08 nghĩa là gì?

Trong PostgreSQL:

cost = estimated work units (không phải thời gian)

Format:

cost = start_cost .. total_cost

Với plan của anh

cost=0.14..88754.08
Giá trịÝ nghĩa
0.14chi phí để lấy row đầu tiên
88754.08chi phí ước lượng tổng để trả toàn bộ kết quả

Điều quan trọng

Cost không phải ms
Cost chỉ là thước đo nội bộ của optimizer

Ví dụ:

costthực tế
100có thể 1ms
10000có thể 100ms
80000có thể 600ms

Optimizer dùng cost để so sánh các plan với nhau.


2️⃣ Actual time 0.038..641.838 là gì?

Format:

actual time = first_row_time .. total_time

Trong plan của anh

actual time=0.038..641.838
Giá trịÝ nghĩa
0.038 msthời gian lấy row đầu tiên
641.838 msthời gian thực thi toàn bộ node

Tức là:

query mất khoảng ~642 ms

3️⃣ Vì sao query của anh mất ~642 ms?

Nhìn vào plan:

Append
Subplans Removed: 45

→ table partition

Sau đó:

Seq Scan on isdn_retry_extend_202604
actual time=641.740..641.740
Rows Removed by Filter: 1636514

💥 Đây chính là nguyên nhân.

PostgreSQL phải:

scan 1,636,514 rows

để tìm:

rows=0

4️⃣ Vì sao PostgreSQL lại Seq Scan partition đó?

Partition 202604 không có index phù hợp

Trong plan:

Index Scan using ... on 202603
Index Scan using ... on 202605

Nhưng:

Seq Scan on 202604

→ partition 202604 không có index hoặc index không phù hợp


5️⃣ Cách đọc EXPLAIN PLAN nhanh (phương pháp DBA)

Chỉ cần nhìn 4 dòng sau:

Bước 1 — tìm node tốn thời gian nhất

Tìm dòng có:

actual time = lớn nhất

Trong plan của anh:

Seq Scan on 202604
actual time=641.740

điểm nghẽn


Bước 2 — xem Rows Removed by Filter

Rows Removed by Filter: 1636514

→ scan quá nhiều row


Bước 3 — xem type scan

Scan typeÝ nghĩa
Index Scantốt
Bitmap Index Scanok
Seq Scannguy hiểm nếu table lớn

Bước 4 — xem index condition

Index Cond: isdn='938630490'

Nếu không có Index Cond → index không được dùng.


6️⃣ Tối ưu query này thế nào?

Query:

SELECT *
FROM isdn_retry_extend
WHERE retry_datetime BETWEEN now() AND now() + interval '60 day'
AND isdn = '938630490'
AND status IN ('2','1')

Index hiện tại:

isdn_retry_extend_202603_idx1

Có vẻ index chỉ có:

(isdn)

Nhưng query có thêm:

retry_datetime
status

Index tối ưu hơn

Khuyến nghị:

CREATE INDEX idx_retry_extend_opt
ON isdn_retry_extend (isdn, retry_datetime, status);

Hoặc tốt hơn:

CREATE INDEX idx_retry_extend_opt
ON isdn_retry_extend (isdn, retry_datetime)
WHERE status IN ('1','2');

→ partial index


7️⃣ Sau khi index đúng, plan sẽ thành

Hiện tại:

Seq Scan
641 ms

Sau tối ưu:

Index Scan
<1 ms

8️⃣ Quy tắc vàng đọc Explain Plan PostgreSQL

Chỉ cần nhớ 5 bước này:

1️⃣ Tìm node có actual time lớn nhất
2️⃣ Kiểm tra scan type (Seq vs Index)
3️⃣ Xem Rows Removed by Filter
4️⃣ Kiểm tra Index Cond
5️⃣ So sánh cost vs actual

9️⃣ Một mẹo/bí kíp DBA cực quan trọng

Luôn chạy:

EXPLAIN (ANALYZE, BUFFERS)

Ví dụ:

EXPLAIN (ANALYZE, BUFFERS)
SELECT ...

Nó sẽ cho thêm:

shared hit
shared read

→ biết query đọc bao nhiêu block disk.

Bí kíp đọc Explain Plan nhanh chóng và hiệu quả:

Để không bị "ngợp" giữa một rừng chữ, bạn hãy áp dụng quy tắc sau:

A. Đọc từ dưới lên trên, từ trong ra ngoài

Cấu trúc Explain Plan là một cái cây (Tree). Những dòng thụt lề vào sâu nhất là những việc được làm đầu tiên. Hãy tìm những chỗ có chi phí lớn nhất hoặc thời gian thực tế dài nhất.

B. Tập trung vào "Sự khác biệt" (The Gap)

  • Cost vs Actual Time: Đôi khi Cost rất thấp nhưng Actual Time lại cao (do tranh chấp tài nguyên, lock...).

  • Rows vs Actual Rows: Nếu PostgreSQL ước tính (rows) là 1 mà thực tế chạy ra 1,000,000 dòng, nghĩa là thống kê (statistics) của database đang bị cũ, dẫn đến nó chọn sai chiến thuật chạy (Plan).

C. Nhận diện các "Từ khóa nguy hiểm"

  • Seq Scan: Quét toàn bộ bảng. Nếu bảng lớn, đây là kẻ thù số 1.

  • Filter: Sau khi quét xong mới lọc lại. Rất tốn kém. Nên chuyển thành Index Cond (lọc bằng index).

  • Rows Removed by Filter: Nếu số này quá lớn (như trong hình của bạn là 1,636,514 dòng bị loại bỏ), nghĩa là bạn đang thiếu Index hoặc Index không hiệu quả.

D. Kiểm tra việc loại bỏ phân vùng (Partition Pruning)

Trong hình của bạn, hệ thống báo Subplans Removed: 45. Điều này tốt, nó đã loại bỏ được 45 bảng không liên quan. Tuy nhiên, nó vẫn phải quét 3 phân vùng (202603, 202604, 202605).


Giải pháp nhanh cho trường hợp của bạn:

Phân vùng isdn_retry_extend_202604 của bạn đang bị Seq Scan.

Lời khuyên: Hãy kiểm tra xem tại sao bảng này không dùng được Index giống như hai bảng còn lại. Có thể do Index trên bảng 202604 bị hỏng, chưa được tạo, hoặc dữ liệu phân bổ khiến Postgres quyết định quét toàn bộ cho nhanh (nhưng thực tế lại chậm).

3. GIẢI PHÁP

Khuyến nghị:

1.Tạo index cho partition 202604:

CREATE INDEX isdn_retry_extend_202604_idx1 ON public.isdn_retry_extend_202604 USING btree (isdn, ((insert_date)::date) DESC NULLS LAST) WITH (fillfactor='100');

CREATE UNIQUE INDEX isdn_retry_extend_202604_pkey ON public.isdn_retry_extend_202604 USING btree (id, retry_datetime);

2.Có thể tạo index ở bảng cha khi đó sẽ tạo cho toàn bộ bảng con

CREATE INDEX idx_retry_extend_opt
ON isdn_retry_extend (isdn, retry_datetime, status);

Hoặc tốt hơn:

CREATE INDEX idx_retry_extend_opt
ON isdn_retry_extend (isdn, retry_datetime)
WHERE status IN ('1','2');

Kết quả: partition 202604 đã có index
Check lại:

SELECT c.relname AS partition_name, i.relname AS index_name, pg_get_indexdef(i.oid) AS index_def FROM pg_class c JOIN pg_inherits inh ON inh.inhrelid = c.oid JOIN pg_class p ON p.oid = inh.inhparent LEFT JOIN pg_index ix ON ix.indrelid = c.oid LEFT JOIN pg_class i ON i.oid = ix.indexrelid WHERE p.relname = 'isdn_retry_extend' ORDER BY c.relname, i.relname;
=============================
TƯ VẤN: Click Here hoặc Hotline/Zalo 090.29.12.888
=============================
Website không chứa bất kỳ quảng cáo nào, mọi đóng góp để duy trì phát triển cho website (donation) xin vui lòng gửi về STK 90.2142.8888 - Ngân hàng Vietcombank Thăng Long - TRAN VAN BINH
=============================
Nếu bạn không muốn bị AI thay thế và tiết kiệm 3-5 NĂM trên con đường trở thành DBA chuyên nghiệp hay làm chủ Database thì hãy đăng ký ngay KHOÁ HỌC ORACLE DATABASE A-Z ENTERPRISE, được Coaching trực tiếp từ tôi với toàn bộ bí kíp thực chiến, thủ tục, quy trình của gần 20 năm kinh nghiệm (mà bạn sẽ KHÔNG THỂ tìm kiếm trên Internet/Google) từ đó giúp bạn dễ dàng quản trị mọi hệ thống Core tại Việt Nam và trên thế giới, đỗ OCP.
- CÁCH ĐĂNG KÝ: Gõ (.) hoặc để lại số điện thoại hoặc inbox https://m.me/tranvanbinh.vn hoặc Hotline/Zalo 090.29.12.888
- Chi tiết tham khảo:
https://bit.ly/oaz_w
=============================
2 khóa học online qua video giúp bạn nhanh chóng có những kiến thức nền tảng về Linux, Oracle, học mọi nơi, chỉ cần có Internet/4G:
- Oracle cơ bản: https://bit.ly/admin_1200
- Linux: https://bit.ly/linux_1200
=============================
KẾT NỐI VỚI CHUYÊN GIA TRẦN VĂN BÌNH:
📧 Mail: binhoracle@gmail.com
☎️ Mobile/Zalo: 0902912888
👨 Facebook: https://www.facebook.com/BinhOracleMaster
👨 Inbox Messenger: https://m.me/101036604657441 (profile)
👨 Fanpage: https://www.facebook.com/tranvanbinh.vn
👨 Inbox Fanpage: https://m.me/tranvanbinh.vn
👨👩 Group FB: https://www.facebook.com/groups/DBAVietNam
👨 Website: https://www.tranvanbinh.vn
👨 Blogger: https://tranvanbinhmaster.blogspot.com
🎬 Youtube: https://www.youtube.com/@binhguru
👨 Tiktok: https://www.tiktok.com/@binhguru
👨 Linkin: https://www.linkedin.com/in/binhoracle
👨 Twitter: https://twitter.com/binhguru
👨 Podcast: https://www.podbean.com/pu/pbblog-eskre-5f82d6
👨 Địa chỉ: Tòa nhà Sun Square - 21 Lê Đức Thọ - Phường Mỹ Đình 1 - Quận Nam Từ Liêm - TP.Hà Nội

=============================
cơ sở dữ liệu, cơ sở dữ liệu quốc gia, database, AI, trí tuệ nhân tạo, artificial intelligence, machine learning, deep learning, LLM, ChatGPT, DeepSeek, Grok, oracle tutorial, học oracle database, Tự học Oracle, Tài liệu Oracle 12c tiếng Việt, Hướng dẫn sử dụng Oracle Database, Oracle SQL cơ bản, Oracle SQL là gì, Khóa học Oracle Hà Nội, Học chứng chỉ Oracle ở đầu, Khóa học Oracle online,sql tutorial, khóa học pl/sql tutorial, học dba, học dba ở việt nam, khóa học dba, khóa học dba sql, tài liệu học dba oracle, Khóa học Oracle online, học oracle sql, học oracle ở đâu tphcm, học oracle bắt đầu từ đâu, học oracle ở hà nội, oracle database tutorial, oracle database 12c, oracle database là gì, oracle database 11g, oracle download, oracle database 19c/21c/23c/23ai, oracle dba tutorial, oracle tunning, sql tunning , oracle 12c, oracle multitenant, Container Databases (CDB), Pluggable Databases (PDB), oracle cloud, oracle security, oracle fga, audit_trail,oracle RAC, ASM, oracle dataguard, oracle goldengate, mview, oracle exadata, oracle oca, oracle ocp, oracle ocm , oracle weblogic, postgresql tutorial, mysql tutorial, mariadb tutorial, ms sql server tutorial, nosql, mongodb tutorial, oci, cloud, middleware tutorial, docker, k8s, micro service, hoc solaris tutorial, hoc linux tutorial, hoc aix tutorial, unix tutorial, securecrt, xshell, mobaxterm, putty

ĐỌC NHIỀU

Trần Văn Bình - Oracle Database Master