昨天介紹到建立單一個欄位索引的方式,今天我來介紹如何建立複合索引(Compound Index),以及它背後運行的規則ESR rule (equality-sort- range rule),另外搭配explain來觀察我們下的搜尋語法,是否有真的命中索引。
在開始之前先跟大家介紹一下,我目前很常用的應用程式mongodb compass,這是MongoDB提供的應用程式,除了用來顯示目前資料庫內的資料,這個軟體提供幾個蠻好用的功能。
有時候在撰寫程式碼不知道如何寫query,或是不確定自己設定的索引是否合適,就可以利用mongodb compass實際下query觀察看看。
例如:我們進到collection的頁面,點選Documents可以看到目前collection內的資料,同時也可以透過上方的filter,填入要搜尋資料的條件。
點選Aggregations可以使用之前我們有介紹過的aggregatem語法,實際跑跑看是否正確撈到想要的資料,也可以點選右上方的Explain去看跑起來的效率,或是是否用使用到index。
點選Indexs可以看到目前有建立的索引,在這裡可以看到我們建立索引的大小、類型,同時也可以在這裡直接新增或刪除索引。
以及我們今天主要會介紹到的Explain Plan,它會分析query是否命中索引、搜尋的效率並且回傳相關的資訊,可以作為改寫index或query的參考。
現在假設我們依舊有一個專門紀錄商品資料的collection,裡面的資料格式如下。
// 範例資料,collection裡面有100筆商品資料
{
_id: "632fce925476003ff0d03743",
name: "鍵盤",
price: 3866,
amount: 73,
category: "3C"
}
這時我建立一個category_1_price_1_amount_1的索引,此時category、price、amount建立的順序是有意義的。
如果我們下這樣的query指令
find({ category: “3C”, amount: { $gte: 3 } }).sort({ price: 1 })
正好會符合我們上面建立好的索引,原因是因為我們使用索引第一個欄位category做相等(equality)的搜尋條件,使用第二個欄位price進行資料的排序(sort),第三個欄位amount進行範圍搜尋( range),剛好有符合MongoDB建立索引的ESR rule條件。
假設我們換一個query指令,改成
find({ category: “3C”, price: { $lt: 3000 } }).sort({ amount: -1 })
變成不符合原本category_1_price_1_amount_1索引的設定,再進行搜尋時,只會使用到一部分的索引category_1_price_1,排序的部分,會使用到記憶體進行排序,在Explain的結果也有特別寫到Sorted in Memory: yes。
按照官方文件的說法,當要進行資料排序時,如果沒有合適的索引,或是排序的欄位本身並沒有建立索引,這時候會進行blocking sort,它執行排序的效率,比使用索引進行排序還要差。
如果要進行排序的資料大小超過 100 MB,並且沒有透過allowDiskUse() 設定可以採用硬碟寫入暫時性的資料,MongoDB會回傳錯誤訊息,如果有開啟設定,則會採用硬碟寫入資料進行排序,但是執行query的效率就會變差。
因此還是建議在建立索引時,務必確保有遵守ESR rule
參考資料
The ESR (Equality, Sort, Range) Rule
Use Indexes to Sort Query Results
Sort and Index Use
本篇文章同步放在我的部落格,大家有空可以進來逛逛