优化排序
在MongoDB里面,如果能够利用索引来排序的话,直接按照索引顺序加载数据就可以了。如果不能利用索引来排序的话,就必须在加载了数据之后,再次进行排序,也就是进行内存排序。
可想而知,如果内存排序,再叠加分页查询的话,性能会更差。比如你要查询skip(10000).limit(100)
,那么在最坏情况下,MongoDB要把所有的文件加载到内存里排序,然后找到从10000
开始的100
条数据
优化的思路也类似MySQL。第一种是把查询优化成利用索引来排序,可以考虑修改查询,也可以考虑修改索引。比如你可以新建索引。
我还优化过一个分页查询。早期的时候,有一个查询是需要排序加分页的,但是最开始数据量不多,所以随便写了也没问题。但是后面数据量上来以后,这个地方查询就越来越慢。看到这个排序加分页的查询,我第一个想法就是这个查询肯定是内存查询,不然不会这么慢。一排查果然是这样,后来创建了一个新的索引,确保排序的时候可以直接利用索引来排序。
另一种优化思路是借鉴在分库分表里提到的禁止跨页查询,也就是每次查询带上上一次查询的极值作为查询条件。
MongoDB的分页查询还有一种优化方式,但是这种优化方式需要业务折中。也就是原来分页向后翻页是通过偏移量来进行的,那么现在可以通过修改查询条件,在查询语句里带上前一页的排序字段的极值。比如我们的查询是根据创建时间
create_time
倒序排序,那么就可以优化成查询条件里上一批最小的create_time
,接近于WHERE create_time <= $last_min_create_time
的语义
注意,这里的极值是最大值还是最小值,跟你的排序有关。
另外你可以进一步把话题引导到MySQL和分库分表上。
总体来说,MongoDB的分页查询面临的问题和关系型数据库分页查询面临的问题差不多,而在分片集合上进行分页查询的问题,也和分库分表的问题差不多。总之,分页查询如果不小心的话,是比较容易出现性能问题的。
既然MongoDB会有这种分页的问题,那么分片情况下处理分页的mongos岂不是容易成为瓶颈吗?
所以就可以考虑增加mongos的数量。
增加mongos数量
如果是分片集合的话,查询都要靠mongos来执行路由,并且合并结果集。
换一句话来说,mongos就是查询的性能瓶颈,它可能是CPU瓶颈、内存瓶颈或网络带宽瓶颈。比如,你有分片查询,那么mongos就必须要求各个分片查询到结果之后,自己再排序,选出全局分页里对应的数据。
因此,在实践中要密切关注查询性能,并且发现查询很慢的时候,就要去看看是不是mongos引起的。
之前我还优化过mongos,不过mongos实例能优化的不多,主要就是增加mongos实例,而且最好是能独立部署mongos,独享系统的CPU和内存资源。
另一种面试的思路是隔离 ,也就是要考虑到mongos本身容易称为性能瓶颈,并且你也不能无限增加mongos实例,所以如果公司资源足够,应该让核心业务使用独立的mongos实例,或者说独立的MongoDB集群。
并且,为了保证核心服务的查询效率和稳定性,我都是单独准备了一个集群给核心服务,这样可以保证核心服务的mongos互相之间没影响