multi-key索引和wildCard索引場景比較

MongoDB中文社群發表於2022-01-12

本文來自獲得《2021MongoDB技術實踐與應用案例徵集活動》優秀案例獎作品

作者:雷徹



引文


MongoDB早期版本支援multi-key索引,加快陣列檢索,很受程式設計師喜歡;在4.2版本又推出了wildCard索引,支援object和陣列檢索。這兩種索引有相似之處,但在功能上wildCard更強大。日常工作中,有同學對這兩種索引的使用場景比較模糊,因此在這裡拋磚引玉,供大家借鑑。  


Multi-key index


multi-key 支援對陣列的高效查詢。


舉例:

    db.employee1.insertMany([{"name":"xiaoming","age":25,"ctime":new ISODate(),goodAt:["mongodb","hbase","c++"]},{"name":"xiaohong","age":28,"ctime":newISODate(),goodAt:["es","java","c++"]},{"name":"xiaoguang","age":29,"ctime":newISODate(),goodAt:["mysql","c++","mongodb"]}])--indexdb.employee1.createIndex({goodAt:1})--查詢db.employee1.find({"goodAt":"mysql"})

    explain的結果中,winningPlan.inputStage.stage為IXSCAN ,走索引goodAt_1。這裡字

    段"mysql"是一個完整的陣列元素。下面再做兩個測試:

     

    侵入查詢測試

    如果陣列元素為json串,不能通過multi-key索引查詢某個元素的屬性

      db.employee1.insertMany([{"name":"a","age":25,"ctime":new ISODate(),"goodAt":[ {database:"mysql", lang:"c++"}, {database:"hbase",lang:"java"},  {database:"tidb",lang:"golang"} ]}])--擷取json屬性,不支援;db.employee1.find({"goodAt":{"database":"mysql"}}).explain() /**走索引,結果為空,沒有滿足條件的元素**/db.employee1.find({"goodAt":{"database":"mysql", "lang" : "c++" }}).explain() /**走索引,結果不為空**

      建議使用如下寫法:

         --遞迴db.employee1.find({"goodAt.database":"mysql"}).explain() /**不走索引,結果不為空**/

        如果要查詢database欄位,只能對 goodAt.database 加索引
          db.employee1.createIndex({"goodAt.database":1})db.employee1.find({"goodAt.database":"mysql"}).explain() /**走索引,結果不為空**/

          tips: 

          • multi-key適用於對陣列進行索引

          • 不能對陣列進行雜湊 

          • 不支援對巢狀的物件進行查詢;



          WildCard   index

                                                      

          上文中,查詢陣列元素某個欄位,就需要對欄位單獨加索引,用起來很不方便。在MongoDB4.2版本引入了wildCard索引,支援物件,陣列的檢索,並且可以侵入元素內部遍歷,非常方便。

          多屬性集合,ok:{k1:v1,k2:v2},對ok建索引


          舉例:

            db.employee2.insertMany([{"name":"xiaoming","age":25,"ctime":new ISODate(),"goodAt":{"database":["mongodb","hbase"],"programLanguage":"c++"}},{"name":"xiaohong","age":28,此時尚未建索引,查詢goodAt某個屬性,可以看到stage為COLLSCAN新增wildCard索引後"ctime":new ISODate(),"goodAt":{"database":"mysql","programLanguage":"java","middleAware":"zookeeper"}},{"name":"xiaoguang","age":29,"ctime":new ISODate(),"goodAt":{"database":"mongodb","programLanguage":"python","web":"nodejs"}}])

            此時尚未建索引,查詢goodAt某個屬性,可以看到stage為COLLSCAN

              db.employee2.find({"goodAt.database": "mysql"}).explain()

              新增wildCard索引後

                --對goodAt建索引db.employee2.createIndex({ "goodAt.$**": 1 })db.employee2.find({"goodAt.database": "mongodb"}).explain()

                在元素"name":"xiaoming"中,goodAt.database欄位的值為陣列,我們看看能否走索引匹配

                  db.employee2.find({"goodAt.database": "mongodb"}).explain()

                  wildCard索引也支援一個multi-key索引,可以對其中的陣列元素進行索引匹配。

                   

                  侵入查詢測試

                  進一步在wildCard索引中的陣列元素下,新增物件,能否走索引?我們在goodAt.database屬性中,增加陣列屬性,做屬下測試,目標是確認wildCard能否在陣列中遞迴;

                    db.employees2.insert({"name":"xiaohong1","age":29,"ctime":new ISODate(),"goodAt":{"database":[{"rdb":"mysql"}, {"nosql":["mongodb","redis"]}, {"newsql":"tidb"} ],"programLanguage":"go" }})db.employee2.find({"goodAt.database.nosql": "mongodb"}).explain()

                    顯然,wildCard索引支援對陣列元素中的檢索。

                      db.employees2.insert({"name":"a","age":29,"ctime":new ISODate(),"goodAt":{"database":{"rdb":"mysql","nosql":"mongodb","newsql":"tidb"},"programLanguage":"go" }})db.employee2.find({"goodAt.database.nosql": 1}).explain()

                      再回到我們multi-key中的例子,把索引改為wildCard,是否可行?

                        db.employee1.dropIndexes('goodAt_1')db.employee1.createIndex({ "goodAt.$**": 1 })db.employee1.find({"goodAt.database":"mysql"}).explain()

                        可以滿足需求。注意:

                        • wildCard不能支援兩層以上的陣列巢狀

                        • wildCard也不支援對如下查詢的索引訪問

                          db.employee1.find({"goodAt":{"database":"mysql"}}).explain()

                           查詢子屬性,建議使用 {"goodAt.database":1} 而不是 {goodAt:{"database":1}} ,對索引更友 好。

                             


                          小結


                          multi-key和wildCard索引分別適用不同的場景,讓entry建模變得更加簡單。在使用時,需要注意:

                          • multi-key索引主要加快陣列遍歷,功能純粹;

                          • wildCard可以侵入遍物件或陣列內部,避免單屬性建立索引,更加靈活;

                          • wildCard不會遍歷連續巢狀兩層以上的陣列; 

                          • 不建議太多層巢狀,儘量控制在3層以內;



                          關於作者: 雷徹

                          搜狐集團資料庫團隊高階運維工程師,具有豐富的資料庫運維經驗,精通資料庫架構設計、效能優化及故障診斷,目前負責MySQL及MongoDB運維管理工作,並參與公司資料庫雲平臺開發建設,將運維經驗整合到公司資料庫雲平臺中。專注於CDC服務構建。願和大家多交流學習,為社群貢獻一份力量!



                          來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69961190/viewspace-2851960/,如需轉載,請註明出處,否則將追究法律責任。

                          相關文章