नोड .js + mysql कनेक्शन पूलिंग


85

मैं यह पता लगाने की कोशिश कर रहा हूं कि MySQL का सबसे प्रभावशाली तरीके से उपयोग करने के लिए मेरे एप्लिकेशन को कैसे तैयार किया जाए। मैं नोड- 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 स्क्रिप्ट में किया जाता है, इसलिए मैं वास्तव में नहीं जानता कि सम्मेलन / सर्वोत्तम प्रथाओं क्या हैं।

क्या मुझे हमेशा प्रत्येक क्वेरी के बाद कनेक्शन.एंड () का उपयोग करना चाहिए? अगर मैं इसके बारे में कहीं भूल जाता हूं तो क्या होगा?

कैसे एक कनेक्शन वापस करने के लिए mysql मॉड्यूल के निर्यात भाग को फिर से लिखना है ताकि मुझे हर बार getConnection () लिखना न पड़े?


2
उन लोगों के लिए जो यह पाते हैं और सोचते हैं "मेरे पास connection.queryमेरे कोड में सभी जगह हैं" - यह शायद रिफ्लेक्टर का समय है। एक डेटाबेस अमूर्त वर्ग है कि प्रस्तावों का निर्माण select, insert, update, आदि - और केवल का उपयोग connection(या pool) है कि एकल डाटाबेस वर्ग के भीतर ...
random_user_name

@random_user_name क्या आपके पास कोई लिंक या कोड है जो आपके सुझाव को लागू करता है?
KingAndrew

@random_user_name आप इस मामले में लेनदेन कैसे प्रबंधित करेंगे? यदि आप प्रत्येक प्रश्न के बाद कनेक्शन जारी करते हैं?
जेफ रयान

@JeffRyan आपके पास अन्य वर्ग हो सकते हैं जो इस db वर्ग का विस्तार करते हैं जिसमें आप विशेष मामलों का प्रबंधन करते हैं जिन्हें असाधारण लेनदेन की आवश्यकता होती है। लेकिन मुझे लगता है कि random_user_name का सुझाव लेन-देन के खिलाफ जरूरी नहीं है ... मैं आमतौर पर एक समान पैटर्न का उपयोग करता हूं, जिसमें मैं एक आधार मॉडल वर्ग बनाता हूं जो मूल तरीके प्रदान करता है, और उदाहरण के लिए सम्मिलन विधि में लेनदेन की आवश्यकता होती है, क्योंकि यह पहली बार एक रिकॉर्ड सम्मिलित करता है और फिर परिणाम प्राप्त करने के लिए अंतिम सम्मिलित आईडी द्वारा चयन करता है।
लुइसरेटा

जवाबों:


68

यह एक अच्छा तरीका है।

यदि आप एक कनेक्शन प्राप्त करना चाहते हैं तो अपने मॉड्यूल में निम्नलिखित कोड जोड़ें जहां पूल है:

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

module.exports = getConnection;

आपको अभी भी हर बार getConnection लिखना है। लेकिन आप इसे प्राप्त करने के लिए पहली बार मॉड्यूल में कनेक्शन को बचा सकते हैं।

जब आप इसका उपयोग कर रहे हों तो कनेक्शन समाप्त करना न भूलें:

connection.release();

18
बिलकुल चौकन्ना। यह connection.release();अब है, पूल के लिए।
sdanzig

यह सच है। मैंने इसे बदल दिया।
कालसर्वक

इसके अलावा, अगर मैं हो सकता है, मैं कॉलबैक के बजाय एक वादा का उपयोग कर सुझाव है, लेकिन वह फिर भी सिर्फ एक वरीयता ... महान समाधान है
स्पॉक

@ क्या आप इसका एक उदाहरण लिंक कर सकते हैं? एक्सप्रेस के वादे अब तक साथ काम करने के लिए परेशान करने वाले हैं, मुझे लगता है कि मुझे कुछ याद आ रहा है। अब तक मैं केवल var deferred = q.defer () का उपयोग कर सकता हूं और फिर हल या अस्वीकार कर सकता हूं, लेकिन ऐसा लगता है कि बहुत सरल चीज़ के लिए बहुत अधिक ओवरहेड। यदि हां, धन्यवाद :)
PixMach

