基於C#在Mongodb的Skip-Limit和Where-Limit的分頁對比 並且含mongodb協助類的源碼

來源:互聯網
上載者:User

         最近在設計的Log Service中需要用到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中分頁顯示及其啟發

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.