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

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 药流15天同房了怎么办 药流22天同房了怎么办 药流一直不排出怎么办 宝宝脸过敏红了怎么办 小孩湿疹脸上都是红红的怎么办 眼周刺痛红红的怎么办 脸敷面膜刺痛红红的怎么办 脸上有凹凸不平的坑怎么办 宝宝脸上角质层薄有红血丝怎么办 红衣军到决赛圈怎么办 宝宝湿疹留下的黑印怎么办 出牙宝宝很烦躁怎么办 法斗嘴唇破了怎么办 狗嘴巴周围红了怎么办 脸上起红包还痒怎么办 睾丸胀痛有下坠感怎么办 英语不好又不会读怎么办 七个月宝宝手上长倒刺怎么办 字母纹身纹反了怎么办 花甲生的吃了怎么办 别人告我欠他钱怎么办 实习手册没有公司的印章怎么办? 家长管的太严怎么办 对于老公沉迷于股票怎么办 月经期吃了香瓜怎么办 月经漏到内裤上怎么办 上班没时间养狗怎么办 宝宝呼吸道感染反复发烧怎么办 熬夜后头晕想吐怎么办 生气后全身无力酸痛怎么办? 久坐导致的腰疼怎么办 削土豆手变黑了怎么办 熬夜后头痛眼痛怎么办 孩子毎天通宵游戏怎么办 熬夜写作业困了怎么办 三十多岁白头发越来越多怎么办 AI界面字体太小怎么办 睡不着怎么办躺倒床上脑子混乱 作息规律不正常夜里睡不着怎么办 作息不规律引起身体痒怎么办 在大学里好无聊怎么办