1
आप pool.query()सीधे भी उपयोग कर सकते हैं। यह pool.getConnection()-> connection.query()-> connection.release()कोड प्रवाह के लिए एक शॉर्टकट है ।
गल शबुदी

27

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/ के साथ संयुक्त होता है क्योंकि यह आपके लेक्सिकल स्कूपिंग को स्वचालित कॉल में बदल देता है :awaitrelease().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}`);
}

+1 - बस एक नोट - हर प्रश्न की प्रतीक्षा में उदाहरणों में समझ में नहीं आ सकता है जहां आप कई प्रश्न चला रहे हैं जो अभ्यास में क्रमिक रूप से एक साथ चलाया जा सकता है।
random_user_name

@ cale_b जब तक आप कुछ अजीब तरह से जादुई कर रहे हैं, समानांतर में इन प्रश्नों को चलाना असंभव है। यदि आप डेटा निर्भरता वाले किसी लेन-देन में कई क्वेरीज़ चला रहे हैं, तो आप दूसरी क्वेरी तब तक नहीं चला सकते हैं जब तक आप निश्चित नहीं कर लेते हैं कि पहले एक पूरा हो चुका है। यदि आपके प्रश्न लेन-देन साझा कर रहे हैं, जैसा कि प्रदर्शित किया गया है, तो वे एक कनेक्शन भी साझा कर रहे हैं। प्रत्येक कनेक्शन एक समय में केवल एक क्वेरी का समर्थन करता है ( MySQL में MARS जैसी कोई चीज नहीं है )।
21

1
यदि आप वास्तव में डेटाबेस में कई स्वतंत्र संचालन कर रहे हैं, तो कुछ भी usePooledConnectionAsync()पहले वाले को पूरा करने से पहले आपको कई बार कॉल करने से रोक रहा है । ध्यान दें कि, पूलिंग के साथ, आप यह सुनिश्चित करना चाहेंगे कि आप awaitक्वेरी के अलावा अन्य आयोजनों को पूरा करने से बचें, जो कि आप के रूप में पास करते हैं actionAsync-उत्पाद, आप एक गतिरोध पैदा कर सकते हैं (जैसे, एक पूल से अंतिम कनेक्शन प्राप्त करें, फिर कॉल करें एक और फ़ंक्शन जो पूल का उपयोग करके डेटा लोड करने की कोशिश करता है जो हमेशा खाली रहने पर पूल से अपना कनेक्शन लेने की कोशिश करने के लिए इंतजार करेगा)।
बिंकी

1
उलझाने के लिए धन्यवाद। यह एक क्षेत्र हो सकता है मेरी समझ कमजोर है - लेकिन, पहले (पूल में स्विच करने से पहले, मुख्य रूप से आपके उत्तर का उपयोग करते हुए, BTW) मेरे पास "समानांतर" में चलने वाले कई चयन थे (और फिर मैं वापस आने के बाद अपने js तर्क में परिणाम मिलाता हूं। )। मुझे नहीं लगता कि यह जादुई है, लेकिन यह awaitअगले के लिए पूछने से पहले एक नहीं करने के लिए एक अच्छी रणनीति की तरह लग रहा था । मैंने अब कोई विश्लेषण नहीं किया है, लेकिन जिस तरह से मैंने चीजों को लेखक किया है (नए वादे वापस कर रहा है), मुझे लगता है कि यह अभी भी समानांतर में चल रहा है ...
random_user_name

@ cale_b राइट, मैं यह नहीं कह रहा हूं कि पैटर्न खराब है। यदि आपको डेटा के कई टुकड़ों को लोड करने की आवश्यकता है और यह माना जा सकता है कि वे स्वतंत्र हैं या पर्याप्त रूप से अपरिवर्तित हैं, तो स्वतंत्र भार का एक गुच्छा लॉन्च करना और फिर awaitउन्हें केवल तभी लेना है जब आपको वास्तव में उन्हें एक साथ परिणाम की रचना करने की आवश्यकता होती है। (हालांकि मुझे डर है कि इससे झूठे सकारात्मक अनहोनी के वादे अस्वीकृति की घटनाएँ घटेंगी जो भविष्य में नोड.जेएस को दुर्घटनाग्रस्त कर सकती हैं --unhandled-rejections=strict)।
binki

14

आप इस आवरण उपयोगी मिल जाएगा :)

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});
          });

10

मैं 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);
});

1
यदि क्वेरी errसत्य है तो क्या होगा ? क्या यह अभी भी पैरामीटर के callbackसाथ कॉल nullकरने के लिए इंगित नहीं करना चाहिए कि क्वेरी में कुछ त्रुटि है?
जो हुआंग

हां, आप लिखते हैं, क्वेरी त्रुटि के साथ कॉलबैक बबल करने की आवश्यकता है
सागी सूफान

अच्छा है। लेकिन आपको elseइस तरह की एक शर्त जोड़नी चाहिए : if (!err) { callback(rows, err); } else { callback(null, err); }अन्यथा आपका आवेदन लटका रह सकता है। क्योंकि connection.on('error', callback2)सभी "त्रुटियों" का ध्यान नहीं रखा जाएगा। धन्यवाद!
तद्ज

वास्तव में, मैंने यह ठीक कर दिया
सगी त्सोफन

यहां न्यूजिज: आपके पास फ़ंक्शन (डेटा, त्रुटि) और कॉलबैक (डेटा, त्रुटि) क्यों है; जब मैंने देखा है सभी नोडज कोड में से पहला परम के रूप में त्रुटि है और दूसरा पैराम के रूप में डेटा / कॉलबैक है? ex: कॉलबैक (त्रुटि, परिणाम)
KingAndrew

2

जब आप एक कनेक्शन के साथ किया जाता है, तो बस कॉल करें 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


0

मानक mysql.createPool () का उपयोग करते हुए, कनेक्शन पूल द्वारा आलसी रूप से बनाए जाते हैं। यदि आप 100 कनेक्शन तक अनुमति देने के लिए पूल को कॉन्फ़िगर करते हैं, लेकिन कभी भी 5 का उपयोग एक साथ करते हैं, तो केवल 5 कनेक्शन किए जाएंगे। हालाँकि यदि आप इसे 500 कनेक्शनों के लिए कॉन्फ़िगर करते हैं और सभी 500 का उपयोग करते हैं, तो वे प्रक्रिया की अवधि के लिए खुले रहेंगे, भले ही वे निष्क्रिय हों!

इसका मतलब है कि यदि आपका MySQL Server मैक्स_कनेक्ट्स 510 है तो आपके सिस्टम में केवल 10 MySQL कनेक्शन उपलब्ध होंगे, जब तक कि आपका MySQL सर्वर उन्हें बंद नहीं करता (निर्भर करता है कि आपने अपना Wait_timeout क्या सेट किया है) या आपका एप्लिकेशन बंद हो जाता है! उन्हें मुक्त करने का एकमात्र तरीका पूल उदाहरण के माध्यम से कनेक्शन को मैन्युअल रूप से बंद करना या पूल को बंद करना है।

mysql-कनेक्शन-पूल-प्रबंधक मॉड्यूल इस समस्या को ठीक करने के लिए बनाया गया था और स्वचालित रूप से लोड पर निर्भर कनेक्शन की संख्या को मापता है। निष्क्रिय कनेक्शन बंद हैं और निष्क्रिय कनेक्शन पूल अंततः बंद हो गए हैं यदि कोई गतिविधि नहीं हुई है।

    // 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


-5

मैं हमेशा connection.relase () का उपयोग करता हूं; जैसे पूल के बाद

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);
        }
    });

7
लगता है कि आप कनेक्शन का
विमोचन

1
जी हां, यह बुरी खबर है .... इसका असिक स्वभाव का एक साइड इफेक्ट है कि आप इस रिलीज के साथ दूर हो रहे हैं। यदि आप कुछ विलंबता का परिचय देते हैं तो आपको वह क्वेरी दिखाई नहीं देगी। पैटर्न है ... pool.getConnection (फ़ंक्शन (ग़लती, कनेक्शन) {// कनेक्शन कनेक्शन का उपयोग करें। ('कुछ से कुछ चुनें', फ़ंक्शन (त्रुटि, परिणाम, फ़ील्ड) {// और कनेक्शन के साथ किया। कनेक्शन .release
hpavc 20
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.