mongodb查询速度慢是什么原因?

通过mongodb客户端samus代码研究解决问题         最近有项目需要用到mongodb,于是在网上下载了mongodb的源码,根据示例写了测试代码,但发现一个非常奇怪的问题:插入记录的速度比获取数据的速度还要快,而且最重要的问题是获取数据的速度无法让人接受。

测试场景:主文档存储人员基本信息,子文档一存储学生上课合同数据集合,这个集合多的可达到几百,子文档二存储合同的付款记录集合,集合大小一般不会超过50。根据人员ID查询人员文档,序列化后的大小为180K不到,但消耗的时间在400ms以上。

我的主要问题在于不能接收获取一个180K的记录需要400ms以上,这比起传统的RDBMS都没有优势,而且mongodb也是内存映射机制,没道理性能如此之差,而且网络上关于它的性能测试数据远远好于我的测试结果。

排除方式一:是不是因为有子文档的原因?

找一个没有任何合同记录的文档查询,发现结果依旧,没有明显的改善;

排除方式二:没有创建索引?

在搜索列ID上创建索引,结果依旧;

排除方式三:是不是文档数量过大?

一万多行只是小数目,没理由,mongodb管理上千万的文档都是没有问题的,于时还是决定试一试,将记录全部删除,插入一条记录然后查询,结果依旧;

排除方式四:是不是由于客户端序列化的问题?

由于我存储的是自定义的对象,不是默认的Document,所以决定尝试直接存储Document,Document就两个字段,获取速度还是需要180ms。

排除方式五:是否由于客户机器是32位,而mongodb服务是64?

将程序放在64位机器上测试,问题依旧。

排除方式六:是否由于网络传输问题?

没道理啊,测试的客户端以及服务端均在同一局域网,但还是尝试将客户端程序直接在mongodb服务器上执行,问题一样;

上面的六种方式都已经尝试过,没有解决,最后决定求助于老代,毕竟是用过mongodb的高人,给我两个建议就搞定了:

排除方式七:查看mongodb数据文件,看是否已经很大?

经查看,总大小才64M,这比32位文件上限的2G来讲,可以基本忽略;

排除方式八:连接字符串。

Servers=IP:27017;ConnectTimeout=30000;ConnectionLifetime=300000;MinimumPoolSize=8;MaximumPoolSize=256;Pooled=true

我一看到这个参考字符串,第一印象是,我的写法和它不一样(string connectionString = "mongodb://localhost"; ),然后发现有两个重要的参数:

1:ConnectionLifetime=300000,从字面意思来看,是说连接的生命周期,而它的数值设置如此大,显然说明此连接不会被立即关闭,这和sql server的做法有所区别;

2ooled=true,从字面意思来看,应该是有连接池的概念。

分析:从上面的连接参数来看,我之前所理解的连接,就是客户端与服务端之间的连接,它需要在使用完之后马上关闭,即客户端与服务端不在有tcp连接。但我没有很好的理解连接池的作用。连接池实际上从存储很多个已经和服务端建立tcp连接的connection,在它的生命周期内一直保持和服务端的连接,生命周期过后会变成失效连接等待回收。

重新修改连接字符串再进行测试,问题解决,只有第一次请求时,由于需要创建tcp连接,性能会受影响,后面的请求,因为有连接池的存在,性能得到成倍提高。

最后看了下samus源码,就可以看出它是如何使用连接池的。

先看下我写的一个mongodb的帮助类:里面有创建Mongo对象等常规操作。

public

class MongodbFactory2<T>: IDisposable where T : class

