CNode

在Node.js使用mysql模块时遇到的坑

精华
Lleizongmin发布于13 年前最后回复8 年前43 回复139206 浏览0 收藏

之前写了个小程序Node News,用到了MySQL数据库,在本地测试均没神马问题。放上服务器运行一段时间后,偶然发现打开页面的时候页面一直处于等待状态,直到Nginx返回超时错误。于是上服务器检查了遍,发现程序仍然在运行,且能正确记录每次的请求,再修改代码跟踪调试,原来是在查询数据库的时候,回调一直没有被执行,程序就挂在那里了。

想了很久也想不明白为神马mysql模块没有执行回调,最后突然想起来去看了下错误日志,才发现有个“No reconnection after connection lost”错误没有被捕捉到,原来是连接丢失了,上github上看了下文档和issues,上面说到连接丢失后不会自动重新连接,会触发error事件。我赶紧给程序添加了断线后自动重连功能,现在已正常运行了10多天。

MySQL中有一个名叫wait_timeout的变量,表示操作超时时间,当连接超过一定时间没有活动后,会自动关闭该连接,这个值默认为28800(即8小时)。

自动重连数据库的代码:

function handleError (err) {
  if (err) {
    // 如果是连接断开,自动重新连接
    if (err.code === 'PROTOCOL_CONNECTION_LOST') {
      connect();
    } else {
      console.error(err.stack || err);
    }
  }
}

// 连接数据库
function connect () {
  db = mysql.createConnection(config);
  db.connect(handleError);
  db.on('error', handleError);
}

var db;
connect();

网上流传的大多数使用mysql模块的代码,往往忽略了这个问题,一不小心就让一拨又一拨的人往坑里踩了。

