zoukankan      html  css  js  c++  java
  • 基于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.NETMongoDB
  • 相关阅读:
    使用CustomValidate自定义验证控件
    C#中金额的大小写转换
    Andriod出错之Unable to build: the file dx.jar was not loaded from the SDK folder!
    VC 编写的打字练习
    机房工作笔记Ping只有单向通
    web服务协同学习笔记(1)
    Dll 学习3 将MDI子窗口封装在DLL中
    机房工作学习文件共享
    Andriod出错之Failed to find an AVD compatible with target 'Android 2.2'
    Andriod出错之wrapper was not properly loaded first
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3062649.html
Copyright © 2011-2022 走看看