AWS實名帳號開通 AWS DocumentDB (MongoDB 兼容) 查詢緩慢与索引失效排查

亞馬遜雲AWS / 2026-08-04 15:11:29

先分清楚:慢的是查詢,還是整個庫都慢

很多人一遇到 AWS DocumentDB 查詢變慢,第一反應就是去補索引,結果忙了一圈,速度還是沒起來。原因往往很簡單:問題不一定出在索引,可能是查詢寫法、資料型態、排序方式、資料分布,甚至是實例壓力。排查之前,先把現象說清楚。是只有某一條查詢慢,還是同一張集合上的大部分查詢都慢;是某個時間段慢,還是長期都慢;是剛寫入後慢,還是歷史資料查詢慢。這些差異,決定你該先看索引、執行計畫,還是資源與負載。

DocumentDB 雖然兼容 MongoDB,但它不是原封不動的 MongoDB。這句話很重要。兼容代表語法和常見使用方式大體相近,不代表查詢優化器、索引選擇、統計資訊更新節奏都完全一致。很多在 MongoDB 上表現穩定的寫法,到了 DocumentDB 可能就不再走你預期的索引。反過來看,有些人看到索引已經建好,就直覺認定查詢應該很快,這種判斷在 DocumentDB 上尤其容易出錯。

先理解 DocumentDB 的幾個常見差異

兼容不等於行為一致

DocumentDB 的索引機制、查詢規劃、資料統計與 MongoDB 並非完全相同。也就是說,你看到的是同樣的集合、同樣的欄位、同樣的索引名稱,但最終執行路徑可能不一樣。尤其是條件複雜、排序和篩選同時存在、或查詢結構有多層嵌套時,優化器可能會選擇一條看似保守、實際卻很慢的路。

AWS實名帳號開通 因此,排查時不要只問一個問題:有沒有索引。更要問:這條查詢能不能被這個索引精準命中;即便能命中,是否需要掃很多索引鍵;即便掃到索引,是否還要回表取大量文件;最後回傳的資料量是不是太大,導致網路與反序列化成了瓶頸。真正影響速度的,從來不是索引這一個點。

AWS實名帳號開通 常見誤區不是索引沒建,而是索引沒被選中

在實務裡,所謂索引失效,很多時候其實不是索引壞了,而是查詢計畫沒有選它。這種情況下,索引依然存在,也沒有錯,只是對這條查詢而言,優化器認為別的路更便宜。問題是,優化器的判斷並不總是符合人的直覺,尤其當資料量增長、欄位分布改變、或查詢條件變得更複雜時,原本好用的索引可能突然就不再好用。

所以排查時,不要被表象騙了。看到集合有索引,不能直接下結論;看到查詢沒有報錯,也不能等同於效率正常。你要觀察的是實際執行路徑、掃描的數量、返回的數量,以及這條查詢是否因為某個細節被迫走了全掃。

排查順序要對,別一上來就重建索引

第一步:還原成最原始的查詢

先把應用層的複雜包裝拆掉,只保留最核心的 filter、sort、limit、projection。很多慢查詢不是查詢條件本身慢,而是外層多做了幾層加工,例如先查出一大批資料,再在應用程式裡二次過濾和排序。這樣即使底層查詢用了索引,上層也還是慢。

還原查詢還有一個好處:你可以清楚看出每個條件是否必要。某些條件只是業務上方便,實際卻讓索引難以命中。比如在一個主鍵很明確的查詢裡,又附加了一個低選擇性的狀態欄位,這個狀態欄位不但幫不上忙,還可能讓優化器做出更差的選擇。

第二步:看執行計畫,不要只看結果

只看查詢結果,永遠看不出問題在哪。真正有價值的是執行計畫。你要重點觀察幾個指標:掃描了多少文件、命中了多少索引鍵、最終返回多少筆資料、是否出現大量的過濾再過濾。當掃描量遠大於返回量時,通常代表索引沒有真正幫上忙。

如果你的工具或版本支援執行計畫輸出,請把每次優化前後的結果做對比。不要只憑體感說快了。很多時候,一次看似有效的調整,可能只是因為當時快取命中,或者那批資料剛好很少。真正有用的,是在相同條件下比較執行路徑有沒有變短。

第三步:用最小成本驗證索引是否有效

當你懷疑某個索引沒被用上,可以做一個非常簡單的對照:在相同條件下,分別測試有無特定索引時的執行表現,或者用強制方式驗證該索引是否能顯著縮短掃描範圍。若強制使用後速度明顯改善,說明問題多半不在索引本身,而在查詢形態或優化器選擇。