{

//public  string connectionString = "mongodb://10.1.55.172";

public

string connectionString = ConfigurationManager.AppSettings["mongodb"];

public

string databaseName =

"myDatabase";

Mongo mongo;

MongoDatabase mongoDatabase;

public  MongoCollection<T> mongoCollection;

public  MongodbFactory2()

{

mongo = GetMongo();

mongoDatabase = mongo.GetDatabase(databaseName) as MongoDatabase;

mongoCollection = mongoDatabase.GetCollection<T>() as MongoCollection<T>;

mongo.Connect();

}

public

void Dispose()

{

this.mongo.Disconnect();

}

///

<summary>

/// 配置Mongo,将类T映射到集合

///

</summary>

private Mongo GetMongo()

{

var config =

new MongoConfigurationBuilder();

config.Mapping(mapping =>

{

mapping.DefaultProfile(profile =>

{

profile.SubClassesAre(t => t.IsSubclassOf(typeof(T)));

});

mapping.Map<T>();

});

config.ConnectionString(connectionString);

return

new Mongo(config.BuildConfiguration());

}

从上面的代码中可以看到有这么一句:mongo.Connect(),我第一印象就是创建客户端与服务端的连接,其实有了连接池,这个操作并非每次都创建远程连接,有的情况只是从连接池中直接返回可用连接对象而已。

从源码分析是如何利用连接池,连接是如何创建的。

1:Mongo类的Connect函数:需要跟踪_connection对象。

///

<summary>

///   Connects to server.

///

</summary>

///

<returns></returns>

///

<exception cref = "MongoDB.MongoConnectionException">Thrown when connection fails.</exception>

public

void Connect()

{

_connection.Open();

}

2:再看这句:return new Mongo(config.BuildConfiguration());

///

<summary>

///   Initializes a new instance of the <see cref = "Mongo" /> class.

///

</summary>

///

<param name = "configuration">The mongo configuration.</param>

public Mongo(MongoConfiguration configuration){

if(configuration ==

null)

throw

new ArgumentNullException("configuration");

configuration.ValidateAndSeal();

_configuration = configuration;

_connection = ConnectionFactoryFactory.GetConnection(configuration.ConnectionString);

}

上面代码的最后一句有_connection的生成过程。

3:可以跟踪到最终生成connection的函数,终于看到builder.Pooled这个参数了,这的值就是连接串中的参数。

///

<summary>

/// Creates the factory.

///

</summary>

///

<param name="connectionString">The connection string.</param>

///

<returns></returns>

private

static IConnectionFactory CreateFactory(string connectionString){

var builder =

new MongoConnectionStringBuilder(connectionString);

if(builder.Pooled)

return

new PooledConnectionFactory(connectionString);

return

new SimpleConnectionFactory(connectionString);

}

4:再看PooledConnectionFactory是如何创建连接的:这的作用就是将可用连接放入连接池中,而最终真正创建连接的函数是CreateRawConnection()

///

<summary>

/// Ensures the size of the minimal pool.

///

</summary>

private

void EnsureMinimalPoolSize()

{

lock(_syncObject)

while(PoolSize < Builder.MinimumPoolSize)

_freeConnections.Enqueue(CreateRawConnection());

}

5:真正远程连接部分。

///

<summary>

/// Creates the raw connection.

///

</summary>

///

<returns></returns>

protected RawConnection CreateRawConnection()

{

var endPoint = GetNextEndPoint();

try

{

return

new RawConnection(endPoint, Builder.ConnectionTimeout);

}catch(SocketException exception){

throw

new MongoConnectionException("Failed to connect to server "

+ endPoint, ConnectionString, endPoint, exception);

}

}

private

readonly TcpClient _client =

new TcpClient();

private

readonly List<string> _authenticatedDatabases =

new List<string>();

private

bool _isDisposed;

///

<summary>

/// Initializes a new instance of the <see cref="RawConnection"/> class.

///

</summary>

///

<param name="endPoint">The end point.</param>

///

<param name="connectionTimeout">The connection timeout.</param>

public RawConnection(MongoServerEndPoint endPoint,TimeSpan connectionTimeout)

{

if(endPoint ==

null)

throw

new ArgumentNullException("endPoint");

EndPoint = endPoint;

CreationTime = DateTime.UtcNow;

_client.NoDelay =

true;

_client.ReceiveTimeout = (int)connectionTimeout.TotalMilliseconds;

_client.SendTimeout = (int)connectionTimeout.TotalMilliseconds;

//Todo: custom exception?

_client.Connect(EndPoint.Host, EndPoint.Port);

}

接着我们来看下,连接的生命周期是如何实现的:主要逻辑在PooledConnectionFactory,如果发现连接已经过期,则将连接放入不可用队列,将此连接从空闲连接中删除掉。

///

<summary>

/// Checks the free connections alive.

///

</summary>

private

void CheckFreeConnectionsAlive()

{

lock(_syncObject)

{

var freeConnections = _freeConnections.ToArray();

_freeConnections.Clear();

foreach(var freeConnection in freeConnections)

if(IsAlive(freeConnection))

_freeConnections.Enqueue(freeConnection);

else

_invalidConnections.Add(freeConnection);

}

}

///

<summary>

/// Determines whether the specified connection is alive.

///

</summary>

///

<param name="connection">The connection.</param>

///

<returns>

///

<c>true</c> if the specified connection is alive; otherwise, <c>false</c>.

///

</returns>

private

bool IsAlive(RawConnection connection)

{

if(connection ==

null)

throw

new ArgumentNullException("connection");

if(!connection.IsConnected)

return

false;

if(connection.IsInvalid)

return

false;

if(Builder.ConnectionLifetime != TimeSpan.Zero)

if(connection.CreationTime.Add(Builder.ConnectionLifetime) < DateTime.Now)

return

false;

return

true;

}

最后我们来看我最上面的mongodb帮忙类的如下方法:即释放连接,而这里的释放也不是直接意义上将连接从客户端与服务端之间解除,只不过是将此连接从忙队列中删除,重新回归到可用队列:

public

void Dispose()

{

this.mongo.Disconnect();

}

再看看mongo.Disconnect()

///

<summary>

///   Disconnects this instance.

///

</summary>

///

<returns></returns>

public

bool Disconnect()

{

_connection.Close();

return _connection.IsConnected;

}

继续往下就会定位到如下核心内容:

///

<summary>

///   Returns the connection.

///

</summary>

///

<param name = "connection">The connection.</param>

public

override

void Close(RawConnection connection)

{

if(connection ==

null)

throw

new ArgumentNullException("connection");

if(!IsAlive(connection))

{

lock(_syncObject)

{

_usedConnections.Remove(connection);

_invalidConnections.Add(connection);

}

return;

}

lock(_syncObject)

{

_usedConnections.Remove(connection);

_freeConnections.Enqueue(connection);

Monitor.Pulse(_syncObject);

}

}

总结:经过各位不同的尝试,终于解决了mongodb查询慢的原因,并非mongodb本身问题,也非网络,非数据问题指点。,而是在于没有正确使用好客户端连接,不容易啊。

mongodb查询速度慢是什么原因?的更多相关文章

