天天看點

用Nodejs連接配接MySQL

用Nodejs連接配接MySQL

從零開始nodejs系列文章,将介紹如何利Javascript做為服務端腳本,通過Nodejs架構web開發。Nodejs架構是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏覽器就基于V8,同時打開20-30個網頁都很流暢。Nodejs标準的web開發架構Express,可以幫助我們迅速建立web站點,比起PHP的開發效率更高,而且學習曲線更低。非常适合小型網站,個性化網站,我們自己的Geek網站!!

關于作者

  • 張丹(Conan), 程式員Java,R,PHP,Javascript
  • weibo:@Conan_Z
  • blog: http://blog.fens.me
  • email: [email protected]

轉載請注明出處:

http://blog.fens.me/nodejs-mysql-intro/

用Nodejs連接配接MySQL

前言

MySQL是一款常用的開源資料庫産品,通常也是免費資料庫的首選。查了一下NPM清單,發現Nodejs有13庫可以通路MySQL,felixge/node-mysql似乎是最受關注項目,我也決定嘗試用一下。

要注意名字,”felixge/node-mysql”非”node-mysql”,安裝部分會介紹這個小插曲!

目錄

  1. node-mysql介紹
  2. 建立MySQL測試庫
  3. node-mysql安裝
  4. node-mysql使用

1. node-mysql介紹

felixge/node-mysql是一個純nodejs的用javascript實作的一個MySQL用戶端程式。felixge/node-mysql封裝了Nodejs對MySQL的基本操作,100% MIT公共許可證。

項目位址:https://github.com/felixge/node-mysql

2. 建立MySQL測試庫

本地建立MySQL測試庫:nodejs

~ mysql -uroot -p
mysql> CREATE DATABASE nodejs;
mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| nodejs             |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

mysql> GRANT ALL ON nodejs.* to nodejs@'%' IDENTIFIED BY 'nodejs';
mysql> GRANT ALL ON nodejs.* to nodejs@localhost IDENTIFIED BY 'nodejs';      

重新登陸MySQL

C:\Users\Administrator>mysql -unodejs -p
Enter password: ******

mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| nodejs             |
| test               |
+--------------------+
3 rows in set (0.00 sec)

mysql> USE nodejs
Database changed
      

  

建立一個user表

CREATE TABLE t_user(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(16) NOT NULL ,
create_date TIMESTAMP NULL DEFAULT now()
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE UNIQUE INDEX t_quiz_IDX_0 on t_user(name);

mysql> SHOW TABLES;
+------------------+
| Tables_in_nodejs |
+------------------+
| t_user           |
+------------------+
1 row in set (0.04 sec)
      

3. node-mysql安裝

我的系統環境

  • win7 64bit
  • Nodejs:v0.10.5
  • Npm:1.2.19
  • MySQL:Server version: 5.6.11 MySQL Community Server (GPL)

建立工程:nodejs-node-mysql

~ D:\workspace\javascript>mkdir nodejs-node-mysql
~ D:\workspace\javascript>cd nodejs-node-mysql
~ D:\workspace\javascript\nodejs-node-mysql>npm install node-mysql
[email protected] node_modules\node-mysql
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected] ([email protected], [email protected])
      

這裡有一個小插曲

安裝“node-mysql”後,打開package.json檔案發現,這個項目位址是

https://github.com/redblaze/node-mysql.git           

從依賴關系可以看到,它依賴于mysql庫,是對felixge/node-mysql的封裝。

用Nodejs連接配接MySQL

由于這個項目star是0,fork也是0. 是以,我也不準備花時間測試了,重新安裝felixge/node-mysql的包。

重新安裝node-mysql

~ D:\workspace\javascript\nodejs-node-mysql>rm -rf node_modules
~ D:\workspace\javascript\nodejs-node-mysql>npm install [email protected]
npm http GET https://registry.npmjs.org/mysql/2.0.0-alpha9
npm http 200 https://registry.npmjs.org/mysql/2.0.0-alpha9
npm http GET https://registry.npmjs.org/mysql/-/mysql-2.0.0-alpha9.tgz
npm http 200 https://registry.npmjs.org/mysql/-/mysql-2.0.0-alpha9.tgz
npm http GET https://registry.npmjs.org/require-all/0.0.3
npm http GET https://registry.npmjs.org/bignumber.js/1.0.1
npm http 304 https://registry.npmjs.org/require-all/0.0.3
npm http 304 https://registry.npmjs.org/bignumber.js/1.0.1
[email protected] node_modules\mysql
├── [email protected]
└── [email protected]
      

這回就對了,繼續下面的開發!

建立node程式啟動檔案:app.js

第一個測試

~ vi app.js           
var mysql = require('mysql');
var conn = mysql.createConnection({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database:'nodejs',
    port: 3306
});
conn.connect();
conn.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
    if (err) throw err;
    console.log('The solution is: ', rows[0].solution);
});
conn.end();
      

