Estoy tratando de averiguar cómo estructurar mi aplicación para usar MySQL de la manera más eficiente. Estoy usando el módulo node-mysql. Otros hilos aquí sugirieron usar la agrupación de conexiones, así que configuré un pequeño módulo mysql.js
var mysql = require('mysql');
var pool = mysql.createPool({
host : 'localhost',
user : 'root',
password : 'root',
database : 'guess'
});
exports.pool = pool;
Ahora, cada vez que quiero consultar mysql, necesito este módulo y luego consultar la base de datos
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);
})
})
}
¿Es este un buen enfoque? Realmente no pude encontrar demasiados ejemplos de uso de conexiones mysql además de uno muy simple donde todo se hace en el script principal de app.js, así que realmente no sé cuáles son las convenciones / mejores prácticas.
¿Debo usar siempre connection.end () después de cada consulta? ¿Qué pasa si lo olvido en alguna parte?
¿Cómo reescribir la parte de exportaciones de mi módulo mysql para devolver solo una conexión para no tener que escribir getConnection () cada vez?
connection.query
todo el lugar en mi código", probablemente sea el momento de refactorizar. Construir una clase de abstracción de base de datos que ofreceselect
,insert
,update
, etc - y sólo utilizanconnection
(opool
) dentro de esa clase db sola ...Respuestas:
Es un buen enfoque.
Si solo desea obtener una conexión, agregue el siguiente código a su módulo donde se encuentra el grupo:
var getConnection = function(callback) { pool.getConnection(function(err, connection) { callback(err, connection); }); }; module.exports = getConnection;
Todavía tienes que escribir getConnection cada vez. Pero puede guardar la conexión en el módulo la primera vez que la obtenga.
No olvide finalizar la conexión cuando haya terminado de usarla:
connection.release();
fuente
connection.release();
ahora, para piscinas.pool.query()
directamente. Este es un atajo para elpool.getConnection()
->connection.query()
->connection.release()
flujo de código.Debe evitar usarlo
pool.getConnection()
si puede. Si llamapool.getConnection()
, debe llamarconnection.release()
cuando haya terminado de usar la conexión. De lo contrario, su aplicación se quedará atascada esperando para siempre que las conexiones regresen al grupo una vez que alcance el límite de conexiones.Para consultas sencillas, puede utilizar
pool.query()
. Esta abreviatura lo llamará automáticamenteconnection.release()
, incluso en condiciones de error.function doSomething(cb) { pool.query('SELECT 2*2 "value"', (ex, rows) => { if (ex) { cb(ex); } else { cb(null, rows[0].value); } }); }
Sin embargo, en algunos casos debe utilizar
pool.getConnection()
. Estos casos incluyen:Si debe usar
pool.getConnection()
, asegúrese de llamarconnection.release()
usando un patrón similar al siguiente: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); }); } }); } }); } }); } }); }
Personalmente prefiero usar
Promise
sy eluseAsync()
patrón. Este patrón combinado conasync
/await
hace que sea mucho más difícil olvidar accidentalmenterelease()
la conexión porque convierte su alcance léxico en una llamada automática a.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}`); }
fuente
usePooledConnectionAsync()
varias veces antes de completar la primera. Tenga en cuenta que, con la agrupación, querrá asegurarse de evitarawait
eventos distintos a la finalización de la consulta dentro de la función que pasa; de loactionAsync
contrario, podría terminar creando un interbloqueo (por ejemplo, obtener la última conexión de una agrupación y luego llamar otra función que intenta cargar datos usando el grupo que esperará una eternidad para intentar obtener su propia conexión del grupo cuando esté vacío).await
una antes de pedir la siguiente. No he hecho ningún análisis ahora, pero por la forma en que he escrito las cosas (devolviendo nuevas promesas), creo que todavía se está ejecutando en paralelo ...await
una forma de hacerlo es lanzar un montón de cargas independientes y luego solo usarlas cuando realmente las necesite para componer los resultados juntos. (aunque tengo miedo de que eso resulte en eventos de rechazo de promesas no controladas de falso positivo que podrían bloquear node.js en el futuro--unhandled-rejections=strict
).Encontrará esta envoltura ú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; } }; } };
Requerirlo, úselo así:
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}); });
fuente
Estoy usando esta conexión de clase base con 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;
Úselo así:
var DB = require('../dal/base.js'); DB.query("select * from tasks", null, function (data, error) { callback(data, error); });
fuente
err
es verdadera? ¿No debería seguir llamandocallback
con elnull
parámetro para indicar que hay algún error en la consulta?else
condición como esta: de loif (!err) { callback(rows, err); } else { callback(null, err); }
contrario, su aplicación podría bloquearse. Porqueconnection.on('error', callback2)
no se ocupará de todos los "errores". ¡Gracias!Cuando haya terminado con una conexión, simplemente llame
connection.release()
y la conexión volverá a la piscina, lista para ser utilizada nuevamente por otra persona.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. }); });
Si desea cerrar la conexión y eliminarla del grupo, utilice
connection.destroy()
en su lugar. El grupo creará una nueva conexión la próxima vez que se necesite una.Fuente : https://github.com/mysqljs/mysql
fuente
Usando el estándar mysql.createPool (), el grupo crea conexiones de manera perezosa. Si configura el grupo para permitir hasta 100 conexiones, pero solo usa 5 simultáneamente, solo se realizarán 5 conexiones. Sin embargo, si lo configura para 500 conexiones y usa las 500, permanecerán abiertas durante la duración del proceso, ¡incluso si están inactivas!
Esto significa que si su servidor MySQL max_connections es 510, su sistema solo tendrá 10 conexiones mySQL disponibles hasta que su servidor MySQL las cierre (depende de lo que haya configurado su wait_timeout) o su aplicación se cierre. La única forma de liberarlos es cerrar manualmente las conexiones a través de la instancia del grupo o cerrar el grupo.
El módulo mysql-connection-pool-manager se creó para solucionar este problema y escalar automáticamente el número de conexiones dependiendo de la carga. Las conexiones inactivas se cierran y los grupos de conexiones inactivos se cierran eventualmente si no ha habido ninguna actividad.
// 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
fuente
yo siempre uso connection.relase (); después de 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); } });
fuente