mongodb驱动的正确使用方法
来源:互联网 发布:基本款 男朋友 知乎 编辑:程序博客网 时间:2024/04/29 23:08
mongo数据库在nodejs平台有2个常用驱动,mongodb和mongoose,mongodb接口非常接近mongo数据库原生的操作方式,是helloworld之类演示代码的首选mongo数据库连接驱动,因此成为大部分nodejs初学者最先接触的mongo数据库驱动。初学者在学会mongo连接的同时,却也可悲的被helloword这种演示性质的数据库操作习惯潜移默化了。
cat test.js
var server_options={};var db_options={ w:-1,// 设置w=-1是mongodb 1.2后的强制要求,见官方api文档 logger:{ doDebug:true, debug:function(msg,obj){ console.log('[debug]',msg); }, log:function(msg,obj){ console.log('[log]',msg); }, error:function(msg,obj){ console.log('[error]',msg); } }};var mongodb = require("mongodb"), mongoserver = new mongodb.Server('localhost', 27017,server_options ), db = new mongodb.Db('test', mongoserver, db_options);function test_save(){ //前一个db和后一个db,是同一个对象。 db.open(function(err,db){ if(err)return console.error(err); console.log('* mongodb connected'); db.collection('foo').save({test:1},function(err,result){ console.log(result); db.close(); }); })}test_save();
这是个随处可见,大家非常熟悉的mongo数据库helloword代码:设置连接参数,open, 访问collection, close。 唯一不同的是为了显示代码背后的实际运作,我给db_options加上一个日志(logger)选项。node test.js
, 客户端输出信息:
[debug] opened connection[debug] opened connection[debug] opened connection[debug] opened connection[debug] opened connection[debug] writing command to mongodb* mongodb connected[debug] writing command to mongodb{ test: 1, _id: 51908a93718f6ad00c000001 }[debug] closed connection[debug] closed connection[debug] closed connection[debug] closed connection[debug] closed connection
服务端mongo数据库的输出日志:
Mon May 13 12:54:33 [initandlisten] connection accepted from 127.0.0.1:2815 #51Mon May 13 12:54:33 [initandlisten] connection accepted from 127.0.0.1:2816 #52Mon May 13 12:54:33 [initandlisten] connection accepted from 127.0.0.1:2817 #53Mon May 13 12:54:33 [initandlisten] connection accepted from 127.0.0.1:2818 #54Mon May 13 12:54:33 [initandlisten] connection accepted from 127.0.0.1:2819 #55Mon May 13 12:54:33 [conn51] end connection 127.0.0.1:2815Mon May 13 12:54:33 [conn52] end connection 127.0.0.1:2816Mon May 13 12:54:33 [conn53] end connection 127.0.0.1:2817Mon May 13 12:54:33 [conn54] end connection 127.0.0.1:2818Mon May 13 12:54:33 [conn55] end connection 127.0.0.1:2819
客户端和服务器端的日志都表明,db.open了5个连接,而并非一般同学所想象的1个连接。why?这是因为mongoserver = new mongodb.Server('localhost', 27017,server_options )
的serveroptions有个poolSize选项,默认值是5(见官方api文档)。db对象不仅扮演着与mongo数据库通讯的中间人角色,还同时是一个连接池。默认设置的情况下,helloword代码打开一个有着5个连接的连接池,然后再关闭这个连接池。作为ran and quit的演示,这个操作流程当然没问题,但如果放到http server的应用场景就成大问题了。每次http请求都打开5个数据库连接而后又关闭5个数据库连接,对性能影响可想而知,更为糟糕是open and close的操作流程会导致一个潜在的并发错误。
_cat server_1.js
var server_options={};var db_options={w:-1};var mongodb = require("mongodb"), mongoserver = new mongodb.Server('localhost', 27017,server_options ), db = new mongodb.Db('test', mongoserver, db_options);var http=require('http');var server=http.createServer(function(req,res){ db.open(function(err,db){ if(err)return console.error(err); console.log('* mongodb connected'); db.collection('foo').save({test:1},function(err,result){ res.end(JSON.stringify(result,null,2)); db.close(); }); })});server.listen(8080,function(){ console.log('server listen to %d',this.address().port);});setTimeout(function(){ //http.get('http://localhost:8080',function(res){console.log('request ok')}); //http.get('http://localhost:8080',function(res){console.log('request ok')});},2000);
node server.js
, 浏览器访问ok,但如果做ab类并发测试或者把倒数2、3行的注释去掉,问题就来了。
c:\nodejs\node_modules\mongodb\lib\mongodb\db.js:224 throw new Error("db object already connecting, open cannot be called multi ^Error: db object already connecting, open cannot be called multiple times
想象db对象是一扇门,open操作相当于开门,开门后才能阅读房间里面的书籍(数据)。当请求1开门后,紧随而来的请求2也想开门但是开不了,因为请求1还没关门(db.close),门还处于“打开”的状态。其实呢,请求2完全没必要再开门,直接尾随请求1进门即可。错误的根源在于我们要打开一扇已经打开的门。how to fix? easy, 从open and close 行为改为 open once and reuse anywhere。程序启动的时候db.open一次,每次http请求直接访问数据库,扔掉db.open/db.close这2个多余的操作。
cat server_2.js
var server_options={'auto_reconnect':true,poolSize:5};var db_options={w:-1};var mongodb = require("mongodb"), mongoserver = new mongodb.Server('localhost', 27017,server_options ), db = new mongodb.Db('test', mongoserver, db_options);db.open(function(err,db){ if(err)throw err; console.info('mongodb connected');});var http=require('http');var server=http.createServer(function(req,res){ db.collection('foo').save({test:1},function(err,result){ res.end(JSON.stringify(result,null,2)); });});server.listen(8080,function(){ console.log('server listen to %d',this.address().port);});setTimeout(function(){ http.get('http://localhost:8080',function(res){console.log('request ok')}); http.get('http://localhost:8080',function(res){console.log('request ok')});},2000);
这样改后,虽然没报错了,却引入另一个潜在的问题:当并发访问>5时,因为同时可用的底层数据库连接只有5,从而导致了阻塞。
===================================我是分隔线=====================================
实际应用场景中,直接引用db对象并不是一个好主意。默认情况下,db的poolSize=5,意味着并发只有5, 要提高并发的话,把poolSize拉到10? 20? 50? 100? NO,我们需要的是能动态调整连接数的连接池,既能满足高峰期的连接数要求,也能在空闲期释放闲置的连接,而不是象mongodb的内置连接池那样保持固定连接数。怎么办?重新发明轮子吗?不,重用已有的连接池模块genericpool。
_cat server_3.js
var http=require('http'), mongodb = require("mongodb"), poolModule = require('generic-pool');var pool = poolModule.Pool({ name : 'mongodb', create : function(callback) { var server_options={'auto_reconnect':false,poolSize:1}; var db_options={w:-1}; var mongoserver = new mongodb.Server('localhost', 27017,server_options ); var db=new mongodb.Db('test', mongoserver, db_options); db.open(function(err,db){ if(err)return callback(err); callback(null,db); }); }, destroy : function(db) { db.close(); }, max : 10,//根据应用的可能最高并发数设置 idleTimeoutMillis : 30000, log : false });var server=http.createServer(function(req,res){ pool.acquire(function(err, db) { if (err) { res.statusCode=500; res.end(JSON.stringify(err,null,2)); } else { db.collection('foo').save({test:1},function(err,result){ res.end(JSON.stringify(result,null,2)); pool.release(db); }); } });});server.listen(8080,function(){ console.log('server listen to %d',this.address().port);});setTimeout(function(){ http.get('http://localhost:8080',function(res){console.log('request ok')}); http.get('http://localhost:8080',function(res){console.log('request ok')});},2000);
将poolSize设为1,一个db对象只负责一个底层的数据库连接,generic_pool通过控制db对象的数目,间接控制实际的数据库连接数目。如果poolSize还采取默认值5,1db=5连接,由于每次http请求期间我们实际使用到的只是1个连接,其他4个连接根本用不上,处于闲置状态,其结果是浪费资源,拖慢响应速度。
以上。
备注1:本文mongo数据库设置均采用本机安装的默认设置。
备注2:当需建模的业务对象较多时,使用mongoose驱动是个好主意,它自带的连接池比mongodb实用性强。
备注3:mongodb自1.2以后,官方推荐的连接方式改为MongoClient,将参数设置简化为一个URL(详细见http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html)。目前monodb的版本已经1.3,大家也该是时候转变了吧。
以上文generic_pool连接池的初始化为例,看看新旧连接的对比:
旧连接方法
var pool = poolModule.Pool({ name : 'mongodb', create : function(callback) { var server_options={'auto_reconnect':false,poolSize:1}; var db_options={w:-1}; var mongoserver = new mongodb.Server('localhost', 27017,server_options ); var db=new mongodb.Db('test', mongoserver, db_options); db.open(function(err,db){ if(err)return callback(err); callback(null,db); }); }, //......more code here});
新连接方法
var pool = poolModule.Pool({ name : 'mongodb', create : function(callback) { mongodb.MongoClient.connect('mongodb://localhost/test', { server:{poolSize:1} }, function(err,db){ callback(err,db); }); }, //more code here});
原链接:http://cnodejs.org/topic/5190d61263e9f8a542acd83b
- mongodb驱动的正确使用方法
- 正确的关闭mongodb
- 身份证复印件的正确使用方法
- 身份证复印件的正确使用方法
- 电烙铁的正确使用方法
- log的正确使用方法
- 笔记本电池的正确使用方法
- 搜索引擎的正确使用方法
- 正确的iconv使用方法
- QThread的正确使用方法
- priority_queue的正确使用方法
- QThread的正确使用方法
- PresentModalViewController 的正确使用方法
- popToViewController的正确使用方法
- share_ptr的正确使用方法
- @RequestBody的正确使用方法
- QWaitCondition 的正确使用方法
- mongodb的使用方法
- c++中const用法总结
- 而广东省物价部门有关负责人表示,
- Cracking coding interview(4.1)二叉树平衡问题
- quagga 命令定义的代码分析--从定义到实现
- attr,attr_accessor,attr_reader,attr_writer
- mongodb驱动的正确使用方法
- Python编程中的反模式
- RequestDispatcher的用法
- 不同服务器之间的数据库创建触发器会遇到的一些问题
- php下载文件代码
- 带属性的字符串(NSMutableAttributedString)的使用
- Install Ganglia Monitoring System On Ubuntu 13.10 / Debian 7
- HDU 3215 The first place of 2^n (数论-水题)
- 利用JAVA EE 实现多个页面的跳转