好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

基于C#在Mongodb的SkipLimit和WhereLimit的分页

基于C#在Mongodb的SkipLimit和WhereLimit的分页

基于C#在Mongodb的Skip-Limit和Where-Limit的分页对比 并且含mongodb帮助类的源码

 最近在设计的日志服务中需要用到Mongodb这个Nosql数据库( 不知道Mongodb的点我 ),由于是用于纯存日志,而且日志量巨大,百万千万级的,所以需要用到它的分页查询。

         不过LZ也是刚刚接触这个数据库,不是很了解里面的命令语法,便在网上查了一些资料,结果 结果说mongodb自带的简单很方便的Skip方式的分页效率很低,无奈,无奈得用其他的,

        有多篇文章都推荐Where+Limit的方式分页,说他效率比Skip方式高多了,但是好多资料都是讲一些思路,并没有很具体,但是也很有帮助拉,现在简单的来讲一下这个分页思路(Skip的方式那么简单就不讲啦):

       假设一张表中(Mongodb用集合来代替)有如下条数据:1,3,4,5,6,7,8,9,20,30,50,51,52,59,60(仅仅标志该记录的ID号 你可以理解为主键)

      现在的也尺寸PageSize=4,那么

 第一页的数据为1,3,4,5,这个用where的方式解释为SQL语句为Select top 4 * from table where id>0 因为上一页是没有记录 所以用0来代替  第二页的数据为6,7,8,9,20,这个用where的方式解释为SQL语句为Select top 4 * from table where id>5 这里的5就是上一页的最后一条记录   第二页的数据为30,50,51,52,,这个用where的方式解释为SQL语句为Select top 4 * from table where id>20 这里的20就是第二页的最后一条记录

     这下就简单了,以后需要分页查询的时候传上一个ID号即可,Mongodb里面的思路也是这样 不过不一样的是c#用mongodb需要用其他驱动来查询数据,就用不了SQL语句了,简单的来贴一下代码

     

View Code

 ///   <summary> 
         ///   分页查询 指定索引最后项-PageSize模式 
          ///   </summary> 
         ///   <typeparam name="T">  所需查询的数据的实体类型  </typeparam> 
         ///   <param name="query">  查询的条件 没有可以为null  </param> 
         ///   <param name="indexName">  索引名称  </param> 
         ///   <param name="lastKeyValue">  最后索引的值  </param> 
         ///   <param name="pageSize">  分页的尺寸  </param> 
         ///   <param name="sortType">  排序类型 1升序 -1降序 仅仅针对该索引  </param> 
         ///   <param name="collectionName">  指定的集合名称  </param> 
         ///   <returns>  返回一个List列表数据  </returns> 
         public  List<T> Find<T>(IMongoQuery query,  string  indexName,  object  lastKeyValue,  int  pageSize,  int  sortType,  string   collectionName)
        {
            MongoCollection <T> mc =  this ._db.GetCollection<T> (collectionName);
            MongoCursor <T> mongoCursor =  null  ;
            query  =  this  .InitQuery(query);

              //  判断升降序后进行查询 
             if  (sortType >  0  )
            {
                  //  升序 
                 if  (lastKeyValue !=  null  )
                {
                      //  有上一个主键的值传进来时才添加上一个主键的值的条件 
                    query =  Query.And(query, Query.GT(indexName, BsonValue.Create(lastKeyValue)));
                }
                  //  先按条件查询 再排序 再取数 
                mongoCursor = mc.Find(query).SetSortOrder( new  SortByDocument(indexName,  1  )).SetLimit(pageSize);
            }
              else  
            {
                  //  降序 
                 if  (lastKeyValue !=  null  )
                {
                    query  =  Query.And(query, Query.LT(indexName, BsonValue.Create(lastKeyValue)));
                }
                mongoCursor  = mc.Find(query).SetSortOrder( new  SortByDocument(indexName, - 1  )).SetLimit(pageSize);
            }
              return  mongoCursor.ToList<T> ();
        } 

    当然这个代码片段不怎么好看,估计各位读者看不大清,放心,下面会附源码下载(最恨那种代码贴一半都不知道说什么的人了)

    既然他们都说Skip效率差,那就自己测试看看呗,眼见为实嘛,

    我先在Mongodb从添加1000W条简单的数据,大数据量下测试才有有效果嘛,   

   

    给看下测试的控制台代码吧,都封装好了看的很方便哦,懒的展开的就不要了,很简单的

    