有童鞋回复问使用pool又会怎样,于是去看了下mysql模块的源码:目前可在npm中安装到的最新版本为2.0.0-alpha7,使用mysql.createPool()来创建的pool没办法自动处理连接被关闭的问题,但是在github上的版本已经修复了(应该还没发布到npm上),当触发了connection的error事件时,会把该connection对象从连接池中移除。(源码:https://github.com/felixge/node-mysql/blob/master/lib/Pool.js#L119

使用pool的代码:

var mysql = require('mysql');
var pool  = mysql.createPool(config);

pool.getConnection(function(err, connection) {
  // Use the connection
  connection.query( 'SELECT something FROM sometable', function(err, rows) {
    // And done with the connection.
    connection.end();

    // Don't use the connection here, it has been returned to the pool.
  });
});

参考: 《Nodejs使用mysql》 《No reconnection after connection lost》 《mysql自动断开连接的问题处理方案》 《MySQL数据库连接超时(wait_timeout)问题的处理

原文地址:http://writings.ucdok.com/articles/problems-on-nodejs-mysql 转载时请注明出处。

查看回复

回复 (43)

Q
qinianqing#2·8 年前

这几天刚好遇到这个

D
deajosha#3·11 年前

果断顶

H
hexie#4·13 年前

不知道在

var mysql = require('mysql');

var pool = mysql.createPool({ host : 'example.org', user : 'bob', password : 'secret' });

pool.getConnection(function(err, connection) { // connected! (unless err is set) });

情况会表现得如何。。

D
DoubleSpout#5·13 年前

老雷好贴,顶起~

L
leizongmin#7·13 年前
H
hexie#8·13 年前

果断遇到这个情况了, = 。= 天真以为这种事情,不会发生在自己身上,审核的时候发现网站 连接不了,要好好热修复了。。

H
hexie#9·13 年前

请问要更新到最新版本,该怎么弄呢?

L
leizongmin#10·13 年前
引用 hexie请问要更新到最新版本,该怎么弄呢?

最新版本已经解决了连接池断开后不能自动连接问题: npm install mysql@2.0.0-alpha8

I
ibigbug#11·13 年前

为什么node也有类似,之前知道python的一个sqlalchemy也有Mysql has gone away的问题。

L
leizongmin#12·13 年前
引用 ibigbug为什么node也有类似,之前知道python的一个sqlalchemy也有Mysql has gone away的问题。

这不是Node.js的问题,而是MySQL的一个机制,在客户端长时间不活动时,自动关闭其连接。而由于客户端程序不够完善,未能正确处理而已。

I
iumex#13·13 年前

有这么坑吗。表示没有遇到 过。

L
leizongmin#14·13 年前
引用 iumex有这么坑吗。表示没有遇到 过。

要么是你不是用这个模块,要么是你刚好赶上了好时代人家把这个问题Fixed了你再用的,要么是你很厉害写代码很谨慎。

I
iumex#15·13 年前
引用 iumex有这么坑吗。表示没有遇到 过。

@leizongmin 好吧。 我生在新中国长在红旗下了。

Y
youxiachai#16·13 年前

https://github.com/sequelize/sequelize 对于这个支持多种方言的数据库怎么看?支持MySQL, SQLite and PostgreSQL

A
asoedarren#17·13 年前

但我記得handleError 很久以前官方文件就有了 應該使用這個module的人都會會加

如果不想要被wait timeout踢掉 建議一段時間就作ping 這樣可以一直保持連接

pool的我是用generic-pool自己作掉了

不過曾經有遇到過一個問題是 跨internet連接mysql 有時會突然完全沒有回應 handleError也沒收到任何錯誤 不知道這bug改掉了沒..

D
duyinghua#18·13 年前

但是当用到connect-mysql第三方包来存session信息时却解决不了

L
leizongmin#19·13 年前
引用 duyinghua但是当用到connect mysql第三方包来存session信息时却解决不了

这个是模块不完善导致的问题,新的mysql模块,在使用pool的时候已经可以自动重连了,而connect-mysql模块应该使用mysql的pool来建立连接。 要么你等待connect-mysql模块的作者来完善,要么就干脆基于connect-mysql的代码,自己来改一下了。

Y
yanyumiao#20·12 年前

应该第一时间想到查看日志 (☆_☆)

Y
yanyumiao#21·12 年前

好帖

R
rivera-wang#22·12 年前

mysql模块里面的pool非常好用,可以设置自动重连,poolcluster还可以支持多个master和slave

F
fangker#24·10 年前

mark

J
Jiasm#25·10 年前

三年前就有Pool了么。。。

L
leizongmin#26·10 年前
引用 Jiasm三年前就有Pool了么。。。

@Jiasm 可能不止三年前了……

M
maochunguang#27·10 年前

好文章,避免了一个大坑

B
bykege#28·10 年前

楼主好,如果pool 用end()释放资源的话,再次进行数据库操作时会提示错误:

Error: Connection already released 之后再重新连接,导致系统卡掉,请问有什么解决方法吗

L
leizongmin#31·10 年前
引用 bykege@leizongmin 是否可以给个demo

@bykege 本文那段代码就是demo了

B
bykege#32·10 年前
引用 leizongmin@bykege 本文那段代码就是demo了

@leizongmin

pool.getConnection(function(err, connection) {
  // Use the connection
  connection.query( 'SELECT something FROM sometable', function(err, rows) {
    // And done with the connection.
    connection.end();

    // Don't use the connection here, it has been returned to the pool.
  });
});

用这种方法,链接第一次时可行,再进行数据库链接会由于 connection.end()而在终端报错,停顿1秒后才再度链接

L
leizongmin#33·10 年前
引用 bykege@leizongmin 用这种方法,链接第一次时可行,再进行数据库链接会由于 connection.end()而在终端报错,停顿1秒后才再度链接

@bykege

function myQuery(sql, callback) {
  pool.getConnection(function (err, conn) {
    if (err) return callback(err);
    conn.query(sql, function (err, ret) {
      conn.end();
      callback(err, ret);
    }
  }
}

myQuery('ooxx', console.log)
myQuery('xxoo', console.log)
B
bykege#34·10 年前
引用 leizongmin@bykege

@leizongmin 感谢

C
captainblue2013#35·10 年前

我从一开始就用的easymysql ,不用自己end ,带pool ,带心跳,稳定的一比,不过也很久没更新了,依然好用

X
XadillaX#36·10 年前

三年前...

B
bykege#37·10 年前
引用 captainblue2013@bykege easymysql ,npm主页上还是中文的,应该就是国产的了。 你的写法吧,需要一个callback

@captainblue2013 关于这个模块有两点问题 1:没有选定要操作的数据库 2:回调函数如何调用

B
bykege#39·10 年前
引用 captainblue2013我从一开始就用的easymysql ,不用自己end ,带pool ,带心跳,稳定的一比,不过也很久没更新了,依然好用

@captainblue2013 用你说的模块写一个连接池的函数如下

User.getUsrList=function getUsrList(){ mysql.query({ sql: 'select * from user where user =:user', params: {user: 'xxoo'} }, function (err, rows) { // console.log(rows); }); }; 请问写法是否正确,如何进行调用呢

C
captainblue2013#40·10 年前
引用 bykege@captainblue2013 用你说的模块写一个连接池的函数如下 User.getUsrList=function getUsrList(){ mysql.query({ sql: 'se...

@bykege easymysql ,npm主页上还是中文的,应该就是国产的了。 你的写法吧,需要一个callback

C
captainblue2013#42·10 年前
引用 bykege@captainblue2013 关于这个模块有两点问题 1:没有选定要操作的数据库 2:回调函数如何调用

@bykege 1.有的,你看看官方的样例 2.你这样写怎么拿到返回呢?自己想想

B
bykege#43·10 年前
引用 captainblue2013@bykege 1.有的,你看看官方的样例 2.你这样写怎么拿到返回呢?自己想想

@captainblue2013 可以给我一个你的Demo吗,要对外提供数据库操作函数的那种

参与回复
登录后即可参与回复。登录