運作node

~ D:\workspace\javascript\nodejs-node-mysql>node app.js
The solution is:  2
      

這樣我們就讓Nodejs連接配接上了MySQL。

4. node-mysql使用

下面我們要對node-mysql的API進行常用的測試。

  • 表新删改查
  • 連接配接池配置
  • MySQL斷線重連
  • 連接配接池逾時測試

1). 表新删改查

修改app.js

~ vi app.js           
var mysql = require('mysql');
var conn = mysql.createConnection({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});
conn.connect();

var insertSQL = 'insert into t_user(name) values("conan"),("fens.me")';
var selectSQL = 'select * from t_user limit 10';
var deleteSQL = 'delete from t_user';
var updateSQL = 'update t_user set name="conan update"  where name="conan"';

//delete
conn.query(deleteSQL, function (err0, res0) {
    if (err0) console.log(err0);
    console.log("DELETE Return ==> ");
    console.log(res0);

    //insert
    conn.query(insertSQL, function (err1, res1) {
        if (err1) console.log(err1);
        console.log("INSERT Return ==> ");
        console.log(res1);

        //query
        conn.query(selectSQL, function (err2, rows) {
            if (err2) console.log(err2);

            console.log("SELECT ==> ");
            for (var i in rows) {
                console.log(rows[i]);
            }

            //update
            conn.query(updateSQL, function (err3, res3) {
                if (err3) console.log(err3);
                console.log("UPDATE Return ==> ");
                console.log(res3);

                //query
                conn.query(selectSQL, function (err4, rows2) {
                    if (err4) console.log(err4);

                    console.log("SELECT ==> ");
                    for (var i in rows2) {
                        console.log(rows2[i]);
                    }
                });
            });
        });
    });
});

//conn.end();      

控制台輸出:

D:\workspace\javascript\nodejs-node-mysql>node app.js
DELETE Return ==>
{ fieldCount: 0,
  affectedRows: 2,
  insertId: 0,
  serverStatus: 34,
  warningCount: 0,
  message: '',
  protocol41: true,
  changedRows: 0 }
INSERT Return ==>
{ fieldCount: 0,
  affectedRows: 2,
  insertId: 33,
  serverStatus: 2,
  warningCount: 0,
  message: '&Records: 2  Duplicates: 0  Warnings: 0',
  protocol41: true,
  changedRows: 0 }
SELECT ==>
{ id: 33,
  name: 'conan',
  create_date: Wed Sep 11 2013 12:09:15 GMT+0800 (中國标準時間) }
{ id: 34,
  name: 'fens.me',
  create_date: Wed Sep 11 2013 12:09:15 GMT+0800 (中國标準時間) }
UPDATE Return ==>
{ fieldCount: 0,
  affectedRows: 1,
  insertId: 0,
  serverStatus: 2,
  warningCount: 0,
  message: '(Rows matched: 1  Changed: 1  Warnings: 0',
  protocol41: true,
  changedRows: 1 }
SELECT ==>
{ id: 33,
  name: 'conan update',
  create_date: Wed Sep 11 2013 12:09:15 GMT+0800 (中國标準時間) }
{ id: 34,
  name: 'fens.me',
  create_date: Wed Sep 11 2013 12:09:15 GMT+0800 (中國标準時間) }
      

由于node的異步的,上面是一個連續的操作,代碼會被寫的支離破碎。我們可以通過async庫對上面代碼進行封裝,請參考文章:Nodejs異步流程控制Async

2). 連接配接池配置

增加檔案:app-pooling.js

~ vi app-pooling.js           
var mysql = require('mysql');
var pool = mysql.createPool({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});

var selectSQL = 'select * from t_user limit 10';

pool.getConnection(function (err, conn) {
    if (err) console.log("POOL ==> " + err);

    conn.query(selectSQL,function(err,rows){
        if (err) console.log(err);
        console.log("SELECT ==> ");
        for (var i in rows) {
            console.log(rows[i]);
        }
        conn.release();
    });
});      
D:\workspace\javascript\nodejs-node-mysql>node app-pooling.js
SELECT ==>
{ id: 39,
  name: 'conan update',
  create_date: Wed Sep 11 2013 13:41:18 GMT+0800 (中國标準時間) }
{ id: 40,
  name: 'fens.me',
  create_date: Wed Sep 11 2013 13:41:18 GMT+0800 (中國标準時間) }
      

3). MySQL斷線重連

分别模拟3種錯誤

  • 登陸密碼錯誤
  • 資料庫當機
  • 資料庫連接配接逾時

新增檔案:app-reconnect.js

