MongoDB大数据排序索引如何建立?

340 阅读7分钟

MongoDB大数据排序索引建立

写在前面:MongoDB 支持哪些类型的索引?

MongoDB 支持多种类型的索引,包括单字段索引、复合索引、多键索引、哈希索引、文本索引、 地理位置索引等,每种类型的索引有不同的使用场合。

  • 单字段索引:  建立在单个字段上的索引,索引创建的排序顺序无所谓,MongoDB 可以头/尾开始遍历。
  • 复合索引:  建立在多个字段上的索引,也可以称之为组合索引、联合索引。
  • 多键索引 :MongoDB 的一个字段可能是数组,在对这种字段创建索引时,就是多键索引。MongoDB 会为数组的每个值创建索引。就是说你可以按照数组里面的值做条件来查询,这个时候依然会走索引。
  • 哈希索引 :按数据的哈希值索引,用在哈希分片集群上。
  • 文本索引:  支持对字符串内容的文本搜索查询。文本索引可以包含任何值为字符串或字符串元素数组的字段。一个集合只能有一个文本搜索索引,但该索引可以覆盖多个字段。MongoDB 虽然支持全文索引,但是性能低下,暂时不建议使用。
  • 地理位置索引:  基于经纬度的索引,适合 2D 和 3D 的位置查询。
  • 唯一索引 :确保索引字段不会存储重复值。如果集合已经存在了违反索引的唯一约束的文档,则后台创建唯一索引会失败。
  • TTL 索引 :TTL 索引提供了一个过期机制,允许为每一个文档设置一个过期时间,当一个文档达到预设的过期时间之后就会被删除。

实践过程(可以跳过直接看结果)

最近遇到一个mongo查询并排序的问题,数据量也还好,目前就不到500W,但是性能十分不理想,以前对mongo索引只有最基础的认知,怎么建立查询索引,按照以前建查询索引的经验建立发现,在排序字段没有索引的情况下,mongo内部是先查询,然后对结果集再进行排序操作,由于查询的结果集仍然有数万条数据,排序就会很慢,也需要数秒甚至数十秒才能返回结果;所以就开始研究如何建立索引能又查询又排序; 目前按产品需求要解决如下问题: 1、根据活动查询后根据价格排序 2、根据品类搜索后根据价格排序 3、根据商品名称搜索后根据价格排序

先解决第一个问题-根据活动查询后根据价格排序

要查询的表主要有如下字段:commodityName(商品名称)、activityInfoId(活动id)、activityPrice(商品价格)、category(商品类目)

目前数据库唯一索引有:_id、activityInfoId_1,

image.png

对数据通过活动查询并根据价格排序,发现速度很快,再看执行计划:

image.png

image.png

会走activityInfoId_1索引先查询再排序,由于每个活动的商品数量是一定的,最多也就几千个,所以速度很快,第一个问题就目前而言算是解决了(后面会有索引冲突问题,后文会讲);

再来解决第二个问题--通过类目查询

类目是没有索引的,所以我按活动的解决方式来,先建立了category(类目)索引,搜索排序后发现任然要好几十秒,因为通过类目查询到的商品有的要数十万,在数据库排序太慢了,紧接着我就将排序字段也建立了索引category_1_activityPrice_1

image.png

image.png

命中了索引同时进行了查询和排序操作,很nice

接下来就是商品搜索了

发现通过老方法建立commodityName_1_activityPrice_1索引不管用了!!原因是模糊搜索会试mongo的排序不走索引,只有匹配会走,如果匹配到的结果集很多,那它排序就非常非常慢!,接着我就想,既然排序慢,那我把排序字段放前面,先排序,再查询,修改后索引:activityPrice_1_commodityName_1,结果是:

image.png

因为排序快了,但是扫描要全表扫,除非是带了分页的

image.png

但是这个就取决于查询命中的早晚了,如果排序完后,命中的数据在文档集的最后面,mongo仍然会进行全表扫描,例如模糊搜索:“高级感一粒扣休闲小个子”,查询结果超时,因为他在超时时间内(我设的60s)没有扫到足够的结果, 但是模糊搜索“高级”,就1.6秒出结果,因为他在1.6秒扫到了11条满足条件的结果就直接返回了,这样建索引的话不就成了靠用户搜索词的命中运气决定快慢了,显然不行,而且,最重要的是:activityPrice_1_commodityName_1会影响到前面两个场景,因为mongo一次聚合只能使用一个所有,不能查询走查询的,排序走排序的,所以上述两个场景都变的巨慢,因为有了排序字段索引后,它会默认走排序字段,然后在查询,就导致所有的查询条件都变成了全表扫,巨坑!!!并且我还没找到聚合如何指定索引的操作

image.png

所以我还是选择了commodityName_1的索引,没有选择commodityName_1_activityPrice_1的复合索引是因为商品模糊后排序压根不会触发,所有删了省点空间!这样以来就取决于用户搜索关键字命中的结果集大小,目前这已经是我研究出的最优解了,加之产品在前端做了限制,最少两个字,来减少命中结果集大小!

总结

1、想利用索引实现匹配+排序,需要建立匹配条件+排序的索引,例如,查询活动下的商品并根据价格排序,就建立(活动+价格)排序字段的复合索引,需要注意的是:

如果有两个查询条件:活动和类目,然后再根据价格排序,那么我们建立(活动+类目+价格)的索引,但是如果你筛选条件只传了活动字段,就不会命中该索引,因为价格排在第三个,必须前两个都命中才会走价格的索引;

如果建立了(活动+价格)的索引,但是搜索条件是活动加类目,再根据价格排序,mongo会先进行活动的搜索并根据结果集排序,然后在根据类目进行扫描,速度的快慢就取决于结果集的大小。

2、索引会优先走sort字段的索引,先排序再筛选,所以建立索引一定不要把排序字段放在复合索引的最开始,或者单独给排序字段建索引,否则排序走索引了,扫描就成全表扫了,只要带了有索引的排序字段,查询都会走排序字段的索引,即使走其他查询索引先查询再排序会更快。(当然某些情况也会快,分页的话就取决于命中顺序了)

3、模糊匹配会使索引排序失效,gt,lt也一样,会先走查询,再对结果集进行排序,整个聚合的速度就取决于查询之后结果集的大小,目前无解,模糊匹配之后再排序只能想办法缩小查询的结果集(我没有研究出来哈,如果有大神指点下不甚感激!!