基于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语句了,简单的来贴一下代码

/// <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条简单的数据,大数据量下测试才有有效果嘛,

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

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 查询帮助类
参考的文章:
基于C#在Mongodb的Skip-Limit和Where-Limit的分页对比 并且含mongodb帮助类的源码的更多相关文章
- 基于DevExpress的SpreadsheetControl实现对Excel的打开、预览、保存、另存为、打印(附源码下载)
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- ABP源码分析二十九:ABP.MongoDb
这个Module通过建立一个MongoDbRepositoryBase<TEntity> 基类,封装了对MongoDb数据库的操作. 这个module通过引用MongoDB.Driver, ...
- Java集合类源码解析:HashMap (基于JDK1.8)
目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...
- 死磕Java之聊聊HashSet源码(基于JDK1.8)
HashSet的UML图 HashSet的成员变量及其含义 public class HashSet<E> extends AbstractSet<E> implements ...
- 转MongoDB 使用Skip和limit分页
关于MongoDB 数据分页和排序 limit,skip用户的一些基础语句,介绍MongoDB 数据分页和排序实例方法. 使用Skip和limit可以如下做数据分页: Code: page1 = db ...
- MongoDB 学习笔记(9)--- Limit与Skip方法
MongoDB Limit() 方法 如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的 ...
- MongoDB快速入门(十)- Limit(),Skip() 方法
Limit() 方法 要限制 MongoDB 中的记录,需要使用 limit() 方法. limit() 方法接受一个数字型的参数,这是要显示的文档数. 语法: limit() 方法的基本语法如下 & ...
- Spring data mongodb 聚合,投射,内嵌数组文档分页.
尽量别直接用 DBObject ,Spring data mongodb 的api 本来就没什么多大用处,如果还直接用 DBObject 那么还需要自己去解析结果,说动做个对象映射,累不累 Spri ...
- [转]Asp.Net大型项目实践(11)-基于MVC Action粒度的权限管理【续】【源码在这里】(在线demo,全部源码)
本文转自:http://www.cnblogs.com/legendxian/archive/2010/01/25/1655551.html 接上篇Asp.Net大型项目实践(10)-基于MVC Ac ...
随机推荐
- CodeForces - 632E Thief in a Shop (FFT+记忆化搜索)
题意:有N种物品,每种物品有价值\(a_i\),每种物品可选任意多个,求拿k件物品,可能损失的价值分别为多少. 分析:相当于求\((a_1+a_2+...+a_n)^k\)中,有哪些项的系数不为0.做 ...
- 51nod 1391 01串(hash+DP)
题目链接题意:给定一个01串S,求出它的一个尽可能长的子串S[i..j],满足存在一个位置i<=x <=j, S[i..x]中0比1多,而S[x + 1..j]中1比0多.求满足条件的最长 ...
- 不允许有匹配 "[xX][mM][lL]" 的处理指令目标
八月 , :: 上午 org.apache.tomcat.util.digester.SetPropertiesRule begin 警告: [SetPropertiesRule]{Server/Se ...
- 【c++ primer, 5e】返回类型和return语句
[无返回值函数] 1.在c++的void函数中,可以显式地使用return;语句来提前结束函数的调用. [有返回值函数] 1.值是如何被返回的:返回一个值的方式和初始化一个变量或者形参的方式完全一样. ...
- Windos 系统网络连接 调优
1.运行进入注册表:regedit 2.进入注册表指定路径 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters ...
- kali 安装virtualbox
安装virualbox 的三大步: 1.添加源: Add the following line to your /etc/apt/sources.list: deb http://download.v ...
- 20145314郑凯杰《信息安全系统设计基础》第八周复习总结 Part A
20145314郑凯杰<信息安全系统设计基础>第八周复习总结 Part A 学习知识点内容总结 复习线索:http://group.cnblogs.com/topic/73069.html ...
- PostgreSQL 递归查询 (转)
数据库中的数据存在父子关系(单继承,每一条记录只有一个父亲). 如果要查询一条记录以及他的所有子记录,或者要查询一条记录以及他的所有父记录.那么递归查询就再合适不过了.可以简化复杂的SQL语句 现在 ...
- 再也不学AJAX了!(二)使用AJAX
在上一篇文章中我们知道,AJAX是一系列技术的统称.在本篇中我们将更进一步,详细解释如何使用Ajax技术在项目中获取数据.而为了解释清楚,我们首先要搞清楚我们是从哪里获取数据的,其次我们关注的才是获取 ...
- 酷到没朋友—— Cafflano便携式手磨手冲一体壶
又一款外国新玩具~ 设计紧凑,手磨.滤架.滤壶融合的毫无ps痕迹! 简直是出差旅行,杀人越货必备良品!废话不多说,上图: 肿么样,一壶在手,天下我有~~~哈哈哈~~~