乐趣区

关于mongodb:multikey索引和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 服务构建。愿和大家多交流学习,为社区奉献一份力量!

退出移动版