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