View Code

 class   Program
    {
          static   MongoDBHelper db;

          static   void  Main( string  [] args)
        {
              //  创建Mongodb的数据库实例 
            db =  new   MongoDBHelper();

              #region  1000W条数据的初始化
             //  InitData(); 
             #endregion  

            Console.WriteLine(  "  Mongodb 中自己的Skip-Limit分页与自定义的Where-Limit分页效率测试(毫秒):  "  );
              //  各种分页 尺寸的测试 具体注释我也不写了  
            PagerTest( 1 ,  100 ); //  这个测试忽略,估计第一次查询之后会相应的缓存下数据  导致之后的查询很快 
            PagerTest( 3 ,  100  );
            PagerTest(  30 ,  100  );
            PagerTest(  300 ,  100  );
            PagerTest(  300 ,  1000  );
            PagerTest(  3000 ,  100  );
            PagerTest(  30000 ,  100  );
            PagerTest(  300000 ,  100  );
            
            Console.ReadKey();


        }

          ///   <summary> 
         ///   分页的测试
          ///   </summary> 
         ///   <param name="pageIndex">  页码  </param> 
         ///   <param name="pageSize">  页尺寸  </param> 
         static   void  PagerTest( int  pageIndex, int   pageSize)
        {
              //  分页查询条件空(封装中会转恒真条件) 排序条件空(转为ObjectId递增) 设定页码 也尺寸 
             
            Console.WriteLine(  "  页码{0},页尺寸{1}  "  , pageIndex, pageSize);
            Stopwatch sw1  =  new   Stopwatch();
            sw1.Start();
            List <LogInfo> list1 = db.Find<LogInfo>( null , pageIndex, pageSize,  null  );
            sw1.Stop();
            Console.WriteLine(  "  Skip-Limit方式分页耗时:{0}  "  , sw1.ElapsedMilliseconds);
            Stopwatch sw2  =  new   Stopwatch();
            sw2.Start();
              //  这里以Logid索引为标志 如果集合里面没有这些主键标志的话 完全可以使用自己的ObjectId来做 帮助类里面也是封装好的
              //  根据页码计算的LogId也只是简单的模拟 实际中这些LogId不一定会连续 这种方式分页一般不是传页码 而是传最后一个标志的值 
            List<LogInfo> list2 = db.Find<LogInfo>( null ,  "  LogId  " , (pageIndex -  1 ) * pageSize, pageSize,  1  );
            sw2.Stop();
            Console.WriteLine(  "  Where-Limit方式分页耗时:{0}\r\n  "  , sw2.ElapsedMilliseconds);
        }

          ///   <summary> 
         ///   初始化一下数据
          ///   </summary> 
         static   void   InitData()
        {
              //  创建 测试日志类的索引 索引的配置在LogInfo类的特性中 
            db.CreateIndex<LogInfo> ();

              //  初始化日志的集合 
            List<LogInfo> list =  new  List<LogInfo> ();
              int  temp =  0  ;

              //  插入1000W条 测试的数据 
             for  ( int  i =  1 ; i <=  10000000 ; i++ )
            {
                list.Add(  new   LogInfo
                {
                    LogId  =  i,
                    Content  =  "  content  "  +  i.ToString(),
                    CreateTime  =  DateTime.Now
                });

                  //  temp计数  并作大于100的判断 
                 if  (++temp >=  100  )
                {
                      //  大于等于100就清零 
                    temp =  0  ;
                      //  用封装好的方法批量插入数据 
                    db.Insert<LogInfo> (list);
                      //  插入数据之后将当前数据清空掉 
                     list.Clear();
                }
            }
        }
    } 

     来看下最终的效率测试图吧:

      

       非常 ,very,超级明显的可以看出来Skip-Limit的分页效率有多低了吧,每当页码增加十倍时速度就降低十倍,在30W页的时候查询一次竟然要30秒,在大数据量下查询时完全受不了了,然而where-Limit的那种不管你多少页,速度还是那么快,最后一条的0秒是被四舍五入进0的,你看到了多块了吧。

      连续测试这几都是这几种情况,都不想把表格或者图来看了(第一条测试数据可以忽略,估计第一次查询会慢一点,以后会缓存)

      当然了,Where-Limit的方式查询是快,但是实际做起来还是有点麻烦得,不是传页码,而是传上一页的标志,并且并不是所有的集合都有自己的主键的,没有的话你可以用mongodb自带的ObjectId来查,他是默认的索引,速度也是很快的。

      建议如果是小量数据几千几万条的话 用Skip也无妨啦,毕竟是方便,如果数据量大的话千万别用,危险!!!!

      猛击我去看 源码 ,可以直接运行哦,里面还有我自己写的Mongodb 查询帮助类

      参考的文章:

      MongoDB不使用skip做分页

       使用mongodb做分页/排名查询时的性能问题

     mongodb中分页显示及其启发

 

 

分类:  ASP.NET ,  MongoDB

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于基于C#在Mongodb的SkipLimit和WhereLimit的分页的详细内容...

  阅读:50次