  1. SQLServer查询速度慢的原因

    查询速度慢的原因很多,常见如下几种:  1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应.  3.没有创建计算列导致查询不优化.  4.内存 ...

  2. Oracle查询速度慢的原因总结

    Oracle查询速度慢的原因总结 查询速度慢的原因很多,常见如下几种:1,没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)2,I/O吞吐量小,形成了瓶颈效应.3,没有创建计算列导致 ...

  3. SQL查询速度慢的原因分析和解决方案

    SQL查询速度慢的原因分析和解决方案 查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建 ...

  4. ORACLE 查询不走索引的原因分析,解决办法通过强制索引或动态执行SQL语句提高查询速度

    (一)索引失效的原因分析: <>或者单独的>,<,(有时会用到,有时不会) 有时间范围查询:oracle 时间条件值范围越大就不走索引 like "%_" ...

  5. HBase查询速度慢原因排查

    问题:通过HBase访问服务在HBase中查询 ASSET_NORMAL 表速度很慢 如下,查询一条数据需要2.970s时间: 如下,统计总条数需要14.675s时间: HBase访问服务部署了3个节 ...

  6. SQL Server 百万级数据提高查询速度的方法

    1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉 ...

  7. mongodb查询文档

    说到查询,我们一般就想起了关系型数据库的查询了,比如:order by(排序).limit(分页).范围查询(大于某个值,小于某个值..,in查询,on查询,like查询等待很多),同样mongodb ...

  8. 提高查询速度:SQL Server数据库优化方案

    查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...

  9. 优化SQLServer数据库加快查询速度

    查询速度慢的原因很多,常见如下几种: 1.没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 2.I/O吞吐量小,形成了瓶颈效应. 3.没有创建计算列导致查询不优化. 4.内存不足 ...

随机推荐

  1. 替换国内yum源以及pip源

    因为一些原因,不论是网络还是啥啥啥的原因,国外的源访问时不时的很慢,这时候我们就可以将国外的源替换为国内源,提高下载速度. yum源替换 环境:centos7(如果你的发行版本不是这个,此方法不保证能 ...

  2. Dijkstra算法(C语言)

    Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Di ...

  3. hibernate框架的核心对象和相关知识点

    Hibernate架构下图提供了hibernate体系的高层视图: Hibernate全面解决方案: Hibernate核心APIConfiguration负责管理数据库的配置信息.数据库的配置信息包 ...

  4. fusionjs 学习一 基本试用

    参考demo 项目 https://github.com/rongfengliang/fusionjs-docker-demo 安装 create startkit yarn global add c ...

  5. hasura graphql 引擎基本试用

    hasura 使用一个基于pg数据库的graphql引擎,他的设计比postgrpahql 有好多方便的地方,同时使用也比较简单 安装 docker && docker-compose ...

  6. 全面剖析Smarty缓存机制一[三种缓存方式]

    今天主要全面总结下Smarty模板引擎中强大的缓存机制,缓存机制有效减少了系统对服务器的压力,而这也是很多开发者喜欢Smarty的原因之一,由于篇幅较大,便于博友阅读,这篇文章将剖析Smarty缓存的 ...

  7. linux环境下maven的安装配置

    1.到官网下载maven,上传到服务器上 https://maven.apache.org/download.cgi 2.将压缩包上传服务器对应路径解压: tar -zxvf apache-maven ...

  8. zedgraph控件怎么取得鼠标位置的坐标值(转帖)

    我想取得zedgraph控件上任意鼠标位置的坐标值,IsShowCursorValues可以显示鼠标位置的值但是不能提取赋值给其他的变量.用PointValueEvent这个事件又只能得到已经画出的点 ...

  9. JZ2440 裸机驱动 第5章 GPIO接口

    本章目标:     掌握嵌入式开发的步骤:编程.编译.烧写程序.运行     通过GPIO的操作了解软件如何控制硬件 5.1 GPIO硬件介绍     S3C2440A有130个多功能输入/输出口引脚 ...

  10. 利用spring的CommonsMultipartResolver上传文件

    1.CommonsMultipartResolver是spring里面提供的一个上传方式,效率我不知道,但是加入spring容器管理还是很不错的. 2.先看依赖包pom.xml <project ...