如果強制走索引也沒有明顯改善,那就要回頭看資料分布與返回量。索引只能幫你縮小搜索範圍,不能讓一條本來就要回傳大量資料的查詢憑空變快。當結果集過大時,真正慢的可能是資料傳輸、序列化、客戶端處理,甚至是下游接口。

最常見的索引失效原因,往往都很具體

欄位型態不一致

這是最容易被忽略的一種。索引建在字串欄位上,查詢時卻傳了數字;資料庫裡存的是時間字串,程式裡拿日期型別去比;某些資料是 null,某些是空字串,查詢又做了不一致的條件判斷。型態不一致的後果,不只是匹配不到,還可能直接讓索引效益大打折扣。

在 DocumentDB 這種兼容資料庫裡,型態問題特別常見,因為很多團隊是從不同系統遷移過來的。舊系統可能一直把 ID 存成字串,新系統改成整數,混用一段時間後,查詢條件就開始分裂。你以為自己在查同一個欄位,其實底層已經是兩種資料世界。

複合索引順序不對

複合索引不是欄位越多越好,順序更重要。一般來說,高選擇性的等值條件應該放前面,範圍條件次之,排序欄位則要和索引方向盡量一致。如果順序錯了,就算索引存在,也可能只能用到前半段,後面的條件還得另外過濾。

常見的情況是,查詢條件裡有狀態、租戶、建立時間三個欄位,團隊隨手建了一個狀態在前的索引。看起來合理,實際上狀態值只有幾種,選擇性很差,優化器反而覺得不划算。真正更有效的,往往是把租戶或業務主鍵放前面,再把時間區間和排序欄位接上去。

對欄位做函數運算

如果你在查詢時對欄位做了轉換,例如去空白、轉小寫、截字串、解析日期,再拿結果去比對,索引通常很難發揮正常效果。原因很直接:索引上存的是原始值,不是你運算後的值。資料庫沒辦法預先知道每筆資料經過函數之後會變成什麼樣,只能退回更大範圍的掃描。

這類問題在搜尋姓名、信箱、編碼時很常見。很多人希望搜尋不區分大小寫,就在查詢端把欄位做小寫化,結果索引直接失去意義。更好的做法,是在寫入時就規範資料格式,或額外存一個標準化欄位,讓查詢直接對標準化欄位建索引。

AWS實名帳號開通 正則與模糊匹配的使用方式不對

不是所有正則都能吃到索引。以前綴開頭的匹配,有機會利用索引;但如果是前面帶萬用字元、或在中間任意匹配,通常就很難走索引。很多慢查詢其實就是一個看似簡單的模糊搜尋,背後卻在掃整個集合。

如果你的業務真的需要模糊搜尋,別寄望索引單獨解決全部問題。你要思考的是資料建模方式:能不能把常用搜尋前綴拆出來,能不能預先建立可查詢的關鍵字欄位,能不能把高頻搜尋條件前移。與其事後救火,不如在模型設計時就避開註定難優化的寫法。

條件組合太複雜,優化器寧可掃描

$or、$in、$ne、$exists 這類條件,並不是不能用,但一旦和其他條件混在一起,計畫選擇就會複雜得多。尤其是 $or 裡面每個分支對應的索引都不一致時,優化器有時會選擇更保守的方案,導致你看到的就是大範圍掃描。

這時候最有效的方法不是盲目加索引,而是拆解查詢。把一條大而全的條件拆成幾個更穩定的分支,分別查出結果後再合併,往往比一條超複雜查詢更容易控速。資料庫擅長的是明確的路徑,不擅長讓它同時猜太多可能性。

資料分布也會讓索引看起來像失效

低選擇性欄位,本來就不是好索引

有些欄位雖然常被查,但值的種類很少,例如狀態、性別、是否啟用。這類欄位的選擇性低,單獨拿來建索引,效果通常有限。當整張表大部分資料都落在同一個值上時,索引命中後還是要掃很多資料,優化器自然不一定願意選。

這不是索引沒用,而是它不是最好的入口。真正該做的,是把低選擇性欄位放在複合索引後面,讓前面的高選擇性欄位先縮小範圍。只拿低選擇性欄位當主鍵式查詢入口,常常是註定吃虧的。

資料量增長後,原本有效的索引會變味

很多系統在初期都很順,原因不是設計多神,而是資料量太小。當資料從幾十萬長到幾千萬,原本掃一小段就能回來的查詢,開始變成掃一大段;原本可接受的回表成本,開始拖垮整體耗時。這就是為什麼索引優化一定要跟著數據規模重新檢視。

