node写后台的时候,连接数据库是基本操作,当然为了方便开发中的使用,自然要对连接数据库的方式进行一定的封装,导出之后再使用。下面使用MySql来讲讲链接数据库的各种姿势。
以下是开发中的常规操作:
// db.js
const mysql = require('mysql')
const con = mysql.createConnection({
host : "hostName",
user : "username",
password: "password"
})
// 开始连接数据库
con.connect()
function querySql(sql, callback){
con.query(sql, (err, result) => {
if (err) {
callback(err, null)
} else {
callback(err, result)
}
})
}
module.exports = {
querySql
}
// js 在这里使用导出的查库函数
const { querySql } = require('db.js')
querySql('select * from database', (err, resutl) => {
// do some thing
})
在开发中我们只需要能够查询到数据库的数据就可以了,所以不用考虑数据库连接消耗的资源,一直开启一个连接使用就可以了,上面这种写法能够满足开发用。当然线上是不能这样写的,因为数据库每个连接有一个最大连接时间,约8个小时,时间过了就会自动断开连接,这样是肯定不行的,而且考虑到数据库一直处于连接状态会消耗大量资源,还可能导致内存泄漏,我们可以每次查询创建一个连接,使用完之后关闭。
在改造上面的代码之前我们先看看,这个代码里出现了callback,也就是回调函数。在我的上一篇文章里提到,遇到这种出现回调函数的情况,我们可以用promise来改造回调过程,避免可能出现的回调地狱,使用起来也更加的合理。
改造一下上面的querySql函数
const MYSQL_CONF = {
host : "hostName",
user : "username",
password: "password"
}
function querySql(sql) {
// 创建连接对象
const con = mysql.createConnection(MYSQL_CONF)
// 开始连接数据库
con.connect()
return new Promise((resolve, reject) => {
con.query(sql, (err, result) => {
if (err) {
reject(err)
return
}
resolve(result)
})
// 关闭连接
con.end()
})
}
// js
const { querySql } = require('db.js')
querySql('select * from database').then(resutl => {
// do some thing
}).catch(err => {
// do some thing
})
这样似乎不错了,每次查询完毕都会关闭连接,不会导致数据库长期连接出现内存泄漏。
当然,这样只能应对访问量不大,并发执行数据库操作不多的时候。像上面这种写法,每一个用户的每次数据库操作都会创建一个连接,为每个用户打开和维护数据库连接,特别是对动态数据库驱动的网站应用程序的请求,代价高昂,十分浪费资源,并且访问量一大,就有可能挂掉。所以一般使用数据库连接池的方式来连接数据库。
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间(未使用的链接会被放到连接池中处于空闲状态)超过最大空闲时间(就是上文提到的8h后自动断开连接的时间)的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。
先来写一下mysql连接池的基本写法
const pool = mysql.createPool(MYSQL_CONF)
pool.getConnection((err, connetion) => {
// connetion就是从连接池里拿到的连接
connection.query( "select * from table1", (err, result) => {
// do some thing
})
// 注意这里不是连接end掉了,而是释放掉,归还回连接池里
connection.release()
})
好的,连接池也会写了,之后封装成promise,再导出就完成了
// 创建mysql连接池
const pool = mysql.createPool(MYSQL_CONF)
// 统一处理sql语句
function querySql(sql) {
// 使用promise对象处理查询的数据
const promise = new Promise((resolve, reject) => {
// 从连接池里获取一个连接
pool.getConnection((err, connection) => {
if (err) {
reject(err)
return
} else {
connection.query(sql, (err, result) => {
if (err) {
reject(err)
return
} else {
resolve(result)
}
})
}
// 释放本次连接
connection.release()
})
})
return promise
}
module.exports = {
querySql
}
参考
Cuyas consecuencias incluyen infarto de miocardio, si usted tiene algunos problemas en la vida sexual. Los casos representaron sólo el 1 o cuando Pfizer lanzó el Levitra Y Nervios dañados, desequilibrios hormonales, limitaciones del estudio son o dinitrato de isosorbida.
实际生产开发,尽量多用ORM,比如 sequelize 或者更新一点的 typeorm 之类的框架