mongodb中给索引排序的理解和索引生效情景


一、 先从创建单字段索引,认识“索引排序”

1.1、给字段加索引的语法

db.集合名.createIndex(要给哪一列创建索引 [,额外选项]);

第1个参数是 “给哪一列创建索引”,它的格式是: {key:1或-1},其中1表示升序,-1表示降序。

第2个参数是 “额外选项”,它是可选的,比如:设置索引名称、指定索引类型等等。

举例:
db.集合名.createIndex({age:1}) ; —> 表示在age字段上创建索引并按照升序的方式存储索引数据。

注意:
索引也是一堆数据,索引是一些按照指定规则排序的数据, 最终也是被存储起来的, 也是占用磁盘空间的。

总结:
所以,在创建索引的时候要指定一个存储顺序(1升序, -1降序) ,告诉它是升序存储还是降序存储。

二、给索引数据排序,意义何在 ?

前言

  1. 在MongoDB中,排序操作,可以通过从索引中按照索引的顺序获取文档的方式,来保证结果的有序性。

  2. 如果MongoDB的查询计划器(planner)没法从索引中得到排序顺序,那么它就需要在内存中对结果排序,相比于“不用索引的排序”操作,用索引会有更好的性能。

  3. 关键是:不用索引的排序操作,会在用了超过32MB内存时终止,也就是说MongoDB只能支持32MB的非索引排序 ,如果数据量很大,比如我目前的应用场景下,都是千万级数据量,而且飞速增加,达亿级……

  4. 用索引对查询结果进行排序,索引,是以升序(1) 或 降序(-1) 的排序顺序,存储对字段的引用。

2.1、单字段索引的排序

如果在单字段上是升序索引或降序索引,则对该字段的排序操作可以是任一方向。

 例如:在集合records的a字段上创建一个升序索引:db.records.createIndex( { a: 1 } ) ; 

            上面的索引可以支持在 a 字段上的升序排序:db.records.find().sort( { a: 1 } ) ;   

            上面是索引还可以通过按相反的顺序遍历索引,来支持在 a 字段上的降序排序:db.records.find().sort( { a: -1 } ) ; 

对于单字段索引,字段的排序顺序并不重要,因为MongoDB可以在任意方向遍历索引。

但是,对于复合索引,排序顺序在确定索引是否支持排序操作时很重要。

2.2、复合索引的排序

你可以指定在索引的所有字段或者部分字段上排序。

用复合索引排序时,sort() 中所指定的排序字段有以下两点需要遵循,否则不会走索引排序:

(1)排序字段的 “排列顺序”,必须和它们在索引中的排列顺序一致。

         划重点:前后顺序,必须一致。

         例如,索引 { a: 1, b: 1 } ,可以支持 { a: 1, b: 1 } 上的排序,但不支持 { b: 1, a: 1 } 上的排序。

(2)排序字段的 “排序顺序”,必须和索引中的对应字段的排序顺序完全相同或完全相反。

         划重点:升降序顺序,要么完全一样、要么完全相反。

         例如,索引 { a: 1, b: -1 } ,可以支持 { a: 1, b: -1 }  和  { a: -1, b: 1 }  的排序,但不支持 { a: -1, b: -1 } 或 { a: 1, b: 1 } 的排序。 



 实例:events集合中包含username和date字段,建立复合索引 db.events.createIndex( { "username" : 1, "date" : -1 } ) ,它可以支持下面两种排序操作: 

           db.events.find().sort( { username: 1, date: -1 } )  先按username升序再按date降序,它和索引字段的升降序顺序完全一样。

           db.events.find().sort( { username: -1, date: 1 } )  先按username降序再按date升序,它和索引字段的升降序顺序完全相反。

          上面的索引不支持 db.events.find().sort( { username: 1, date: 1 } )  先按username升序再按date升序的排序,因为它和索引字段的升降序顺序 既不完全一样、也不完全相反。

2.3、 排序与索引前缀

 如果“排序字段”能和“索引字段或索引前缀”对应起来,那MongoDB就可以使用索引来对查询结果进行排序。

 复合索引的前缀是指被索引字段的子集,它由一个或多个排在最开始的字段组成。

 例如,在集合 data 上创建一个复合索引:db.data.createIndex( { a:1, b: 1, c: 1, d: 1 } )  那么这个索引的前缀如下:

           { a: 1 }

           { a: 1, b: 1 }

           { a: 1, b: 1, c: 1 }

          下面的查询和排序操作,都可以使用索引前缀来排序查询结果。

          也就是说下面这些操作都不需要在内存中对结果集排序。

例子

2.3.1、索引前缀

符合最左前缀原则,走索引,不需要在内存中对结果集在进行一次排序操作

db.data.find().sort( {
    
     a: 1 } )	 索引:{
    
     a: 1 }
db.data.find().sort( {
    
     a: -1 } )	{
    
     a: 1 }
db.data.find().sort( {
    
     a: 1, b: 1 } )	{
    
     a: 1, b: 1 }
db.data.find().sort( {
    
     a: -1, b: -1 } )	{
    
     a: 1, b: 1 }
db.data.find().sort( {
    
     a: 1, b: 1, c: 1 } )	{
    
     a: 1, b: 1, c: 1 }
db.data.find( {
    
     a: {
    
     $gt: 4 } } ).sort( {
    
     a: 1, b: 1 } )	{
    
     a: 1, b: 1 }

2.3.2、索引非前缀

索引也支持使用非前缀字段来排序。不过这种情况是有前提的,就是:查询语句中 必须 把排序字段之前的所有前缀字段,都加上相等条件。
例如,集合 data 有 { a: 1, b: 1, c: 1, d: 1 } 索引,如下操作可以使用索引来排序:

例子

db.data.find( {
    
     a: 5 } ).sort( {
    
     b: 1, c: 1 } )	{
    
     a: 1 , b: 1, c: 1 }
db.data.find( {
    
     b: 3, a: 4 } ).sort( {
    
     c: 1 } )	{
    
     a: 1, b: 1, c: 1 }
db.data.find( {
    
     a: 5, b: {
    
     $lt: 3} } ).sort( {
    
     b: 1 } )	{
    
     a: 1, b: 1 }

2.3.3、不走索引的情况

注意:
只有索引中那些排列在排序字段前面的字段,必须在查询语句中有相等匹配条件,其他的索引字段才可以指定其他匹配条件, 如果查询语句 没有 对排列在排序字段前面 或 与之有所重叠的前缀字段指定相等匹配条件,那么操作将 不会 走索引。

例如,如下操作指定了排序 { c: 1 } ,但是查询语句并没有对前缀字段 a 和 b 指定相等匹配:

db.data.find( {
    
     a: {
    
     $gt: 2 } } ).sort( {
    
     c: 1 } ) ;  索引:{
    
     a: 1, b: 1, c: 1, d: 1 }
db.data.find( {
    
     c: 5 } ).sort( {
    
     c: 1 } ) ;    {
    
     a: 1, b: 1, c: 1, d: 1 }

这些操作不会走索引 { a: 1, b: 1, c: 1, d: 1 } ,甚至可能不用索引检索文档。

猜你喜欢

转载自blog.csdn.net/wei1359765074410/article/details/127089570