如果你只在開發環境測過,卻沒有拿接近真實量級的資料壓過,很多問題根本不會暴露。DocumentDB 的查詢表現,和資料量、節點規格、讀寫壓力都密切相關。到了生產環境才發現慢,通常不是偶然,而是前期驗證不足。

一套實戰排查清單,可以直接照著做

  • 先確認慢查詢的原始條件,包含過濾、排序、分頁與返回欄位。
  • 檢查欄位型態是否一致,尤其是字串、數字、日期和 null 的混用。
  • 對照執行計畫,觀察掃描量是否遠高於返回量。
  • 核對複合索引順序,讓等值條件、範圍條件、排序欄位依邏輯排列。
  • 移除函數、轉換、包裹表達式,改成直接對原始欄位查詢。
  • 檢查正則是否帶前綴,避免前置萬用字元造成全掃。
  • 拆解複雜的 $or 與 $in,確認是否可以分支查詢。
  • 觀察資料分布,判斷索引是否因低選擇性而失去價值。
  • 確認返回集是否過大,避免查詢快了但應用層仍慢。
  • 對比相同查詢在不同時間點的表現,排除快取與瞬時負載干擾。

修復索引,不是重建,而是重做查詢路徑

先改查詢,再改索引

很多團隊習慣先補索引,這是反的。真正有效的順序,應該是先把查詢寫對,再根據查詢模式去設計索引。因為索引是服務查詢的,不是反過來遷就查詢裡那些不合理的寫法。如果查詢本身就有問題,再多索引也只是把錯誤包裝得更漂亮。

當你把查詢整理成幾種固定模式之後,索引設計會清楚很多。哪些是單欄位高頻查詢,哪些是租戶加時間範圍,哪些是排序後分頁,哪些是單筆精準查找。不同模式應該對應不同索引,而不是一個大索引想包辦所有情況。

複合索引要圍繞最常見的訪問路徑

好的索引不是欄位最全,而是命中最穩。你要看的是最常見的查詢路徑,而不是最特殊的一條。把高頻場景做穩,通常比面面俱到更重要。對 DocumentDB 來說,這一點尤其明顯,因為查詢規劃不會幫你補償太多糟糕的模型設計。

例如某個列表頁永遠先按租戶過濾,再按建立時間倒序翻頁,那索引就應該優先服務這個路徑。若還需要看狀態,狀態可以放在合適的位置,但不要為了照顧某個偶發條件,把整個索引結構搞得很散。索引一旦失焦,效果會比沒有好索引更差。

控制返回欄位與分頁方式

很多慢查詢其實不是搜尋慢,而是拿太多資料。你只需要列表中的幾個欄位,卻把整個文件都撈回來,等於把索引的收益又吐回去。能做精簡投影,就不要整包返回。能限制筆數,就不要一次拉太多。能用游標式分頁,就不要依賴深度偏移。

特別是翻到很後面的頁數時,偏移量越大,資料庫要跳過的內容就越多。這種查詢在資料量大時很容易顯著變慢。若業務允許,改成基於排序鍵的游標翻頁,通常比單純的 offset 分頁穩定得多。

定期回看索引,不要一次建好就不管

資料結構會變,查詢模式會變,業務重點也會變。今天高頻的是查狀態,明天可能變成查某個合作方,後天又改成按時間窗口批次拉取。索引如果沒有跟著演進,初期看起來正常,長期一定會失焦。

AWS實名帳號開通 定期回看不是形式,而是必要工作。你要看看哪些索引長期沒被用,哪些查詢突然變慢,哪些欄位分布已經改變到原有設計不再合理。對 DocumentDB 來說,這種回顧特別重要,因為兼容環境下的查詢行為,往往比你想像中更依賴資料形態與路徑設計。

結語:索引問題表面像技術細節,實際是模型問題

AWS DocumentDB 的查詢變慢,表面上看是索引失效,往深一層看,通常是查詢方式、資料型態、索引順序、資料分布和資源壓力共同作用的結果。真正有效的排查,不是看到慢就加索引,而是先弄清楚資料怎麼查、查多少、為什麼查不到最短路徑。只要把執行計畫、條件寫法和索引設計三件事對齊,大多數慢查詢都能找到明確原因。

記住一個核心原則:兼容資料庫最怕的,不是沒有索引,而是沿用舊習慣卻不檢查新行為。當你把每一條查詢都當成需要驗證的路徑,而不是理所當然會快的語句,DocumentDB 的問題通常就不難定位。先確認查詢是否合理,再確認索引是否匹配,最後才是考慮擴容與架構調整,這才是最省力也最穩的做法。

Telegram售前客服
客服ID
@cloudcup
联系
Telegram售后客服
客服ID
@yanhuacloud
联系