Estou tentando descobrir como estruturar meu aplicativo para usar o MySQL da maneira mais eficiente. Estou usando o módulo node-mysql. Outros tópicos aqui sugeriram o uso de pool de conexão, então eu configurei um pequeno módulo mysql.js
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'localhost',
user : 'root',
password : 'root',
database : 'guess'
});
exports.pool = pool;
Agora, sempre que eu quiser consultar o mysql, eu exijo este módulo e, em seguida, consulte o banco de dados
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);
})
})
}
Essa é uma boa abordagem? Eu realmente não consegui encontrar muitos exemplos de uso de conexões mysql além de um muito simples onde tudo é feito no script app.js principal, então eu realmente não sei quais são as convenções / melhores práticas.
Devo sempre usar connection.end () após cada consulta? E se eu esquecer em algum lugar?
Como reescrever a parte de exportações do meu módulo mysql para retornar apenas uma conexão, então eu não tenho que escrever getConnection () todas as vezes?
connection.query
tudo em meu código" - provavelmente é hora de refatorar. Criar uma classe de abstração de banco de dados que ofereceselect
,insert
,update
, etc - e só usarconnection
(oupool
) dentro dessa única classe db ...Respostas:
É uma boa abordagem.
Se você deseja apenas obter uma conexão, adicione o seguinte código ao seu módulo onde o pool está:
var getConnection = function(callback) { pool.getConnection(function(err, connection) { callback(err, connection); }); }; module.exports = getConnection;
Você ainda precisa escrever getConnection todas as vezes. Mas você pode salvar a conexão no módulo na primeira vez que a obtiver.
Não se esqueça de encerrar a conexão quando terminar de usá-la:
connection.release();
fonte
connection.release();
agora, para piscinas.pool.query()
diretamente. Este é um atalho para o fluxo de códigopool.getConnection()
->connection.query()
->connection.release()
.Você deve evitar o uso,
pool.getConnection()
se puder. Se você ligarpool.getConnection()
, deverá ligarconnection.release()
quando terminar de usar a conexão. Caso contrário, seu aplicativo ficará paralisado esperando para sempre que as conexões sejam retornadas ao pool assim que você atingir o limite de conexão.Para consultas simples, você pode usar
pool.query()
. Esta abreviação irá chamáconnection.release()
-lo automaticamente - mesmo em condições de erro.function doSomething(cb) { pool.query('SELECT 2*2 "value"', (ex, rows) => { if (ex) { cb(ex); } else { cb(null, rows[0].value); } }); }
No entanto, em alguns casos, você deve usar
pool.getConnection()
. Esses casos incluem:Se for necessário usar
pool.getConnection()
, certifique-se de chamarconnection.release()
usando um padrão semelhante ao abaixo: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); }); } }); } }); } }); } }); }
Eu pessoalmente prefiro usar se
Promise
ouseAsync()
padrão. Esse padrão combinado comasync
/await
torna muito mais difícil esquecer acidentalmenterelease()
a conexão porque transforma seu escopo léxico em uma chamada automática para.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}`); }
fonte
usePooledConnectionAsync()
várias chamadas antes de concluir a primeira. Observe que, com o pooling, você vai querer ter certeza de evitarawait
eventos além da conclusão da consulta dentro da função que você passou - casoactionAsync
contrário, você pode acabar criando um deadlock (por exemplo, obter a última conexão de um pool e chamar outra função que tenta carregar dados usando o pool que vai esperar uma eternidade para tentar obter sua própria conexão do pool quando vazio).await
uma antes de pedir a próxima. Eu não fiz nenhuma análise agora, mas do jeito que eu criei coisas (retornando novas promessas), acho que ainda está funcionando em paralelo ...await
carregá-los quando você realmente precisar deles para compor os resultados juntos é uma maneira de fazer isso (embora eu esteja com medo de que isso resulte em eventos de rejeição de promessa falsos positivos não tratados, que podem travar o node.js no futuro--unhandled-rejections=strict
).Você achará este wrapper útil :)
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; } }; } };
Exigir, use assim:
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}); });
fonte
Estou usando esta conexão de classe base com 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;
Basta usá-lo assim:
var DB = require('../dal/base.js'); DB.query("select * from tasks", null, function (data, error) { callback(data, error); });
fonte
err
for verdadeira? ainda não deveria chamarcallback
comnull
parâmetro para indicar que há algum erro na consulta?else
condição como esta: casoif (!err) { callback(rows, err); } else { callback(null, err); }
contrário, seu aplicativo pode travar. Porqueconnection.on('error', callback2)
não vai cuidar de todos os "erros". Obrigado!Ao terminar a conexão, basta ligar
connection.release()
e a conexão retornará ao pool, pronta para ser utilizada novamente por outra pessoa.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. }); });
Se quiser fechar a conexão e removê-la do pool, use
connection.destroy()
. O pool criará uma nova conexão na próxima vez que uma for necessária.Fonte : https://github.com/mysqljs/mysql
fonte
Usando o mysql.createPool () padrão, as conexões são criadas lentamente pelo pool. Se você configurar o pool para permitir até 100 conexões, mas sempre usar 5 simultaneamente, apenas 5 conexões serão feitas. No entanto, se você configurá-lo para 500 conexões e usar todas as 500, elas permanecerão abertas durante a duração do processo, mesmo se estiverem ociosas!
Isso significa que se seu servidor MySQL max_connections for 510, seu sistema terá apenas 10 conexões mySQL disponíveis até que seu MySQL Server as feche (depende de como você configurou o wait_timeout) ou seu aplicativo feche! A única maneira de liberá-los é fechar manualmente as conexões por meio da instância do pool ou fechar o pool.
O módulo mysql-connection-pool-manager foi criado para corrigir esse problema e escalar automaticamente o número de conexões dependentes da carga. As conexões inativas são fechadas e os pools de conexão inativos são eventualmente fechados se não houver nenhuma atividade.
// 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();
Ref: https://www.npmjs.com/package/mysql-connection-pool-manager
fonte
eu sempre uso connection.relase (); após pool.getconnetion como
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); } });
fonte