~ vi app-reconnect.js           
var mysql = require('mysql');
var conn;
function handleError () {
    conn = mysql.createConnection({
        host: 'localhost',
        user: 'nodejs',
        password: 'nodejs',
        database: 'nodejs',
        port: 3306
    });

    //連接配接錯誤,2秒重試
    conn.connect(function (err) {
        if (err) {
            console.log('error when connecting to db:', err);
            setTimeout(handleError , 2000);
        }
    });

    conn.on('error', function (err) {
        console.log('db error', err);
        // 如果是連接配接斷開,自動重新連接配接
        if (err.code === 'PROTOCOL_CONNECTION_LOST') {
            handleError();
        } else {
            throw err;
        }
    });
}
handleError();      

a. 模拟密碼錯誤

修改password: ‘nodejs11’

控制台輸出。

D:\workspace\javascript\nodejs-node-mysql>node app-reconnect.js
error when connecting to db: { [Error: ER_ACCESS_DENIED_ERROR: Access denied for user 'nodejs'@'localhost' (using pass
rd: YES)]
  code: 'ER_ACCESS_DENIED_ERROR',
  errno: 1045,
  sqlState: '28000',
  fatal: true }
error when connecting to db: { [Error: ER_ACCESS_DENIED_ERROR: Access denied for user 'nodejs'@'localhost' (using pass
rd: YES)]
  code: 'ER_ACCESS_DENIED_ERROR',
  errno: 1045,
  sqlState: '28000',
  fatal: true }
      

b. 模拟資料庫當機

正常啟動node,然後殺掉mysqld的程序。

D:\workspace\javascript\nodejs-node-mysql>node app-reconnect.js
db error { [Error: read ECONNRESET]
  code: 'ECONNRESET',
  errno: 'ECONNRESET',
  syscall: 'read',
  fatal: true }

Error: read ECONNRESET
    at errnoException (net.js:884:11)
    at TCP.onread (net.js:539:19)
      

這個異常,直接導緻node程式被殺死!

c. 模拟連接配接逾時,PROTOCOL_CONNECTION_LOST

切換到root賬戶, 修改MySQL的wait_timeout參數,設定為10毫秒逾時。

~ mysql -uroot -p
mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.00 sec)

mysql> set global wait_timeout=10;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like 'wait_timeout';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 10    |
+---------------+-------+
1 row in set (0.00 sec)
      

修改檔案:app-reconnection.js,在最後增加代碼

~ vi app-reconnection.js           
function query(){
    console.log(new Date());
    var sql = "show variables like 'wait_timeout'";
    conn.query(sql, function (err, res) {
        console.log(res);
    });
}

query();
setInterval(query, 15*1000);
      

程式會每融15秒,做一次查詢。

控制台輸出

D:\workspace\javascript\nodejs-node-mysql>node app-reconnect.js
Wed Sep 11 2013 15:21:14 GMT+0800 (中國标準時間)
[ { Variable_name: 'wait_timeout', Value: '10' } ]
db error { [Error: Connection lost: The server closed the connection.] fatal: true, code: 'PROTOCOL_CONNECTION_LOST' }
Wed Sep 11 2013 15:21:28 GMT+0800 (中國标準時間)
[ { Variable_name: 'wait_timeout', Value: '10' } ]
db error { [Error: Connection lost: The server closed the connection.] fatal: true, code: 'PROTOCOL_CONNECTION_LOST' }
Wed Sep 11 2013 15:21:43 GMT+0800 (中國标準時間)
[ { Variable_name: 'wait_timeout', Value: '10' } ]
      

我們自己的程式捕獲了“PROTOCOL_CONNECTION_LOST”異常,并自動的實作了資料庫重連。

4). MySQL連接配接池的逾時測試

針對wait_timeout問題,我們再對連接配接做一下測試。

修改app-pooling.js檔案

var mysql = require('mysql');
var pool = mysql.createPool({
    host: 'localhost',
    user: 'nodejs',
    password: 'nodejs',
    database: 'nodejs',
    port: 3306
});

var selectSQL ="show variables like 'wait_timeout'";

pool.getConnection(function (err, conn) {
    if (err) console.log("POOL ==> " + err);

    function query(){
        conn.query(selectSQL, function (err, res) {
            console.log(new Date());
            console.log(res);
            conn.release();
        });
    }
    query();
    setInterval(query, 5000);
});
      
D:\workspace\javascript\nodejs-node-mysql>node app-pooling.js
Wed Sep 11 2013 15:32:25 GMT+0800 (中國标準時間)
[ { Variable_name: 'wait_timeout', Value: '10' } ]
Wed Sep 11 2013 15:32:30 GMT+0800 (中國标準時間)
[ { Variable_name: 'wait_timeout', Value: '10' } ]
Wed Sep 11 2013 15:32:35 GMT+0800 (中國标準時間)
[ { Variable_name: 'wait_timeout', Value: '10' } ]      

連接配接池,已經解決了自動重連的問題了,後面我們的開發,可以盡量使用pooling的方式。