mongo3.2 数组索引作为联合索引的一部分导致的范围查询问题
2017-07-23 12:03
381 查看
摘要
在mongo索引一文中有介绍过mongo数组索引,这边主要介绍在mongo3.2中遇到的一个将array字段作为一个联合索引的字段,然后进行非array字段的范围查询遇到的问题。问题
post collection{ "_id" : ObjectId("5972c7b0a98a215bd51b9f9a"), "son" : [ 1, 2 ], "age" : 1, "name" : "stone", "created" : ISODate("2017-07-23T03:42:20.201Z") }
索引son_age:
{son:1,age:1}
范围查询
db.getCollection('post').find({son:1,age:{$gt:1,$lt:30}}).explain("executionStats")
"inputStage" : { "stage" : "IXSCAN", "nReturned" : 1, "executionTimeMillisEstimate" : 0, "works" : 2, "advanced" : 1, "needTime" : 0, "needYield" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "son" : 1, "age" : 1 }, "indexName" : "son_age", "isMultiKey" : true, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "son" : [ "[1.0, 1.0]" ], "age" : [ "[-1.#INF, 30.0)" ] },
但是发现index bound并没有如预想的那样两边都取到,而只取到了上限,然后做filter。这样当数据量多的时候,扫描的index就多了很多,必然会导致性能问题。之前在线上就遇到这样的问题,按照日期进行刷选,查询很慢。
分析
首先在mongo3.4中执行了一把,看到不一样的结果"inputStage" : { "stage" : "IXSCAN", "nReturned" : 0, "executionTimeMillisEstimate" : 0, "works" : 1, "advanced" : 0, "needTime" : 0, "needYield" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "son" : 1, "age" : 1 }, "indexName" : "son_age", "isMultiKey" : true, "multiKeyPaths" : { "son" : [ "son" ], "age" : [] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "son" : [ "[1.0, 1.0]" ], "age" : [ "(1.0, 30.0)" ] },
mongo3.4边界取的是对的,而且多了一个multiKeyPaths,指定了哪个是array字段
"multiKeyPaths" : { "son" : [ "son" ], "age" : [] },
因为mongo3.4中解决了,所以是mongo本身的一个bug,在jira中找到这条
https://jira.mongodb.org/browse/SERVER-15059
3.2及其以前的版本不会保存多键索引的哪个字段是array,所以对于像上面age这样的标量字段,mongo也不能确定table中是否有可能这个字段是否是array。所以会将age:{gt:1,lt:30}当成两个条件去查询,所以索引边界只能有一个。3.4版本进行了优化,增加了multiKeyPaths来存储哪个字段是多键索引字段。
扩展
这个优化方法治标不治本,因为mongo 虽然限定了一个联合索引中只有一个数组字段,但是因为mongo 是free schema.只要保证在一个文档中只有一个数组即可,而不必是同一字段。所以像上述问题,如果age字段也有值是array,那效果就和之前的一样的了。注意multiKeyPaths字段。"inputStage" : { "stage" : "IXSCAN", "nReturned" : 3, "executionTimeMillisEstimate" : 0, "works" : 5, "advanced" : 3, "needTime" : 1, "needYield" : 0, "saveState" : 0, "restoreState" : 0, "isEOF" : 1, "invalidates" : 0, "keyPattern" : { "son" : 1, "age" : 1 }, "indexName" : "son_age", "isMultiKey" : true, "multiKeyPaths" : { "son" : [ "son" ], "age" : [ "age" ] }, "isUnique" : false, "isSparse" : false, "isPartial" : false, "indexVersion" : 1, "direction" : "forward", "indexBounds" : { "son" : [ "[1.0, 1.0]" ], "age" : [ "[-inf.0, 30.0)" ] },
总结
这个问题在mongo3.4中有修复,但是还拿出来讲,是觉得mongo数据库还是会有很多不完善的地方,在实际开发过程中不要想当然,查询还是要跑下查询计划,才能确保万无一失。另外又是吐槽mongo的时候了,free schema有一定的好处,但是也不能太free了,像数组联合索引这个,不同字段都可以,只要保证同一个document只有一个数组,这个比较坑。实际开发中,还是做个ORM。为collection建个映射吧,要不乱插入一个不同类型的字段,就危险了
参考
https://docs.mongodb.com/manual/core/multikey-index-bounds/https://jira.mongodb.org/browse/SERVER-15059
相关文章推荐
- 一次索引导致查询缓慢问题
- mongo数组内唯一索引的问题
- CodeIgniter需要注意错写查询条件导致数据库索引失效的问题
- MongoDB索引文件破坏后导致查询错误的问题
- 数组在for循环中使用splice方法导致索引错乱的问题
- 范围查询如何使用复合索引的专题报告
- Oracle问题小记五:服务启动-索引-子查询-分页存储过程
- SqlServer中的SmallDatetime作为条件查询的截至日期问题!
- mysql分组查询结果集作为条件查询的问题
- 区间查询(树状数组之差点问线问题)
- Lucene 的四大索引查询 ——bool 域搜索 通配符 范围搜索
- 数组名、数组名取地址以及作为函数传递带来的问题
- in_array()大数组查询性能问题
- java中的String.split() 中“|”作为分隔符的问题和数组长度问题
- 解决MySQL中IN子查询会导致无法使用索引问题
- 关于数组名作为指针的问题
- SQLite对字符串字段建立索引后查询速度无优化的问题
- 使用索引的误区之二:使用了 和 != 操作符,导致查询不使用索引
- 通过hibernate session.connection()获得数据库连接时,导致的查询缓慢甚至假死机问题
- MongoDB查询字段没有创建索引导致的连接超时异常解案例分享