1

multi-key索引和wildCard索引场景比较

 2 years ago
source link: https://mongoing.com/archives/81377
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

multi-key索引和wildCard索引场景比较 | MongoDB中文社区

本文来自《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服务构建。愿和大家多交流学习,为社区贡献一份力量!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK