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

本文來自獲得《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":new
ISODate(),goodAt:["es","java","c++"]},{"name":"xiaoguang","age":29,"ctime":new
ISODate(),goodAt:["mysql","c++","mongodb"]}
])
--index
db.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服務構建。願和大家多交流學習,爲社區貢獻一份力量!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章