web-dev-qa-db-ja.com

node.js + mysql接続プーリング

私は、MySQLを最も効率的な方法で使用するためにアプリケーションを構成する方法を見つけようとしています。 node-mysqlモジュールを使用しています。ここの他のスレッドは接続プーリングを使用することを提案したので、小さなモジュールmysql.jsをセットアップしました

var mysql = require('mysql');

var pool  = mysql.createPool({
    Host     : 'localhost',
    user     : 'root',
    password : 'root',
    database : 'guess'
});

exports.pool = pool;

これで、mysqlを照会するたびに、このモジュールが必要になり、次にデータベースを照会します

var mysql = require('../db/mysql').pool;

var test = function(req, res) {
     mysql.getConnection(function(err, conn){
         conn.query("select * from users", function(err, rows) {
              res.json(rows);
         })
     })
}

これは良いアプローチですか?メインのapp.jsスクリプトですべてが行われる非常に単純な接続以外に、mysql接続の使用例をあまり見つけることができなかったので、慣習/ベストプラクティスが何であるかは本当にわかりません。

各クエリの後に必ずconnection.end()を使用する必要がありますか?どこかで忘れたらどうしますか?

Mysqlモジュールのexports部分を書き直して接続だけを返すようにするには、毎回getConnection()を書く必要はありませんか?

65
kasztelan

それは良いアプローチです。

接続を取得するだけの場合は、プールがあるモジュールに次のコードを追加します。

var getConnection = function(callback) {
    pool.getConnection(function(err, connection) {
        callback(err, connection);
    });
};

module.exports = getConnection;

それでも、毎回getConnectionを記述する必要があります。ただし、最初に取得したときにモジュールに接続を保存できます。

接続の使用が終了したら、忘れずに接続を終了してください。

connection.release();
50
Klaasvaak

このラッパーは便利です:)

var pool = mysql.createPool(config.db);

exports.connection = {
    query: function () {
        var queryArgs = Array.prototype.slice.call(arguments),
            events = [],
            eventNameIndex = {};

        pool.getConnection(function (err, conn) {
            if (err) {
                if (eventNameIndex.error) {
                    eventNameIndex.error();
                }
            }
            if (conn) { 
                var q = conn.query.apply(conn, queryArgs);
                q.on('end', function () {
                    conn.release();
                });

                events.forEach(function (args) {
                    q.on.apply(q, args);
                });
            }
        });

        return {
            on: function (eventName, callback) {
                events.Push(Array.prototype.slice.call(arguments));
                eventNameIndex[eventName] = callback;
                return this;
            }
        };
    }
};

それを要求し、次のように使用します:

db.connection.query("SELECT * FROM `table` WHERE `id` = ? ", row_id)
          .on('result', function (row) {
            setData(row);
          })
          .on('error', function (err) {
            callback({error: true, err: err});
          });
13
Felipe Jimenez

私はこの基本クラス接続をmysqlで使用しています:

「base.js」

var mysql   = require("mysql");

var pool = mysql.createPool({
    connectionLimit : 10,
    Host: Config.appSettings().database.Host,
    user: Config.appSettings().database.username,
    password: Config.appSettings().database.password,
    database: Config.appSettings().database.database
});


var DB = (function () {

    function _query(query, params, callback) {
        pool.getConnection(function (err, connection) {
            if (err) {
                connection.release();
                callback(null, err);
                throw err;
            }

            connection.query(query, params, function (err, rows) {
                connection.release();
                if (!err) {
                    callback(rows);
                }
                else {
                    callback(null, err);
                }

            });

            connection.on('error', function (err) {
                connection.release();
                callback(null, err);
                throw err;
            });
        });
    };

    return {
        query: _query
    };
})();

module.exports = DB;

次のように使用します。

var DB = require('../dal/base.js');

DB.query("select * from tasks", null, function (data, error) {
   callback(data, error);
});
7
Sagi Tsofan

可能であれば、pool.getConnection()の使用は避けてください。 pool.getConnection()を呼び出す場合、接続の使用が終了したらmustconnection.release()を呼び出します。そうしないと、接続制限に達すると、接続がプールに返されるまでアプリケーションが永久に待機し続けます。

単純なクエリには、pool.query()を使用できます。この省略表現は、エラー状態であってもconnection.release()を自動的に呼び出します。

function doSomething(cb) {
  pool.query('SELECT 2*2 "value"', (ex, rows) => {
    if (ex) {
      cb(ex);
    } else {
      cb(null, rows[0].value);
    }
  });
}

