解决原生NodeJS 操作MongoDB 数据库的性能问题,封装成更小、更灵活的操作MongoDB库:
Config.js 将所要连接的数据的配置信息封装成一个模块:
const Config = {
dbUrl:'mongodb://admin:123@localhost:27017/',
dbName:'hello'
}
module.exports=Config;
封装数据库时,引入数据库配置模块就好,首先通过 promise 封装:
const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');
class Db {
constructor() {
}
//连接数据库
connect() {
console.time('time_connect')
return new Promise((resolve, reject) => {
MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true },(err, client) => {
if (err) {
console.log("连接数据库失败")
reject(err)
return
}
const db = client.db(Config.dbName);
console.log("连接数据库成功")
console.timeEnd('time_connect')
resolve(db)
})
})
}
//查询数据
find(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then((db) => { //连接数据库返回的db对象
db.collection(collectionName).find(json).toArray(function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
})
})
})
}
}
console.time('time_find1')
const myDb1 = new Db()
myDb1.find('user', {}).then(data=> { //find返回的是promise
console.log("查询数据1");
console.timeEnd('time_find1')
})
console.time('time_find2')
const myDb2 = new Db()
myDb2.find('user', {"name":"jack3"}).then(data => { //find返回的是promise
console.log("查询数据2");
console.timeEnd('time_find2')
})
/**
* 连接数据库成功
* time_connect: 49.488ms
* 连接数据库成功
* (node:12552) Warning: No such label 'time_connect' for console.timeEnd()
* 查询数据1
* time_find1: 65.757ms
* 查询数据2
* time_find2: 53.866ms
*/
发生了两个问题,一是每次查询都需要连接数据库(查询时连接数据库获得 client 对象 ),而是每次查询都需要重新创建一个实例(myDb1,myDb2 )
首先解决多次连接数据库的问题:
可以在构造函数中定义一个 dbClient 的属性,通过这个属性值判断是否成功连接了数据库:
测试:
const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');
class Db {
constructor() {
this.dbClient="";
}
//连接数据库
connect() {
console.time('time_connect')
return new Promise((resolve, reject) => {
if(!this.dbClient){
MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
if (err) {
console.log("连接数据库失败")
reject(err)
return
}
this.dbClient = client.db(Config.dbName);
console.log("连接数据库成功")
console.timeEnd('time_connect')
resolve(this.dbClient)
})
}
else{
resolve(this.dbClient)
}
})
}
//查询数据
find(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then(db => { //连接数据库连接返回的db对象
db.collection(collectionName).find(json).toArray(function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
})
})
})
}
}
const myDb = new Db()
console.time('time_find1')
myDb.find('user', {}).then(data => {
console.log("查询数据1");
console.timeEnd('time_find1')
})
setTimeout(()=>{
console.time('time_find2')
myDb.find('user', {}).then(data => {
console.log("查询数据2");
console.timeEnd('time_find2')
})
},3000)
/**
* 连接数据库成功
* time_connect: 47.374ms
* 查询数据1
* time_find1: 70.183ms
* 查询数据2
* time_find2: 2.112ms
*/
但是多次实例化时,dbClient 重新为"",还是会再次去连接数据库:
const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');
class Db {
constructor() {
this.dbClient="";
}
//连接数据库
connect() {
console.time('time_connect')
return new Promise((resolve, reject) => {
if(!this.dbClient){
MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
if (err) {
console.log("连接数据库失败")
reject(err)
return
}
this.dbClient = client.db(Config.dbName);
console.log("连接数据库成功")
console.timeEnd('time_connect')
resolve(this.dbClient)
})
}
else{
resolve(this.dbClient)
}
})
}
//查询数据
find(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then(db => { //连接数据库连接返回的db对象
db.collection(collectionName).find(json).toArray(function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
})
})
})
}
}
const myDb1 = new Db()
console.time('time_find1')
myDb1.find('user', {}).then(data => {
console.log("查询数据1");
console.timeEnd('time_find1')
})
const myDb2 = new Db()
setTimeout(()=>{
console.time('time_find2')
myDb2.find('user', { "name": "jack5" }).then(data => {
console.log("查询数据2");
console.timeEnd('time_find2')
})
},2000)
/**
* 连接数据库成功
* time_connect: 42.574ms
* 查询数据1
* time_find1: 55.639ms
* 连接数据库成功
* time_connect: 5.675ms
* 查询数据2
* time_find2: 16.233ms
*/
通过单例模式解决这个问题:
const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');
class Db {
static getInstance(){
if (!Db.instance) { //单例 解决多次实例化实例不共享的问题
Db.instance=new Db()
}
return Db.instance
}
constructor() {
this.dbClient="";
}
//连接数据库
connect() {
console.time('time_connect')
return new Promise((resolve, reject) => {
if (!this.dbClient) { //解决数据库多次连接的问题
MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
if (err) {
console.log("连接数据库失败")
reject(err)
return
}
this.dbClient = client.db(Config.dbName);
console.log("连接数据库成功")
console.timeEnd('time_connect')
resolve(this.dbClient)
})
}
else{
resolve(this.dbClient)
}
})
}
//查询数据
find(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then(db => { //连接数据库连接返回的db对象
db.collection(collectionName).find(json).toArray(function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
})
})
})
}
}
const myDb1 = Db.getInstance() //相当于 myDb1 = new Db()
console.time('time_find1')
myDb1.find('user', {}).then(data => {
console.log("查询数据1");
console.timeEnd('time_find1')
})
const myDb2 = Db.getInstance() //相当于 myDb2 = Db.instance
setTimeout(()=>{
console.time('time_find2')
myDb2.find('user', { "name": "jack5" }).then(data => {
console.log("查询数据2");
console.timeEnd('time_find2')
})
},2000)
/**
* 连接数据库成功
* time_connect: 41.880ms
* 查询数据1
* time_find1: 55.679ms
* 查询数据2
* time_find2: 2.045ms
*/
最后封装好的 db库将它暴露出去,这里还可以在初始化的时候就连接数据库,第一次操作数据库在初始化就连接上,而不是执行具体操作的时候连接数据库:
const MongoClient = require('mongodb').MongoClient;
const Config = require('./config.js');
class Db {
static getInstance() {
if (!Db.instance) { //单例 解决多次实例化实例不共享的问题
Db.instance = new Db()
}
return Db.instance
}
constructor() {
this.dbClient = "";
this.connect()
}
//连接数据库
connect() {
//console.time('time_connect')
return new Promise((resolve, reject) => {
if (!this.dbClient) { //解决数据库多次连接的问题
MongoClient.connect(Config.dbUrl, { useUnifiedTopology: true }, (err, client) => {
if (err) {
console.log("连接数据库失败")
reject(err)
return
}
this.dbClient = client.db(Config.dbName);
console.log("连接数据库成功")
//console.timeEnd('time_connect')
resolve(this.dbClient)
})
}
else {
resolve(this.dbClient)
}
})
}
//查询数据
find(collectionName, json) {
return new Promise((resolve, reject) => {
this.connect().then(db => { //连接数据库连接返回的db对象
db.collection(collectionName).find(json).toArray(function (err, result) {
if (err) {
reject(err);
return;
}
resolve(result);
})
})
})
}
//修改数据(一条)
update(collectionName, oldData, newData) {
return new Promise((resolve, reject) => {
this.connect().then((db) => {
db.collection(collectionName).updateOne(oldData, { $set: newData }, (err, result) => {
if (err) {
console.log("修改数据失败")
reject(err)
return;
}
console.log("修改数据成功")
resolve(result)
})
})
})
}
//添加数据(一条)
insert(collectionName, addData) {
return new Promise((resolve, reject) => {
this.connect().then(db => {
db.collection(collectionName).insertOne(addData, (err, result) => {
if (err) {
console.log("添加数据失败")
reject(err)
return
}
console.log("添加数据成功")
resolve(result)
})
})
})
}
//删除数据(一条)
remove(collectionName, deleteData) {
return new Promise((resolve, reject) => {
this.connect().then(db => {
db.collection(collectionName).removeOne(deleteData, (err, result) => {
if (err) {
console.log("删除数据失败")
reject(err)
return
}
console.log("删除数据成功")
resolve(result)
})
})
})
}
}
module.exports = Db.getInstance()
引入封装好的 db 库,通过 kob 操作:
const Koa = require('koa')
const app = new Koa()
const router = require('koa-router')()
const render = require('koa-art-template')
const views = require('koa-views')
const path = require('path')
const Db = require('./util/db')
//配置art-template
render(app, {
root: path.join(__dirname, 'views'), // 视图的位置
extname: '.html', // 后缀名
debug: process.env.NODE_ENV !== 'production' //是否开启调试模式
})
router.get('/', async (ctx)=>{
let res = await Db.find('user', { "name": "李华" })
//console.log(res) //{ _id: 5eef338f7701003ebcbbafa3, name: '李华', age: 18 }
await ctx.render('index', {
name: res[0].name,
age: res[0].age
})
})
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3000);