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);
})
})
}
Это хороший подход? Я не мог найти слишком много примеров использования соединений mysql, кроме очень простой, где все сделано в основном app.js script, поэтому я действительно не знаю, что такое соглашение/лучшие практики.
Должен ли я всегда использовать connection.end() после каждого запроса? Что, если я забуду об этом где-нибудь?
Как переписать часть экспорта моего модуля mysql, чтобы вернуть только соединение, поэтому мне не нужно каждый раз писать getConnection()?
Ответы
Ответ 1
Это хороший подход.
Если вы просто хотите получить соединение, добавьте следующий код в свой модуль, в котором находится пул:
var getConnection = function(callback) {
pool.getConnection(function(err, connection) {
callback(err, connection);
});
};
module.exports = getConnection;
Вам все равно приходится писать getConnection каждый раз. Но вы можете сохранить соединение в модуле при первом его получении.
Не забудьте завершить соединение, когда вы закончите с ним:
connection.release();
Ответ 2
Вы найдете эту оболочку полезной:)
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});
});
Ответ 3
Я использую это соединение базового класса с 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);
});
Ответ 4
Вы должны избегать использования pool.getConnection()
если можете. Если вы вызываете pool.getConnection()
, вы должны вызвать connection.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()
. Эти случаи включают в себя:
- Создание нескольких запросов в рамках транзакции.
- Совместное использование объектов данных, таких как временные таблицы, между последующими запросами.
Если вы должны использовать 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);
});
}
});
}
});
}
});
}
});
}
Лично я предпочитаю использовать Promise
и 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}');
}
Ответ 5
Когда вы закончите соединение, просто вызовите 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
Ответ 6
Используя стандартный mysql.createPool(), соединения лениво создаются пулом. Если вы сконфигурируете пул, чтобы разрешить до 100 подключений, но только когда-либо используете 5 одновременно, будет сделано только 5 подключений. Однако, если вы сконфигурируете его для 500 подключений и используете все 500, они останутся открытыми на время процесса, даже если они простаивают!
Это означает, что если ваш MySQL Server max_connections равен 510, ваша система будет иметь только 10 доступных подключений mySQL, пока ваш MySQL сервер не закроет их (зависит от того, на что вы установили wait_timeout) или ваше приложение закроется! Единственный способ освободить их - это вручную закрыть соединения через экземпляр пула или закрыть пул.
Модуль 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
Ответ 7
i всегда использую connection.relase(); после pool.getconnetion like
pool.getConnection(function (err, connection) {
connection.release();
if (!err)
{
console.log('*** Mysql Connection established with ', config.database, ' and connected as id ' + connection.threadId);
//CHECKING USERNAME EXISTENCE
email = receivedValues.email
connection.query('SELECT * FROM users WHERE email = ?', [email],
function (err, rows) {
if (!err)
{
if (rows.length == 1)
{
if (bcrypt.compareSync(req.body.password, rows[0].password))
{
var alldata = rows;
var userid = rows[0].id;
var tokendata = (receivedValues, userid);
var token = jwt.sign(receivedValues, config.secret, {
expiresIn: 1440 * 60 * 30 // expires in 1440 minutes
});
console.log("*** Authorised User");
res.json({
"code": 200,
"status": "Success",
"token": token,
"userData": alldata,
"message": "Authorised User!"
});
logger.info('url=', URL.url, 'Responce=', 'User Signin, username', req.body.email, 'User Id=', rows[0].id);
return;
}
else
{
console.log("*** Redirecting: Unauthorised User");
res.json({"code": 200, "status": "Fail", "message": "Unauthorised User!"});
logger.error('*** Redirecting: Unauthorised User');
return;
}
}
else
{
console.error("*** Redirecting: No User found with provided name");
res.json({
"code": 200,
"status": "Error",
"message": "No User found with provided name"
});
logger.error('url=', URL.url, 'No User found with provided name');
return;
}
}
else
{
console.log("*** Redirecting: Error for selecting user");
res.json({"code": 200, "status": "Error", "message": "Error for selecting user"});
logger.error('url=', URL.url, 'Error for selecting user', req.body.email);
return;
}
});
connection.on('error', function (err) {
console.log('*** Redirecting: Error Creating User...');
res.json({"code": 200, "status": "Error", "message": "Error Checking Username Duplicate"});
return;
});
}
else
{
Errors.Connection_Error(res);
}
});