Full Table Scan trong Database là gì? Nguyên nhân, Cách phát hiện và Tối ưu
10/07/2026Một truy vấn từng chạy mượt mà bỗng dưng ì ạch, đẩy CPU và I/O của database lên mức báo động? Thủ phạm giấu mặt thường là Full table scan – hiện tượng hệ thống phải quét cạn kiệt toàn bộ bảng thay vì tận dụng chỉ mục (Index). Bài viết này cùng Viettel IDC giải phẫu bản chất kỹ thuật của Full table scan, cách nhận diện qua Execution Plan và các phương pháp tối ưu hiệu năng triệt để.
Full Table Scan là gì?
1. Bản chất kỹ thuật của Full Table Scan
Full Table Scan (Quét toàn bảng) là quá trình hệ quản trị cơ sở dữ liệu (DBMS) phải đọc tuần tự toàn bộ các dòng dữ liệu trong một bảng vật lý để tìm ra các bản ghi thỏa mãn điều kiện truy vấn, thay vì sử dụng cấu trúc chỉ mục (Index) để tìm đến đích nhanh chóng.
Về bản chất, đây là thao tác đọc dữ liệu ở mức độ thấp nhất và tốn kém nhất. Khác với việc có "đường tắt", hệ thống buộc phải nạp toàn bộ các trang dữ liệu (data pages/blocks) của bảng từ ổ cứng vào bộ nhớ chính (RAM) để đối chiếu điều kiện lọc, bất kể bạn chỉ cần tìm 1 dòng hay 1 triệu dòng.
2. Phân biệt Full Table Scan, Index Scan và Index Seek
Để tối ưu hóa, bạn cần hiểu rõ sự khác biệt giữa 3 cơ chế truy xuất dữ liệu cốt lõi này:
3. Full Table Scan có thực sự cần loại bỏ không?
Nhiều người mới làm việc với CSDL lầm tưởng quét toàn bảng luôn là dấu hiệu của một hệ thống tồi. Trên thực tế, Query Optimizer (Bộ tối ưu hóa truy vấn của DBMS) được lập trình rất thông minh và hoàn toàn có thể chủ động chọn Full Table Scan trong các kịch bản hợp lý:
- Bảng có kích thước cực nhỏ: Việc đọc một vài trang dữ liệu của bảng thẳng vào RAM tốn ít tài nguyên hơn nhiều so với việc phải duyệt qua cây chỉ mục rồi mới vòng lại đọc bảng gốc.
- Truy vấn cần trích xuất lượng dữ liệu lớn: Nếu kết quả trả về chiếm tỷ lệ lớn (ví dụ trên 20-30% số dòng của bảng), chi phí để tra cứu ngược (Key Lookup) từ Index về bảng gốc cho từng dòng sẽ đắt đỏ và tốn I/O hơn việc quét bảng một lần duy nhất.
- Tác vụ phân tích (OLAP/ETL): Các truy vấn xử lý theo lô (batch processing), làm báo cáo tổng hợp vốn dĩ yêu cầu phải đọc toàn bộ tập dữ liệu.
Vấn đề cốt lõi: Full Table Scan chỉ thực sự trở thành "thảm họa" khi nó xảy ra ngoài dự kiến trên các hệ thống giao dịch trực tuyến (OLTP), lặp lại với tần suất cao (hàng ngàn lần mỗi phút) trên các bảng dữ liệu khổng lồ. Đây chính là nguyên nhân trực tiếp vắt kiệt CPU và làm tê liệt hệ thống.
Cơ chế hoạt động của Query Optimizer khi quyết định Full Table Scan
1. Vai trò của Query Optimizer (Bộ tối ưu hóa truy vấn)
Khi một câu lệnh SQL được gửi đến hệ quản trị CSDL, nó không được thực thi ngay lập tức. Query Optimizer (hay Query Planner) sẽ tiếp nhận, phân tích và vạch ra nhiều phương án thực thi khả thi (Execution Plan Candidates). Nhiệm vụ của Optimizer là lựa chọn phương án có chi phí tài nguyên thấp nhất dựa trên mô hình CBO (Cost-Based Optimization - Tối ưu hóa dựa trên chi phí).
2. Cost-Based Optimization: Khi nào Optimizer "chọn" quét toàn bảng?
Hệ thống tính toán chi phí dựa trên nhiều yếu tố: số trang dữ liệu cần đọc, độ chọn lọc (Selectivity) của điều kiện lọc, và đặc biệt là bài toán so sánh giữa Sequential I/O (I/O tuần tự) và Random I/O (I/O ngẫu nhiên).
Trong ổ cứng máy chủ, việc đọc dữ liệu quét tuần tự liên tục thường nhanh hơn đáng kể so với việc ổ đĩa phải nhảy rải rác để đọc ngẫu nhiên. Do đó, nếu điều kiện WHERE có độ chọn lọc thấp (tức là trả về một tỷ lệ lớn số dòng trong bảng), Optimizer sẽ đánh giá việc dùng Index Seek (kéo theo vô số lượt tra cứu ngẫu nhiên để lấy dữ liệu gốc) đắt đỏ hơn việc dùng Sequential I/O để quét một mạch từ đầu đến cuối. Kết quả: Full Table Scan được lựa chọn.
3. Sự chi phối của Table Statistics (Thống kê dữ liệu)
Quyết định của Optimizer không phải là phép thuật, nó phụ thuộc hoàn toàn vào Table Statistics - bộ thông tin thống kê về phân bố dữ liệu (tổng số dòng, phân bổ giá trị theo từng cột, mức độ trùng lặp) mà hệ quản trị CSDL cập nhật định kỳ.
Nếu thống kê này bị lỗi thời (ví dụ: ngay sau một đợt import dữ liệu khổng lồ mà chưa chạy lệnh cập nhật), Optimizer sẽ trở nên "mù lòa". Hệ quả là nó có thể đưa ra quyết định sai lệch, chọn Full Table Scan dù thực tế Index Seek mới là phương án tối ưu, hoặc ngược lại.
Top 6 nguyên nhân phổ biến nhất gây ra Full Table Scan
1. Thiếu Index hoặc Index không phù hợp
Đây là nguyên nhân kinh điển nhất. Các cột thường xuyên xuất hiện trong mệnh đề WHERE, JOIN hoặc ORDER BY nhưng lại không có index tương ứng. Khi không có "mục lục", cơ sở dữ liệu buộc phải quét tuần tự toàn bảng để tìm ra dữ liệu thỏa mãn điều kiện.
2. Không đảm bảo tính SARGable (Sử dụng hàm trên cột)
SARGable (Search-ARGument-ABLE) là khả năng DBMS sử dụng được chỉ mục cho câu lệnh. Việc áp dụng một hàm (function) trực tiếp lên cột đã được đánh index sẽ phá vỡ cấu trúc này.
- Ví dụ: Viết WHERE YEAR(created_at) = 2026 thay vì so sánh khoảng ngày tháng WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01'. Lúc này, giá trị sau khi qua hàm không còn khớp trực tiếp với cây chỉ mục, ép hệ thống phải dùng Full Table Scan.
3. Toán tử LIKE với Wildcard ở đầu chuỗi (%keyword)
Cấu trúc cây B-Tree của Index được thiết kế tối ưu cho tìm kiếm theo tiền tố (prefix) từ trái sang phải. Nếu bạn viết câu lệnh dạng WHERE ten LIKE '%nguyen' (đặt ký tự % ở đầu), index thông thường sẽ vô tác dụng vì hệ thống không biết bắt đầu dò từ đâu trong cây chỉ mục, dẫn đến phải quét chuỗi con trên toàn bảng.
4. Bảng có kích thước quá nhỏ (Small Tables)
Như đã đề cập ở phần bản chất kỹ thuật, với những bảng có quá ít trang dữ liệu, Optimizer hoàn toàn có thể chủ động tính toán và thấy rằng Full Table Scan tốn ít chi phí (rẻ hơn) việc phải tra cứu qua index. Đây là hành vi vận hành bình thường và tối ưu của hệ thống, không phải lỗi cần khắc phục.
5. Lạm dụng SELECT * và thiếu điều kiện lọc
Sử dụng SELECT * thay vì chỉ định rõ các cột cần thiết, kết hợp với việc thiếu vắng mệnh đề WHERE chặt chẽ, khiến Optimizer không có căn cứ giới hạn phạm vi dữ liệu. Việc kéo toàn bộ cột cũng làm mất cơ hội sử dụng Covering Index (chỉ mục bao phủ), biến quét toàn bảng thành lựa chọn duy nhất.
6. Index bị "lão hóa" do thống kê chưa được cập nhật
Sau các chiến dịch Insert/Update/Delete khối lượng lớn, cấu trúc dữ liệu thay đổi nhưng nếu hệ thống không được bảo trì cập nhật thống kê (chạy lệnh ANALYZE trong PostgreSQL hoặc UPDATE STATISTICS trong SQL Server), Optimizer sẽ dựa trên bộ dữ liệu cũ rích để tính toán, từ đó đưa ra Execution Plan cực kỳ kém tối ưu.
Cách phát hiện Full Table Scan qua Execution Plan
Để chẩn đoán chính xác hệ thống có đang bị "vắt kiệt" bởi Full Table Scan hay không, các DBA không bao giờ đoán mò mà luôn dựa vào Execution Plan (Kế hoạch thực thi).
1. Sử dụng lệnh EXPLAIN / EXPLAIN ANALYZE (PostgreSQL, MySQL)
- Trong PostgreSQL: Bằng cách thêm tiền tố EXPLAIN ANALYZE trước câu truy vấn, hệ thống sẽ trả về kế hoạch thực thi thực tế. Nếu bạn thấy thao tác Seq Scan (Sequential Scan) xuất hiện ở cấp độ cao nhất của một bảng lớn, đó chính là Full Table Scan.
- Trong MySQL: Sử dụng lệnh EXPLAIN <query>. Hãy chú ý đến cột type. Nếu cột này trả về giá trị ALL, điều đó khẳng định MySQL đang phải quét toàn bộ các dòng của bảng để tìm kết quả.
2. Phân tích qua Actual Execution Plan trong SQL Server (SSMS)
Công cụ SQL Server Management Studio (SSMS) cung cấp giao diện đồ họa rất trực quan cho Execution Plan. Hai nút đồ họa (node) bạn cần đặc biệt cảnh giác là:
- Table Scan: Hệ thống đang quét toàn bộ một bảng Heap (bảng không có Clustered Index).
- Clustered Index Scan: Dù có chữ "Index", nhưng nếu đi kèm với một mệnh đề WHERE kém hiệu quả, thao tác này thực chất vẫn là quét toàn bộ các trang dữ liệu mức lá (leaf level) của bảng, tương đương với hệ quả của Full Table Scan.
3. Giải mã các chỉ số (Metrics) quan trọng
Khi đọc Execution Plan, đừng chỉ nhìn vào loại thao tác mà hãy phân tích sâu các chỉ số sau:
- Cost (Chi phí): Tỷ trọng chi phí tài nguyên (CPU, I/O) mà Optimizer tính toán cho bước quét bảng so với tổng thể truy vấn.
- Rows Examined vs. Rows Returned (Dòng quét thực tế vs. Dòng trả về): Đây là bằng chứng rõ ràng nhất của sự lãng phí. Nếu hệ thống phải đọc (Examined) tới 1.000.000 dòng chỉ để lọc và trả về (Returned) 5 dòng kết quả, bạn đang đối mặt với một vấn đề nghiêm trọng.