ただし、場合によってはpool.getConnection()を使用する必要があります。これらのケースは次のとおりです。

  1. トランザクション内で複数のクエリを作成します。
  2. 後続のクエリ間で一時テーブルなどのデータオブジェクトを共有します。

pool.getConnection()を使用する必要がある場合は、以下のようなパターンを使用してconnection.release()を呼び出してください。

function doSomething(cb) {
  pool.getConnection((ex, connection) => {
    if (ex) {
      cb(ex);
    } else {
      // Ensure that any call to cb releases the connection
      // by wrapping it.
      cb = (cb => {
        return function () {
          connection.release();
          cb.apply(this, arguments);
        };
      })(cb);
      connection.beginTransaction(ex => {
        if (ex) {
          cb(ex);
        } else {
          connection.query('INSERT INTO table1 ("value") VALUES (\'my value\');', ex => {
            if (ex) {
              cb(ex);
            } else {
              connection.query('INSERT INTO table2 ("value") VALUES (\'my other value\')', ex => {
                if (ex) {
                  cb(ex);
                } else {
                  connection.commit(ex => {
                    cb(ex);
                  });
                }
              });
            }
          });
        }
      });
    }
  });
}

個人的にはPromisesとuseAsync()パターンを使用することを好みます。このパターンとasync/awaitを組み合わせると、字句スコープがrelease()の自動呼び出しに変わるため、.release()接続を誤って忘れることが非常に難しくなります。

async function usePooledConnectionAsync(actionAsync) {
  const connection = await new Promise((resolve, reject) => {
    pool.getConnection((ex, connection) => {
      if (ex) {
        reject(ex);
      } else {
        resolve(connection);
      }
    });
  });
  try {
    return await actionAsync(connection);
  } finally {
    connection.release();
  }
}

async function doSomethingElse() {
  // Usage example:
  const result = await usePooledConnectionAsync(async connection => {
    const rows = await new Promise((resolve, reject) => {
      connection.query('SELECT 2*4 "value"', (ex, rows) => {
        if (ex) {
          reject(ex);
        } else {
          resolve(rows);
        }
      });
    });
    return rows[0].value;
  });
  console.log(`result=${result}`);
}
7
binki

接続が完了したら、connection.release()を呼び出すだけで、接続はプールに戻り、他の人が再び使用できるようになります。

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

pool.getConnection(function(err, connection) {
  // Use the connection
  connection.query('SELECT something FROM sometable', function (error, results, fields) {
    // And done with the connection.
    connection.release();

    // Handle error after the release.
    if (error) throw error;

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

接続を閉じてプールから削除する場合は、代わりにconnection.destroy()を使用します。プールは、次に接続が必要になったときに新しい接続を作成します。

ソースhttps://github.com/mysqljs/mysql

1

標準のmysql.createPool()を使用して、接続はプールによって遅延的に作成されます。最大100の接続を許可するようにプールを構成し、同時に5つしか使用しない場合、5つの接続のみが作成されます。ただし、500個の接続を設定し、500個すべてを使用する場合、アイドル状態であっても、プロセス中は開いたままになります!

これは、MySQLサーバーのmax_connectionsが510の場合、MySQLサーバーがそれらを閉じる(wait_timeoutの設定に依存する)か、アプリケーションが閉じるまで、システムで使用できるmySQL接続は10のみです。それらを解放する唯一の方法は、プールインスタンスを介して接続を手動で閉じるか、プールを閉じることです。

mysql-connection-pool-managerモジュールは、この問題を修正し、負荷に応じて接続数を自動的にスケーリングするために作成されました。アクティビティがなかった場合、非アクティブな接続は閉じられ、アイドル接続プールは最終的に閉じられます。

    // Load modules
const PoolManager = require('mysql-connection-pool-manager');

// Options
const options = {
  ...example settings
}

// Initialising the instance
const mySQL = PoolManager(options);

// Accessing mySQL directly
var connection = mySQL.raw.createConnection({
  Host     : 'localhost',
  user     : 'me',
  password : 'secret',
  database : 'my_db'
});

// Initialising connection
connection.connect();

// Performing query
connection.query('SELECT 1 + 1 AS solution', function (error, results, fields) {
  if (error) throw error;
  console.log('The solution is: ', results[0].solution);
});

// Ending connection
connection.end();

参照: https://www.npmjs.com/package/mysql-connection-pool-manager

0
Yordan