写点什么

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

  • 2022 年 1 月 12 日
  • 本文字数:2315 字

    阅读完需:约 8 分钟

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

用户头像

还未添加个人签名 2019.07.27 加入

还未添加个人简介

评论

发布
暂无评论
multi-key索引和wildCard索引场景比较