基於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中分頁顯示及其啟發


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM