本文以一個真實的全文檢索查詢為例,展示從「無索引 → 全表掃描」到「建立全文索引/複合索引 → 索引搜尋」的完整優化過程,解讀 SET STATISTICS IO ON 輸出的邏輯讀取變化,掌握索引優化的核心判斷依據。
SELECT TOP 50
ROW_NUMBER() OVER (ORDER BY ResumeCreateTime DESC) AS RowID,
TopDegree, DegreeRankID, UserResume.UserResumeID, UserResume.UserID,
ResumeName, BirthDate, WorkStartedDate, SalaryNeeded,
BufferTimeSpanID, ResumeCreateTime
FROM dbo.UserResume
INNER JOIN dbo.WorkExperience ON WorkExperience.UserResumeID = UserResume.UserResumeID
WHERE CONTAINS(WorkExperience.WorkSummary, '經理') OR CONTAINS(UserResume.ResumeName, '簡歷');
Table 'Worktable'. Scan count 0, logical reads 0 Table 'UserResume'. Scan count 1, logical reads 18,524 Table 'WorkExperience'. Scan count 1, logical reads 8,679 (1 row affected)
分析:
— UserResume 18,524 邏輯讀 = 全表掃描 (Clustered Index Scan / Table Scan)。
— WorkExperience 8,679 邏輯讀 = 全表掃描。
— CONTAINS 謂詞無全文索引支援,退化為 LIKE 掃描 (或報錯需全文索引)。
— 總邏輯讀 ~27K,隨資料增長線性惡化。
CONTAINS / FREETEXT **必須**有全文索引才能高效執行。
-- 建立全文目錄 (若不存在)
CREATE FULLTEXT CATALOG FTCatalog AS DEFAULT;
-- UserResume.ResumeName 全文索引
CREATE FULLTEXT INDEX ON dbo.UserResume (ResumeName LANGUAGE 1028) -- 1028 = 繁體中文
KEY INDEX PK_UserResume_UserResumeID -- 必須有唯一索引作為全文鍵
ON FTCatalog WITH CHANGE_TRACKING AUTO;
-- WorkExperience.WorkSummary 全文索引
CREATE FULLTEXT INDEX ON dbo.WorkExperience (WorkSummary LANGUAGE 1028)
KEY INDEX PK_WorkExperience_WorkExperienceID
ON FTCatalog WITH CHANGE_TRACKING AUTO;
-- UserResume: 連接鍵 + 排序欄位 + 篩選欄位覆蓋
CREATE NONCLUSTERED INDEX IX_UserResume_Join_Sort_Cover
ON dbo.UserResume (UserResumeID) INCLUDE (ResumeCreateTime, TopDegree, DegreeRankID, UserID, ResumeName, BirthDate, WorkStartedDate, SalaryNeeded, BufferTimeSpanID);
-- WorkExperience: 連接鍵 + 全文鍵已涵蓋,若有額外篩選可加入
CREATE NONCLUSTERED INDEX IX_WorkExperience_Join_Cover
ON dbo.WorkExperience (UserResumeID) INCLUDE (WorkSummary);
| 指標 | 優化前 | 優化後 (預期) | 改善幅度 |
|---|---|---|---|
| UserResume 邏輯讀 | 18,524 (Scan) | ~200 (Index Seek + Key Lookup / Covered) | 99%↓ |
| WorkExperience 邏輯讀 | 8,679 (Scan) | ~50 (Fulltext Index Seek + Join) | 99%↓ |
| 查詢耗時 | 秒級 | 毫秒級 | 數量級↓ |
| CPU 時間 | 高 | 極低 | 顯著↓ |
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO
-- 執行優化後查詢
SELECT TOP 50 ... (同上)
GO
SET STATISTICS IO OFF;
SET STATISTICS TIME OFF;
CHANGE_TRACKING AUTO 非同步更新,大量匯入時可 ALTER FULLTEXT INDEX ... SET CHANGE_TRACKING MANUAL → 批次完成後 START FULL POPULATION / START INCREMENTAL POPULATION。sys.dm_db_index_physical_stats 定期檢查 avg_fragmentation_in_percent > 30% 重建,5-30% 重組。sys.dm_db_index_usage_stats 監控 user_seeks + user_scans + user_lookups = 0 且 user_updates > 0 的索引,評估刪除。user_updates / (user_seeks+scans+lookups) 比率評估 ROI。AUTO_UPDATE_STATISTICS ON、大表啟用 AUTO_UPDATE_STATISTICS_ASYNC ON、維護窗口手動 UPDATE STATISTICS ... WITH FULLSCAN。索引優化是「測量 → 假設 → 驗證 → 迭代」的科學過程,STATISTICS IO/TIME 是最誠實的量儀。