mongodb 连接池如何管理连接的?有必要实现连接池吗

博客分类:
异常日志:
Out of semaphores to get db connection 查看源代码发现是连接池资源用尽:
查代码看原因:
DBPortPool 写道
if ( ! _waitingSem.tryAcquire() )
throw new SemaphoresOut();
_waitingSem初始化代码
DBPortPool 写道
_waitingSem = new Semaphore( _options.connectionsPerHost * _options.threadsAllowedToBlockForConnectionMultiplier );
MongoOptions 写道
public MongoOptions(){
public void reset(){
connectionsPerHost = Bytes.CONNECTIONS_PER_HOST;
threadsAllowedToBlockForConnectionMultiplier = 5;
maxWaitTime = 1000 * 60 * 2;
connectTimeout = 0;
socketTimeout = 0;
socketKeepAlive =
autoConnectRetry =
maxAutoConnectRetryTime = 0;
wtimeout = 0;
dbDecoderFactory = DefaultDBDecoder.FACTORY;
socketFactory = SocketFactory.getDefault();
static final int CONNECTIONS_PER_HOST = Integer.parseInt( System.getProperty( "MONGO.POOLSIZE" , "10" ) );
改变连接池大小:
1、可以通过系统属性改变连接池大小。
2、代码层面修改,new Mongo的时,先一个你需要的MongoOptions
浏览: 72180 次
来自: 杭州
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'博客分类:
该文档是翻译自文档[mongodb-docs-.pdf]的[Java Language Center]章节,根据自己的理解整理而成。
希望能给像我这样开始接触的朋友一点帮助,同时也做个备忘,因为是刚刚学习,其中的很多功能目前都用不上,以后万一有什么功能不太清楚,也可以直接查阅该文档了。
MongoDB Java Driver 简单操作 一、Java驱动一致性
MongoDB的Java驱动是线程安全的,对于一般的应用,只要一个Mongo实例即可,Mongo有个内置的连接池(池大小默认为10个)。
对于有大量写和读的环境中,为了确保在一个Session中使用同一个DB时,我们可以用以下方式保证一致性:
DB mdb = mongo.getDB('dbname');
mdb.requestStart(); // // 业务代码 // mdb.requestDone();
DB和DBCollection是绝对线程安全的,它们被缓存起来了,所以在应用中取到的可能是同一个对象。
二、保存/查找对象(DBObject)
Java驱动提供了DBObject接口,方便我们保存对象到数据库中。
定义需要保存的对象:
public class Tweet implements DBObject {
/** ...... */ }
然后我们可以使用该对象:
Tweet tweet = new Tweet(); tweet.put("user", userId); tweet.put("message", message); tweet.put("date", new Date());
collection.insert(tweet);
当从数据库中查询时,结果会自动的转换成DBObject对象,我们可以转换成我们自己的类型:
collection.setObjectClass(Tweet);
Tweet myTweet = (Tweet)collection.findOne();
三、创建连接
Mongo m = new Mongo(); Mongo m = new Mongo("localhost"); Mongo m = new Mongo("localhost", 27017);
DB db = m.getDB("mydb);
注意:事实上,Mongo实例代表了一个数据库连接池,即使在多线程的环境中,一个Mongo实例对我们来说已经足够了。 四、认证(可选的)
boolean auth = db.authenticate("myUserName", "myPasswd"); 五、取得Collection列表
Set&String& colls = db.getCollectionNames();
for(String s : colls) {
System.out.prinln(s); }
六、获取一个Collection
DBCollection coll = db.getCollection("testCollection");
使用DBCollection,我们可以进行插入、查询数据等数据操作。
七、插入文档
假设有个JSON文档如下所示:
"name": "MongoDB",
"type": "database",
"count": 1,
注意:上面的JSON文档有个内嵌文档"info"。
我们完全可以利用BasicDBObject来创建一个和上面的JSON一样的文档,并且把它保存在MongoDB中。
DBObject doc = new BasicDBObject();
doc.put("name", "MongoDB"); doc.put("type", "database"); doc.put("count", 1);
DBObject info = new BasicDBObject(); info.put("x", 203); info.put("y", 102);
doc.put("info", info);
coll.insert(doc); 八、查询第一个文档(findOne())
为了验证在上面我们保存的类似JSON的数据,我们可以用findOne()方法取得数据。
findOne(): 返回一个文档; find(): 返回一个游标(DBCursor),其中包含一组对象DBObject;
DBObject doc = coll.findOne(); System.out.println(doc);
我们将会看到控制台输出: { "_id" : "49902cdeb45c2c" , "name" : "MongoDB" , "type" : "database" , "count" : 1 , "info" : { "x" : 203 , "y" : 102} , "_ns" : "testCollection"} 九、插入多个文档
为了在后来展示更多的查询方法,我们先插入几个文档,它们的JSON像这样: {
"i": value }
使用一个循环插入数据:
for(int i = 0; i & 100; i++) {
coll.insert(new BasicDBObject().append("i", i)); }
我们注意到,同一个coll,我们完全可以插入不同风格的数据,这就是MongoDB的重要特性“模式自由”。 十、统计文档数
现在我们已经有101份文档在数据库中了,现在统计一下看是否正确。
long count = coll.getCount(); System.out.println(count);
控制台将会输出:101 十一、使用游标取得所有的文档
DBCursor cursor = coll.find();
while(cursor.hasNext()) {
DBObject object = cursor.next();
System.out.println(object); } 十二、查询单个文档
DBObject query = new BasicDBObject();
query.put("i", 71);
cursor = coll.find(query);
while(cur.hasNext()) {
DBObject object = cursor.next();
System.out.println(object); }
控制台的输出类似如下:
{ "_id" : "50ce" , "i" : 71 , "_ns" : "testCollection"} 十三、查询文档集合
根据查询条件,我们可以通过DBCollection从数据库中取出多个对象,比如查询i&50的文档集合:
query = new BasicDBObject();
query.put("i", new BasicDBObject("$gt", 50)); // i&50
cursor = coll.find(query);
while(cursor.hasNext()) {
DBObject object = cursor.next();
System.out.println(object); }
比如查询条件为 20&i&=30:
query = new BasicDBObject();
// 20&i&=30 query.put("i", new BasicDBObject("$gt", 20).append("$lte", 30));
cursor = coll.find(query);
while(cursor.hasNext()) {
DBObject object = cursor.next();
System.out.println(object); }
十四、创建索引
MongoDB支持索引,并且给一个DBCollection添加索引非常简单,你只要指明需要创建索引的字段,然后指明其是升序(1)还是降序(-1)即可,比如在"i"上创建升序索引。
coll.createIndex(new BasicDBObject("i", 1)); // 1代表升序 十五、查询索引
我们可以查询到所有的索引:
List&DBObject& list = coll.getIndexInfo();
for(DBObject index : list){
System.out.println(index); }
控制台的输出类似如下所示:
{ "name" : "i_1" , "ns" : "mydb.testCollection" , "key" : { "i" : 1} , "_ns" : "system.indexes"}
MongoDB的管理功能
一、获取所有的数据库
Mongo m = new Mongo();
for(String s : m.getDatabaseNames()) {
System.out.println(s); }
二、删除数据库
m.dropDatabase("my_new_db");
MongoDB的Java类型
一、对象ID
ObjectId被用作自动生成的唯一ID.
ObjectId id = new ObjectId(); ObjectId copy = new ObjectId(id); 二、正则表达式
Pattern john = Pattern.compile("joh?n", CASE_INSENSITIVE); DBObject query = new BasicDBObject("name", john);
// 查询所有 "name" 匹配 /joh?n/i 的文档 DBCursor cursor = collection.find(query);
三、日期和时间
Date now = new Date(); DBObject time = new BasicDBObject("ts", now);
collection.save(time); 四、数据库引用
DBRef可以用来保存数据库引用。
DBRef addressRef = new DBRef(db, "foo.bar", address_id); DBObject address = addressRef.fetch();
DBObject person = BasicDBObjectBuilder.start()
.add("name", "Fred")
.add("address", addressRef)
.get(); collection.save(person);
DBObject fred = collection.findOne(); DBRef addressObj = (DBRef)fred.get("address"); addressObj.fetch(); 五、二进制数据
字节数组(byte[])被当作二进制数据。 六、内嵌文档
JSON样式的数据如下: {
则在MongoDB中,Java表示为:
DBObject y = new BasicDBObject("y", 3); DBObject x = new BasicDBObject("x", y); 七、数组
任何继承自List的对象,在MongoDB中,都被当成是数组。
如果想表示如下JSON数据:
{"foo": "bar"},
则在Java中,应该为:
List&Object& x = new ArrayList&Object&(); x.add(1); x.add(2); x.add(new BasicDBObject("foo", "bar")); x.add(4);
DBObject doc = new BasicDBObject("x", x); System.out.println(doc);
===========================================如有批评、指教、疑惑,请:祝大家使用JAVA愉快!
论坛回复 /
(1 / 8319)
浏览: 150315 次
来自: 上海
楼主,你空间的座右铭是“不抛弃,不放弃”,这篇文章是“抛弃My ...
一个为什么一个mongo实例就是一个数据库连接池?可以配置的大 ...
[b][/b][i][/i][u][/u]引用[*][img] ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'博客分类:
在关系型数据库中,我们总是需要关闭使用的数据库连接,不然大量的创建连接会导致资源的浪费甚至于数据库宕机。这篇文章主要想解释一下mongoDB的连接池以及连接管理机制,如果正对此有疑惑的朋友可以看一下。
通常我们习惯于new 一个connection并且通常在finally语句中调用connection的close()方法将其关闭。正巧,mongoDB中当我们new一个Mongo的时候,会发现它也有一个close()方法。所以会出现这样的情况:我们在需要DB操作的方法中new一个mongo实例,然后调用mongo.getDB()方法拿到对应的连接,操作完数据之后再调用mongo.close()方法来关闭连接。 看起来貌似是没有什么问题,但是如果你再研究一下mongo的API,你会发现这样耳朵操作就相当于园丁在浇花的时候去打了一桶水,然后舀了一勺水浇一朵花,然后他把一桶水全倒了回去,重新打一桶水,再舀了一勺水浇另外一朵花。。。
说到这里大家应该都已经明白了,其实当你new Mongo()的时候,就创建了一个连接池,getDB()只是从这个连接池中拿一个可用的连接。而连接池是不需要我们及时关闭的,我们可以在程序的生命周期中维护一个这样的单例,至于从连接池中拿出的连接,我们需要关闭吗?答案是NO。你会发现DB根本没有close()之类的方法。在mongoDB中,一个连接池会维持一定数目的连接,当你需要的时候调用getDB()去连接池中拿到连接,而mongo会在这个DB执行完数据操作时候自动收回连接到连接池中待用。所以在mongoDB中大家不必担心连接没有关闭的问题,在你需要在所有操作结束或者整个程序shutdown的时候调用mongo的close()方法即可。
以下的官网的一些解释:
public Class Mongo:
A database connection with internal connection pooling. For most applications, you should have one Mongo instance for the entire JVM.
public Class MongoClient:
A MongoDB client with internal connection pooling. For most applications, you should have one MongoClient instance for the entire JVM.
Note: This class supersedes the Mongo class. While it extends Mongo, it differs from it in that the default write concern is to wait for acknowledgment from the server of all write operations. In addition, its constructors accept instances of MongoClientOptions and MongoClientURI, which both also set the same default write concern.
In general, users of this class will pick up all of the default options specified in MongoClientOptions. In particular, note that the default value of the connectionsPerHost option has been increased to 100 from the old default value of 10 used by the superceded Mongo class.
Mongo 是一个过期的类,取而代之的是MongoClient, 值得一提的是MongoClient把connection的默认值从以前的10个变成了现在的100个,省去了自定义配置的繁琐,很贴心。
下面是我写的一个MongoConnectionFactory:
public class MongoConnFactory {
private static MongoClient mongoClient =
@SuppressWarnings("deprecation")
public static
DB getDB() throws UnknownHostException {
if(mongoClient == null){
intializeMongoClient();
String dbName = AppConfig.getValue(Const.MONGODB_DBNAME);
String username = AppConfig.getValue(Const.MONGODB_USERNAME);
String password = AppConfig.getValue(Const.MONGODB_PASSWORD);
conn = mongoClient.getDB(dbName);
conn.authenticate(username, password.toCharArray());
private static void intializeMongoClient() throws UnknownHostException {
String host = AppConfig.getValue(Const.MONGODB_HOST);
int port = AppConfig.getValueAsInteger(Const.MONGODB_PORT);
mongoClient = new MongoClient( host , port );
static synchronized void closeConnection(){
if(mongoClient != null){
mongoClient.close();
原创文章,转载请注明出处:
BigCat2013
浏览: 24455 次
来自: 上海
string2020 写道上面写的使用场景太抽象了,求其他的使 ...
上面写的使用场景太抽象了,求其他的使用场景
string2020 写道集群搭建好之后,有什么用搭建一些分布 ...
集群搭建好之后,有什么用
devilyard 写道原来挺简单的万事开头难
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'博客分类:
MongoDB的连接对象是DBPort,所以可以从DBPortPool对象看起,构造函数如下:
DBPortPool( ServerAddress addr , MongoOptions options ){
super( "DBPortPool-" + addr.toString() + ", options = " +
options.toString() ,options.connectionsPerHost );
_options =
_waitingSem = new Semaphore( _options.connectionsPerHost * _options.threadsAllowedToBlockForConnectionMultiplier );
可以看出java-driver会为每个ServerAddress创建一个连接池。也是因为MongoDB是在驱动中设置高可用的,可以配置多个MongoS或MongoD的连接。_waitingSem是个很有意思的信号量,他是由connectionsPerHost和threadsAllowedToBlockForConnectionMultiplier相乘得出的,在后面来分析他的用法。
DBPortPool的父类是SimplePool,再来看从父类的构造函数可以看出:
publicSimplePool(String name, int size){
_sem = new Semaphore(size);
其中,size就是connectionsPerHost,SimplePool使用connectionsPerHost创建了一个信号量_sem,可以通过_sem在SimplePool中的使用方式来分析一下连接的创建策略。
SimplePool中,通过下面方法申请获取连接的许可证,
privatebooleanpermitAcquired(finallong waitTime) throws InterruptedException {
if(waitTime & 0) {
return_sem.tryAcquire(waitTime, TimeUnit.MILLISECONDS);
} elseif (waitTime & 0) {
_sem.acquire();
returntrue;
return_sem.tryAcquire();
获取这个许可有一个等待时间的策略,如果waitTime为0,那么如果可以获取连接就立即返回true,如果不能获取连接就立即返回false;如果设置了waitTime为正数,会等待waitTime毫秒,如果这段时间仍然没有连接可以使用就返回false;如果waitTime就负数,那么会一直等待直到获得许可为止。
这在被调用方法不能设置超时时间的时候,在调用方法中设置超时时间的方法。
这也是一个平衡线程利用率的策略,在一个有限且使用率高的线程池中,让一个线程等待时间过长无疑是一种浪费,MongoDB使用阻塞信号量配置等待时间的方法,配置线程的释放策略。
这个方法是在获取DBPort对象时被调用的,用来申请获取连接的许可:
public T get(long waitTime) throws InterruptedException {
if(!permitAcquired(waitTime)) {
returnnull;
//获取连接的方法略……
// t =createNewAndReleasePermitIfFailure();
SimplePool是一个获取池对象的基类方法,createNewAndReleasePermitIfFailure方法会调用子类的createNew方法来创建具体对象。然后再看看这个信号量什么时候被释放:
private TcreateNewAndReleasePermitIfFailure() {
T newMember = createNew();
if(newMember == null) {
thrownew IllegalStateException("null pool members are not allowed");
returnnewM
} catch(RuntimeException e) {
_sem.release();
} catch (Errore) {
_sem.release();
可见,在连接创建失败的时候会释放这个信号量。
publicvoid done( T t ){
synchronized( this ) {
if (_closed) {
cleanup(t);
// 释放连接方法略……
_sem.release();
还有就是连接使用完毕,要调用done释放连接,信号量就会被恢复。
从上可知。connectionPerHost是单个Mongo服务连接地址的连接池数量,连接都被占用的获取连接等待时间是可设置的,由MongoClientOptions.maxWaitTime参数决定,默认时间是两分钟,可以通过减少这个值来提升线程利用率。
再来看看_waitingSem这个信号量的用法,他只在一个方法中被使用了:
public DBPort get() {
DBPort port = null;
if ( ! _waitingSem.tryAcquire() )
thrownew SemaphoresOut(_options.connectionsPerHost * _options.threadsAllowedToBlockForConnectionMultiplier);
port = get( _options.maxWaitTime );
} catch(InterruptedException e) {
thrownew MongoInterruptedException(e);
} finally {
_waitingSem.release();
if ( port== null )
thrownew ConnectionWaitTimeOut( _options.maxWaitTime );
port._lastThread =System.identityHashCode(Thread.currentThread());
_waitingSem值的大小是连接池大小乘以一个常数(&=1),这个信号量在获取连接动作之前取得,在获取连接后马上释放,而不用等待连接被放回线程池,说明在线程池为空的情况下,最多可以有(connectionsPerHost* threadsAllowedToBlockForConnectionMultiplier)多个线程被阻塞在
port = get(_options.maxWaitTime)这段代码处。超过这个数就会抛异常,告诉后面的线程不用等了。所以这是一个等待获取连接的队列,队列的长度就是连接池大小乘以一个常数。如果线程池数量有限,而又允许有一定数据损失的场景下,可以将threadsAllowedToBlockForConnectionMultiplier值调小,比如写日志。像MongoDB这种周期性从内存向磁盘写入数据的情况,默认是每60秒写如一次数据,如果内存不够大,可能会在客户端积压很长的等待队列,如果操作比较频繁,而又不想处理因为等待造成的异常,就将这个值调大。
浏览: 6390 次
来自: 上海
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'mongo驱动 mgo 连接池问题 - Golang中国
19:41 发布 7410 次点击
我想知道mongo 和驱动 mgo是怎么处理连接池的问题,我用Dial打开很多连接,mongo控制台就显示打开了多少连接,这是没用到连接池?
mgo自带连接池的,默认3个idle,封装一下,如下:
package db
"labix.org/v2/mgo"
"labix.org/v2/mgo/bson"
USER string = "user"
string = "msg"
*mgo.Session
databaseName = "test"
func Session() *mgo.Session {
if session == nil {
var err error
session, err = mgo.Dial("localhost")
if err != nil {
panic(err) // no, not really
return session.Clone()
func M(collection string, f func(*mgo.Collection)) {
session := Session()
defer func() {
session.Close()
if err := recover(); err != nil {
Log("M", err)
c := session.DB(databaseName).C(collection)
使用示例:
var m = M{}
db.M(db.USER, func(c *mgo.Collection) {
c.Find(M{"_id": id}).One(m)
Log("find one m", m, m["name"], m["img"])
先dial一个session出来
然后使用 .clone来分配 session
连接池最大默认是 4096 个?
后方可回复, 如果你还没有账号你可以
一个帐号。

我要回帖

更多关于 nodejs mongodb连接池 的文章

 

随机推荐