Oracle 4 — Tối ưu: CBO, execution plan, index & AWR
Oracle 4 — Tối ưu: CBO, execution plan, index & AWR
Tối ưu truy vấn Oracle là một thế giới rộng hơn hẳn EXPLAIN của PostgreSQL: không chỉ đọc plan, mà còn cả một hệ sinh thái công cụ chẩn đoán (AWR/ASH/ADDM), cơ chế ổn định plan (SQL plan baselines), và một bộ hint phong phú để can thiệp khi cần. Bài này đi từ trái tim của hệ thống — bộ tối ưu chi phí CBO — ra tới các công cụ vận hành thực tế, luôn giữ tinh thần: hiểu vì sao plan được chọn, rồi mới can thiệp.
Lưu ý sandbox: Knowledge Base chạy trên PostgreSQL. Toàn bộ cú pháp
DBMS_XPLAN,DBMS_STATS, hint Oracle, AWR/ASH trong bài này không chạy được trong sandbox Postgres. Tất cả ví dụ đều ghi rõ (minh hoạ — Oracle, không chạy trong sandbox Postgres). Muốn thực hànhEXPLAINchạy thật, xem bài PostgreSQL EXPLAIN.
1. CBO — Cost-Based Optimizer
Cũng như planner của PostgreSQL, Oracle không để bạn ra lệnh "hãy dùng index này, quét bảng kia". Bạn viết SQL khai báo, còn CBO (Cost-Based Optimizer) chịu trách nhiệm sinh nhiều kế hoạch thực thi tương đương, ước lượng chi phí (cost) từng phương án, và chọn cái rẻ nhất. Từ Oracle 10g, RBO (Rule-Based Optimizer) cũ đã bị loại bỏ hoàn toàn — mọi thứ đều đi qua CBO.
Cost là một con số vô hướng do CBO tự tính, xấp xỉ tổng chi phí I/O + CPU + (đôi khi) network để chạy plan. Cost thấp hơn = plan được chọn. Điều mấu chốt: CBO không nhìn dữ liệu thật để quyết định, nó nhìn statistics — bản tóm tắt thống kê về dữ liệu.
Statistics — nhiên liệu của CBO
Thống kê ở Oracle được thu thập bằng package DBMS_STATS (thay cho lệnh ANALYZE cũ, nay không còn khuyến nghị cho mục đích optimizer). Có nhiều tầng thống kê:
- Table statistics: số dòng (
NUM_ROWS), số block, độ dài dòng trung bình. - Column statistics: số giá trị phân biệt (NDV — number of distinct values), min/max, số NULL, và histogram cho các cột phân bố lệch.
- Index statistics: số lá (leaf blocks), chiều cao B-tree, clustering factor (mức độ dữ liệu bảng được sắp xếp theo thứ tự index — ảnh hưởng lớn tới việc CBO có chọn index hay không).
-- (minh hoạ — Oracle, không chạy trong sandbox Postgres)
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'SALES',
tabname => 'ORDERS',
method_opt => 'FOR ALL COLUMNS SIZE AUTO', -- tự quyết histogram
cascade => TRUE, -- gồm cả index
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE);
END;
/
Histogram đặc biệt quan trọng với cột phân bố lệch (skew). Ví dụ cột status có 98% giá trị 'DONE' và 2% 'ERROR': nếu không có histogram, CBO giả định phân bố đều và ước lượng sai số dòng cho WHERE status='ERROR'. Với histogram, CBO biết 'ERROR' hiếm → chọn index; còn 'DONE' phổ biến → quét full table. Đây chính là lý do một truy vấn có thể "đúng plan" hay "sai plan" tùy giá trị bind (xem phần bind peeking bên dưới).
Vì sao thống kê cũ → plan tệ
Đây là nguyên nhân số một của các query "tự nhiên chậm". Kịch bản kinh điển: bảng ORDERS lúc thu thập stats có 10.000 dòng. Sau vài tháng nó lên 50 triệu dòng nhưng stats chưa cập nhật. CBO vẫn tưởng bảng nhỏ → chọn NESTED LOOPS quét index lặp đi lặp lại, hoặc chọn FULL TABLE SCAN vì nghĩ bảng bé xíu. Kết quả: plan hợp lý cho 10.000 dòng trở thành thảm họa với 50 triệu dòng.
Oracle giảm nhẹ vấn đề này bằng job tự động thu thập stats (auto optimizer stats collection) chạy trong maintenance window hằng đêm, nhắm các đối tượng "stale" (thay đổi > ~10% số dòng). Nhưng job này không phải lúc nào cũng kịp — với bảng tăng trưởng nhanh hoặc sau khi nạp dữ liệu lớn (bulk load), bạn thường phải chủ động gather stats ngay.
Adaptive plans & SQL plan baselines
Oracle 12c+ bổ sung các cơ chế "tự chữa" khi ước lượng của CBO sai:
- Adaptive plans: plan có thể thay đổi ngay trong lúc chạy. Ví dụ CBO chọn NESTED LOOPS nhưng đặt sẵn một subplan HASH JOIN; khi executor thấy số dòng thực vượt ngưỡng, nó chuyển sang HASH JOIN giữa chừng. Có cả adaptive statistics (dynamic sampling, SQL plan directives) để bù cho stats thiếu.
- SQL plan baselines (SQL Plan Management — SPM): mục tiêu ngược lại — ổn định plan. Mỗi câu SQL có thể có nhiều plan "được chấp nhận" (accepted). CBO chỉ được dùng plan nằm trong baseline; plan mới chỉ được thêm vào sau khi được kiểm chứng là không tệ hơn. Đây là "phao cứu sinh" chống hiện tượng plan regression — plan tốt bỗng đổi thành plan xấu sau khi gather stats hoặc nâng cấp DB. Lưu ý: baseline không lưu bind-awareness.
2. Đọc execution plan
Có hai loại plan cần phân biệt rạch ròi, y như EXPLAIN vs EXPLAIN ANALYZE bên Postgres:
EXPLAIN PLAN — plan ước lượng (không chạy)
EXPLAIN PLAN chỉ để CBO sinh plan và ghi vào bảng PLAN_TABLE, không thực thi câu lệnh. Sau đó ta hiển thị bằng DBMS_XPLAN.DISPLAY:
-- (minh hoạ — Oracle, không chạy trong sandbox Postgres)
EXPLAIN PLAN FOR
SELECT o.order_id, c.name
FROM orders o JOIN customers c ON c.id = o.cust_id
WHERE o.status = 'ERROR';
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
Vì không chạy, mọi con số chỉ là ước lượng của CBO — không biết được thực tế lệch bao nhiêu.
DBMS_XPLAN.DISPLAY_CURSOR — plan thực tế (A-Rows vs E-Rows)
Kỹ thuật chẩn đoán mạnh nhất là so E-Rows (Estimated Rows — CBO đoán) với A-Rows (Actual Rows — thực tế chạy). Lệch lớn giữa hai cột là dấu hiệu stats sai và là gốc rễ của phần lớn plan tệ. Để có A-Rows, phải bật thu thập rowsource statistics — bằng hint /*+ GATHER_PLAN_STATISTICS */ hoặc đặt STATISTICS_LEVEL = ALL, rồi hiển thị với format ALLSTATS LAST:
-- (minh hoạ — Oracle, không chạy trong sandbox Postgres)
SELECT /*+ GATHER_PLAN_STATISTICS */ o.order_id, c.name
FROM orders o JOIN customers c ON c.id = o.cust_id
WHERE o.status = 'ERROR';
SELECT * FROM TABLE(
DBMS_XPLAN.DISPLAY_CURSOR(format => 'ALLSTATS LAST'));
Kết quả rút gọn (format ALLSTATS LAST thêm cột Starts / E-Rows / A-Rows / A-Time / Buffers):
(minh hoạ — Oracle, không chạy trong sandbox Postgres)
------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 142 | 00:00:00.03|
| 1 | NESTED LOOPS | | 1 | 140 | 142 | 00:00:00.03|
| 2 | TABLE ACCESS BY INDEX ROWID| ORDERS | 1 | 140 | 142 | 00:00:00.01|
|* 3 | INDEX RANGE SCAN | IX_ORD_ST | 1 | 140 | 142 | 00:00:00.00|
| 4 | TABLE ACCESS BY INDEX ROWID| CUSTOMERS | 142 | 1 | 142 | 00:00:00.01|
|* 5 | INDEX UNIQUE SCAN | PK_CUST | 142 | 1 | 142 | 00:00:00.00|
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
3 - access("O"."STATUS"='ERROR')
5 - access("C"."ID"="O"."CUST_ID")
Đọc plan: E-Rows≈140 rất sát A-Rows=142 → stats tốt, plan hợp lý. Cột Starts cho biết số lần một bước được thực thi — ở đây node 4/5 chạy 142 lần (một lần cho mỗi dòng của NESTED LOOPS bên ngoài). Nếu E-Rows=1 nhưng A-Rows=1.000.000, bạn biết ngay CBO đã đánh giá thấp và có thể đã chọn NESTED LOOPS thảm họa.
Các công cụ khác
SET AUTOTRACE ONtrong SQL*Plus: chạy câu lệnh và in kèm plan + số liệu I/O (consistent gets, physical reads). Tiện cho vòng lặp thử nhanh.- Real-Time SQL Monitoring (SQL Monitor): tự động theo dõi các câu SQL "nặng" (chạy song song, hoặc tốn > ~5 giây CPU/IO). Báo cáo
DBMS_SQLTUNE.REPORT_SQL_MONITORcho thấy plan kèm tiến độ real-time từng bước (dòng đã xử lý, thời gian, wait) — cực mạnh để soi một câu SQL đang chạy lâu. Lưu ý: cần Tuning Pack license.
Access path & join method
Đọc plan Oracle cần thuộc "từ vựng" các thao tác:
Access path (cách lấy dữ liệu một bảng):
FULL TABLE SCAN(FTS): quét toàn bộ bảng. Không phải lúc nào cũng xấu — với truy vấn lấy phần lớn dữ liệu thì FTS tuần tự (multiblock read) nhanh hơn index. Nhưng FTS trên bảng lớn khi chỉ cần vài dòng là dấu hiệu thiếu index / stats sai.INDEX RANGE SCAN: quét một dải khóa index (dùng cho>,<,BETWEEN,LIKE 'x%').INDEX UNIQUE SCAN: tra đúng một dòng qua khóa duy nhất / primary key.INDEX FAST FULL SCAN: quét toàn bộ index như quét bảng (multiblock), dùng khi mọi cột cần đều nằm trong index (covering) — không cần đọc bảng.
Join method (cách ghép hai nguồn dữ liệu):
NESTED LOOPS: với mỗi dòng bảng ngoài, dò bảng trong qua index. Tối ưu khi bảng ngoài ít dòng và bảng trong có index tốt (OLTP điển hình).HASH JOIN: xây bảng băm từ nguồn nhỏ hơn rồi quét nguồn lớn dò vào. Tối ưu cho join khối lượng lớn (data warehouse, báo cáo).MERGE JOIN(sort-merge): sắp xếp cả hai nguồn rồi trộn. Dùng khi dữ liệu đã sắp sẵn hoặc join theo bất đẳng thức.
3. Chỉ mục Oracle
B-tree — mặc định
Index B-tree cân bằng là loại mặc định, phù hợp cột lực chọn cao (high cardinality — nhiều giá trị phân biệt) và mọi hệ OLTP ghi nhiều. Các biến thể hữu ích:
- Composite index (nhiều cột): thứ tự cột quyết định. Đặt cột dùng cho điều kiện bằng (
=) và cột lọc chọn lọc nhất lên trước; index vẫn dùng được khi truy vấn chỉ đụng tới tiền tố bên trái (leftmost prefix). - Function-based index: lập chỉ mục trên biểu thức, ví dụ
CREATE INDEX ix ON emp(UPPER(name))đểWHERE UPPER(name)='AN'dùng được index. - Invisible index: index tồn tại và được bảo trì nhưng CBO bỏ qua (trừ khi bật
OPTIMIZER_USE_INVISIBLE_INDEXES). Dùng để thử nghiệm an toàn: tạo index invisible, test riêng bằng hint, rồi mới bật visible — hoặc để "thử gỡ" một index nghi thừa mà không cần drop. - Reverse key index: đảo byte của khóa trước khi lập chỉ mục. Chống "hot block" ở lá phải cùng của B-tree khi nhiều phiên cùng chèn khóa tăng dần (sequence) trong môi trường RAC — đổi lại, mất khả năng range scan.
Bitmap index — kho dữ liệu, cẩn thận với DML
Bitmap index lưu mỗi giá trị phân biệt bằng một chuỗi bit (bitmap) ánh xạ tới các dòng. Nó ngược hoàn toàn với B-tree về đối tượng phù hợp: bitmap tối ưu cho cột lực chọn thấp (low cardinality — ít giá trị phân biệt: giới tính, status, mã vùng), tốn ít dung lượng, và cho phép kết hợp nhiều bitmap bằng phép AND/OR bit cực nhanh — lý tưởng cho truy vấn ad-hoc nhiều điều kiện trong data warehouse.
Cảnh báo quan trọng — không dùng bitmap cho OLTP ghi nhiều. Khi cập nhật một dòng, Oracle khóa cả key entry của bitmap chứ không khóa từng bit. Vì một key trỏ tới rất nhiều dòng, một câu
UPDATEđổistatuscủa một dòng có thể khóa hàng loạt dòng khác dùng chung giá trị đó, gây tắc nghẽn DML nghiêm trọng trong hệ đa người dùng. Với cột lực chọn thấp trong OLTP, giải pháp đúng thường là... vẫn dùng B-tree bình thường (Oracle nén nhánh trùng lặp khá tốt), hoặc thiết kế lại.
Tóm tắt lựa chọn:
| Tiêu chí | B-tree | Bitmap |
|---|---|---|
| Cardinality phù hợp | Cao | Thấp |
| Môi trường | OLTP, mọi nơi | Data warehouse / read-heavy |
| DML đồng thời | Tốt | Rất kém (khóa lan) |
| Kết hợp nhiều điều kiện | Trung bình | Rất mạnh (AND/OR bit) |
| Cột NULL | Không lập chỉ mục NULL | Lập chỉ mục cả NULL |
4. AWR / ASH / ADDM — chẩn đoán ở tầng hệ thống
Đây là điểm PostgreSQL không có sẵn tương đương chính hãng (phải dùng pg_stat_statements, extension của bên thứ ba). Oracle tích hợp sẵn cả một kho dữ liệu hiệu năng:
- AWR (Automatic Workload Repository): định kỳ (mặc định 60 phút) chụp snapshot các số liệu hiệu năng — top SQL, wait events, I/O, memory, dung lượng session. Báo cáo AWR so hai snapshot để cho biết "giữa 9h và 10h sáng, cái gì tốn tài nguyên nhất".
- ASH (Active Session History): lấy mẫu trạng thái mọi session đang hoạt động mỗi giây (session nào, đang chạy SQL gì, đang chờ wait event gì). ASH trả lời "ngay lúc hệ thống treo lúc 14:32, các phiên đang kẹt ở đâu" — mịn hơn AWR nhiều.
- ADDM (Automatic Database Diagnostic Monitor): tự phân tích dữ liệu AWR và đưa ra gợi ý (thêm index, gather stats, tăng vùng nhớ...).
Điều tra điển hình: mở AWR → xem mục Top SQL by Elapsed Time / by Buffer Gets để tìm câu SQL ngốn nhất → xem phần Top Wait Events. Vài wait event hay gặp:
db file sequential read: đọc block đơn lẻ, thường do truy cập qua index (nhiều = index đọc quá nhiều block hoặc clustering factor xấu).db file scattered read: đọc nhiều block, dấu hiệu full table scan.log file sync: session chờ redo được ghi khi COMMIT — nhiều = commit quá thường xuyên hoặc I/O redo chậm.
Cảnh báo license — cực kỳ quan trọng về chi phí. AWR, ASH, ADDM, SQL Monitor yêu cầu Diagnostics Pack (và Tuning Pack cho một số tính năng), chỉ có trên Enterprise Edition và phải mua riêng. Trên EE, tham số
CONTROL_MANAGEMENT_PACK_ACCESSmặc định làDIAGNOSTIC+TUNING, nghĩa là chỉ cần chạy báo cáo AWR/ADDM là đã kích hoạt tính năng có phí — việc sử dụng được ghi vàoDBA_FEATURE_USAGE_STATISTICSvà là mục tiêu quen thuộc của audit Oracle. Nếu không mua pack, đừng chạy các công cụ này; hãy dùngV$views cơ bản (V$SQL,V$SESSION,V$SQL_PLAN) vốn miễn phí.
5. Hint — can thiệp có chủ đích
Hint là chỉ dẫn đặt trong comment đặc biệt /*+ ... */ ngay sau SELECT/INSERT/... để ép CBO theo ý bạn. Một số hint hay dùng:
-- (minh hoạ — Oracle, không chạy trong sandbox Postgres)
SELECT /*+ INDEX(o ix_ord_status) */ ... -- ép dùng index cụ thể
SELECT /*+ FULL(o) */ ... -- ép full table scan
SELECT /*+ LEADING(o c) USE_NL(c) */ ... -- o là bảng dẫn, ghép NESTED LOOPS
SELECT /*+ USE_HASH(o c) */ ... -- ép HASH JOIN
SELECT /*+ PARALLEL(o, 4) */ ... -- chạy song song 4 luồng
INDEX/FULL: chọn access path.LEADING: chỉ định bảng nào đứng đầu thứ tự join.USE_NL/USE_HASH: ép join method.PARALLEL: kích hoạt parallel query cho các tác vụ khối lớn.
Cảnh báo lạm dụng hint. Hint đóng băng quyết định của CBO. Đúng hôm nay không có nghĩa đúng sau khi dữ liệu tăng gấp 10 hay cấu trúc bảng đổi — lúc đó chính cái hint lại giữ plan ở trạng thái tệ. Hint chỉ nên là giải pháp cuối cùng, cục bộ. Trong đa số trường hợp, sửa gốc rễ tốt hơn: gather stats cho đúng, tạo/xóa index, viết lại SQL, hoặc dùng SQL plan baseline để cố định plan tốt một cách "chính thống" thay vì rải hint khắp code.
6. Bind variable & shared pool
Oracle cache plan đã biên dịch trong shared pool (library cache). Khi một câu SQL đến:
- Nếu văn bản câu lệnh trùng khớp một cursor đã có → soft parse: tái dùng plan có sẵn, rẻ.
- Nếu không tìm thấy → hard parse: CBO phải sinh plan mới, tốn CPU và cần latch (dễ gây tranh chấp khi hard parse nhiều).
Vấn đề: nếu code ghép giá trị trực tiếp (WHERE id = 12345, WHERE id = 12346, ...), mỗi câu là văn bản khác nhau → hard parse liên tục, làm ngập shared pool và giết hiệu năng. Giải pháp là bind variable:
-- (minh hoạ — Oracle, không chạy trong sandbox Postgres)
SELECT * FROM orders WHERE id = :order_id; -- tái dùng plan cho mọi giá trị
- Bind peeking: ở lần hard parse đầu tiên, CBO "nhìn trộm" giá trị bind hiện có để ước lượng độ chọn lọc như thể dùng literal. Cạm bẫy: nếu giá trị đầu tiên không đại diện (ví dụ gặp đúng giá trị hiếm), plan sinh ra có thể tệ cho các giá trị sau — vì mọi lần sau đều dùng lại plan đó.
- Adaptive Cursor Sharing (11g+): khắc phục bind peeking. Với cột có histogram, Oracle cho phép cùng một câu SQL dùng nhiều plan khác nhau tùy khoảng giá trị bind (bind-aware), thay vì một plan cứng.
CURSOR_SHARING: tham số cho phép Oracle tự thay literal bằng bind ở tầng hệ thống (FORCE). Đây là "băng cứu thương" cho ứng dụng cũ lỡ dùng literal, không phải giải pháp thiết kế — nên sửa code dùng bind ngay từ đầu.
Luồng chẩn đoán từ AWR tới plan
Use case thực tế — một câu SQL bỗng chậm
Bối cảnh. Báo cáo cuối ngày trên bảng orders (đã lên ~40 triệu dòng sau đợt nạp dữ liệu lớn) chạy từ 2 giây nhảy lên gần 90 giây. Không ai đổi code.
Bước 1 — Tìm thủ phạm. Mở AWR khung giờ báo cáo, mục Top SQL by Elapsed Time cho ra sql_id của câu lọc WHERE status = 'PENDING' AND region = 'HN'.
Bước 2 — Xem plan thực tế. Chạy lại với /*+ GATHER_PLAN_STATISTICS */ rồi DBMS_XPLAN.DISPLAY_CURSOR(format=>'ALLSTATS LAST'). Plan cho thấy FULL TABLE SCAN trên orders, và điểm chí mạng: E-Rows = 8.000 nhưng A-Rows = 12 — CBO tưởng có 8.000 dòng khớp nên cho rằng FTS rẻ hơn index.
Bước 3 — Chẩn đoán gốc rễ. E-Rows lệch A-Rows tới ~660 lần → stats cũ. Đợt bulk load đã bơm hàng chục triệu dòng nhưng job auto-stats chưa chạy; CBO vẫn dùng NDV và số dòng của "phiên bản bảng nhỏ" trước đó, đánh giá sai độ chọn lọc của status/region.
Bước 4 — Sửa. Thu thập lại stats cho bảng (kèm histogram để bắt phân bố lệch của status):
-- (minh hoạ — Oracle, không chạy trong sandbox Postgres)
BEGIN
DBMS_STATS.GATHER_TABLE_STATS('SALES','ORDERS',
method_opt => 'FOR ALL COLUMNS SIZE AUTO', cascade => TRUE);
END;
/
Sau gather, E-Rows tụt về ~15, sát A-Rows. Nhưng vẫn còn FTS vì thiếu index cho tổ hợp lọc này. Tạo composite index:
-- (minh hoạ — Oracle, không chạy trong sandbox Postgres)
CREATE INDEX ix_ord_status_region ON orders(status, region);
Kết quả. Plan chuyển sang INDEX RANGE SCAN + TABLE ACCESS BY INDEX ROWID, thời gian về ~0,4 giây. Nếu về sau plan lại có nguy cơ regress sau mỗi lần gather stats, ta cố định plan tốt bằng SQL plan baseline thay vì rải hint /*+ INDEX(...) */ khắp nơi.
Bài học: triệu chứng là "SQL chậm", nhưng gốc rễ là stats cũ (đọc được nhờ E-Rows vs A-Rows), rồi mới đến thiếu index. Sửa đúng thứ tự — stats trước, index sau, baseline nếu cần — thay vì vội vàng nhét hint.
Ghi nhớ
- CBO chọn plan theo cost, cost tính từ statistics. Stats sai → plan sai. Đây là nguyên nhân số một của "SQL tự nhiên chậm".
DBMS_STATSlà công cụ chuẩn để thu thập stats (không dùngANALYZEcho optimizer). Nhớ histogram cho cột phân bố lệch, gather ngay sau bulk load lớn.- So E-Rows với A-Rows (
DBMS_XPLAN.DISPLAY_CURSOR+ALLSTATS LAST, cầnGATHER_PLAN_STATISTICS) là kỹ thuật chẩn đoán mạnh nhất: lệch lớn = stats hỏng. - Thuộc từ vựng plan: access path (FTS / INDEX RANGE / UNIQUE / FAST FULL) và join method (NESTED LOOPS cho OLTP ít dòng, HASH JOIN cho khối lớn).
- B-tree cho cardinality cao & OLTP; bitmap cho cardinality thấp & data warehouse — tuyệt đối tránh bitmap trong OLTP ghi nhiều vì khóa lan khi DML.
- AWR / ASH / ADDM / SQL Monitor cần Diagnostics/Tuning Pack (EE, phải mua riêng) — chạy chúng khi chưa mua license là bẫy audit.
V$views là lựa chọn miễn phí. - Dùng bind variable để tránh hard parse; hiểu bind peeking và Adaptive Cursor Sharing.
- Hint là giải pháp cuối cùng, dễ đóng băng plan tệ. Ưu tiên sửa gốc (stats/index/rewrite) hoặc SQL plan baseline để ổn định plan.
Liên kết trong loạt bài
- Oracle 1 — Kiến trúc: SGA/shared pool, tiến trình — nền tảng để hiểu library cache & hard parse.
- Oracle 3 — SQL đặc trưng: cú pháp và tính năng SQL riêng của Oracle.
- Oracle 5 — Partitioning & lưu trữ: chia bảng để hỗ trợ partition pruning — một hướng tối ưu ở tầng lưu trữ.
- PostgreSQL EXPLAIN: đối chiếu cách tiếp cận planner của Postgres, và là nơi thực hành
EXPLAINchạy thật trong sandbox.
Nguồn tham khảo: ORACLE-BASE — DBMS_XPLAN, Oracle Optimizer blog — useful execution plans, Jonathan Lewis — E-rows/A-rows, Oracle — Bitmap vs B-tree Index, Oracle Docs — Indexes & IOTs, ORACLE-BASE — Adaptive Cursor Sharing, Oracle Diagnostics Pack datasheet.
Bài viết liên quan
Cách index hoạt động (B-Tree), đọc EXPLAIN, seq scan vs index scan và mẫu tối ưu truy vấn.
Khoá, ràng buộc, quan hệ và chuẩn hoá 1NF/2NF/3NF — thiết kế lược đồ đúng từ đầu.
Kết nhiều bảng đúng cách: các loại JOIN, bẫy thường gặp và phép hợp tập.
Truy vấn lồng, CTE (WITH), CTE đệ quy và hàm cửa sổ — vũ khí cho phân tích nâng cao.