วิธีที่เหมาะสมในการใช้โมดูล node.js postgresql คืออะไร?


95

ฉันจะเขียนแอปบน Node.js Heroku และการใช้โมดูลหน้า ฉันไม่สามารถหาวิธีที่ "ถูกต้อง" ในการรับวัตถุไคลเอ็นต์สำหรับแต่ละคำขอที่ฉันต้องการค้นหาฐานข้อมูล

เอกสารประกอบใช้รหัสดังนี้:

pg.connect(conString, function(err, client) {
  // Use the client to do things here
});

แต่แน่นอนว่าคุณไม่จำเป็นต้องเรียกใช้pg.connectทุกฟังก์ชันที่ใช้ฐานข้อมูลใช่ไหม ฉันเคยเห็นรหัสอื่นที่ทำสิ่งนี้:

var conString = process.env.DATABASE_URL || "tcp://postgres:1234@localhost/postgres";
var client = new pg.Client(conString);
client.connect();
// client is a global so you can use it anywhere now

ฉันเอนเอียงไปยังตัวเลือกที่สองเนื่องจากฉันเชื่อว่าอินสแตนซ์ฐานข้อมูลฟรีสำหรับ Heroku นั้น จำกัด การเชื่อมต่อเพียงครั้งเดียว แต่มีข้อเสียในการทำเช่นนี้หรือไม่? ฉันจำเป็นต้องตรวจสอบว่าวัตถุไคลเอนต์ของฉันยังคงเชื่อมต่ออยู่ทุกครั้งก่อนที่จะใช้งานหรือไม่?

คำตอบ:


158

ฉันเขียนของโหนด postgres ก่อนอื่นฉันต้องขออภัยที่เอกสารประกอบไม่สามารถทำให้ตัวเลือกที่ถูกต้องชัดเจนได้นั่นคือความผิดของฉัน ฉันจะพยายามปรับปรุงให้ดีขึ้น ตอนนี้ฉันเขียนGistเพื่ออธิบายเรื่องนี้เนื่องจากการสนทนายาวเกินไปสำหรับ Twitter

การใช้pg.connectเป็นวิธีที่จะไปในสภาพแวดล้อมเว็บ

เซิร์ฟเวอร์ PostgreSQL สามารถจัดการได้ครั้งละ 1 แบบสอบถามต่อการเชื่อมต่อ นั่นหมายความว่าหากคุณมี 1 global ที่new pg.Client()เชื่อมต่อกับแบ็กเอนด์ของคุณทั้งแอปของคุณจะถูกทำให้เป็นขวดโดยขึ้นอยู่กับว่า postgres สามารถตอบคำถามได้รวดเร็วเพียงใด มันจะจัดเรียงทุกอย่างตามลำดับโดยเข้าคิวแต่ละแบบสอบถาม ใช่มันไม่ตรงกันแล้วก็ไม่เป็นไร ... แต่คุณจะไม่คูณปริมาณงานของคุณด้วย 10x หรือไม่? ใช้การpg.connect ตั้งค่าเป็น pg.defaults.poolSizeสิ่งที่มีเหตุผล (เราทำ 25-100 ยังไม่แน่ใจว่าจำนวนที่ถูกต้อง)

new pg.Clientมีไว้สำหรับเมื่อคุณรู้ว่าคุณกำลังทำอะไรอยู่ เมื่อคุณต้องการลูกค้าที่มีอายุยืนยาวด้วยเหตุผลบางประการหรือจำเป็นต้องควบคุมวงจรชีวิตอย่างรอบคอบ LISTEN/NOTIFYเป็นตัวอย่างที่ดีของที่นี่คือเมื่อใช้ ไคลเอนต์ที่รับฟังต้องอยู่ใกล้ ๆ และเชื่อมต่อและไม่แชร์เพื่อให้สามารถจัดการกับNOTIFYข้อความได้อย่างถูกต้อง ตัวอย่างอื่น ๆ คือเมื่อเปิดไคลเอนต์แบบ 1-off เพื่อฆ่าสิ่งที่ค้างหรือในสคริปต์บรรทัดคำสั่ง

สิ่งที่มีประโยชน์มากอย่างหนึ่งคือการรวมการเข้าถึงฐานข้อมูลทั้งหมดในแอปไว้ที่ไฟล์เดียว อย่าทิ้งpg.connectสายหรือลูกค้าใหม่ตลอด มีไฟล์db.jsลักษณะดังนี้:

module.exports = {
   query: function(text, values, cb) {
      pg.connect(function(err, client, done) {
        client.query(text, values, function(err, result) {
          done();
          cb(err, result);
        })
      });
   }
}

ด้วยวิธีนี้คุณสามารถเปลี่ยนการใช้งานของคุณจากpg.connectเป็นกลุ่มลูกค้าที่กำหนดเองหรืออะไรก็ได้และต้องเปลี่ยนสิ่งต่างๆในที่เดียวเท่านั้น

ดูที่โมดูล node-pg-queryที่ทำสิ่งนี้ได้


2
ขอโทษค่ะฉันค่อนข้างใหม่กับ DBMS และยังมีปัญหาในการทำความเข้าใจเรื่องนี้ แต่ทำไมเราไม่ต้องการเรียก "litter pg.connect" ล่ะ เป็นไปเพื่อความเรียบง่ายหรือเนื่องจากเหตุผลด้านประสิทธิภาพ? ตัวอย่างเช่นฉันเรียก pg.connect หนึ่งครั้งในแต่ละเส้นทางที่ฉันมีในแอพพื้นฐานของฉัน (ทั้งหมดที่มี conString เดียวกัน) นี่โอเคไหม? โดยสัญชาตญาณรู้สึกเหมือนกำลังทำการเชื่อมต่อใหม่กับฐานข้อมูลเดียวกันทุกครั้งที่ฉันเรียกมัน (ซึ่งฉันไม่ต้องการ) แต่มันใช้การเชื่อมต่อแบบรวมภายในหรือไม่? ขอบคุณ.
user1164937

น่ากลัว เหตุใดคุณจึงใช้การเชื่อมต่อหนึ่งรายการต่อแบบสอบถามแทนที่จะเป็นการเชื่อมต่อหนึ่งรายการต่อคำขอ ฉันกำลังมองหาวิธีที่เหมาะสมในการแบ่งปันการเชื่อมต่อกับคำถามต่างๆภายในคำขอและได้พิจารณา res.locals ก่อนที่จะพบคำตอบของคุณที่นี่
Joe Lapp

2
เดี๋ยวก่อน ดูเหมือนว่าโซลูชันของคุณที่นี่จะไม่รองรับธุรกรรม
Joe Lapp

1
จะปิดการเชื่อมต่อ pg ได้อย่างไร?
Vardan

1
โปรดทราบว่า pg.connect ถูกลบโพสต์ v7 ของ node-postgres aka pg ดูstackoverflow.com/questions/45174120/pg-connect-not-a-function
Colin D

24

ฉันเป็นผู้เขียนpg-promซึ่งช่วยลดความยุ่งยากในการใช้node-postgresผ่านทางสัญญา

กล่าวถึงปัญหาเกี่ยวกับวิธีที่ถูกต้องในการเชื่อมต่อและยกเลิกการเชื่อมต่อจากฐานข้อมูลโดยใช้พูลการเชื่อมต่อที่ดำเนินการโดยnode-postgresเป็นต้นเช่นธุรกรรมอัตโนมัติ

คำขอแต่ละรายการในpg- Promise จะสรุปเฉพาะสิ่งที่เกี่ยวข้องกับตรรกะทางธุรกิจของคุณ:

db.any('SELECT * FROM users WHERE status = $1', ['active'])
    .then(data => {
        console.log('DATA:', data);
    })
    .catch(error => {
        console.log('ERROR:', error);
    });

กล่าวคือคุณไม่จำเป็นต้องจัดการกับตรรกะการเชื่อมต่อเมื่อดำเนินการสืบค้นเนื่องจากคุณตั้งค่าการเชื่อมต่อเพียงครั้งเดียวทั่วโลกเช่นนี้:

const pgp = require('pg-promise')(/*options*/);

const cn = {
    host: 'localhost', // server name or IP address;
    port: 5432,
    database: 'myDatabase',
    user: 'myUser',
    password: 'myPassword'
};
// alternative:
// const cn = 'postgres://username:password@host:port/database';

const db = pgp(cn); // database instance;

คุณสามารถหาตัวอย่างอื่น ๆ อีกมากมายในการเรียนรู้จากตัวอย่างการกวดวิชาหรือบนหน้าแรกของโครงการ


สวัสดี Heroku ยอมรับเฉพาะการเชื่อมต่อ SSL ในpgนี้ระบุโดยpg.defaults.ssl = true;. คุณทำสิ่งนี้ได้pg-promiseอย่างไร?
ocram

@ocram github.com/vitaly-t/pg-promise/wiki/…หรือคุณสามารถระบุ SSL ภายในพารามิเตอร์การเชื่อมต่อ: github.com/vitaly-t/pg-promise/wiki/Connection-Syntax
vitaly-t

ฉันยังใหม่กับสิ่งนี้มากที่สุด: จาวาสคริปต์สัญญาโปสการ์ด ฯลฯ และนี่คือสิ่งที่ฉันต้องการ ขอบคุณ!!
Ryan Rodemoyer

1
@ocram ฉันเพิ่งทำงานนี้โดยทำpgp.pg.defaults.ssl = true;
CharlieC

สิ่งนี้จะสร้างการเชื่อมต่อหลายรายการเพื่อปรับปรุงทรูพุต postgres โดยอัตโนมัติเมื่อเราส่งคำขอแบบสอบถามหลายรายการไปยัง postgres หรือไม่
อาทิตย์ที่

5

สระว่ายน้ำเป็นทางที่จะไปตอนนี้บางอย่างเป็นแบบนี้

const { Pool } = require('pg');

    const pool = new Pool({
      connectionString: DATABASE_URL,
      ssl: false,
      max: 20,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    });
    module.exports = {
        query: (text, params) => pool.query(text, params)
      }

สามารถใช้เป็น db.query('<BEGIN,COMMIT,ROLLBACK,your query,anything')


1

เป็นการดีกว่าที่จะสร้างพูล pg ทั่วโลกและทุกครั้งที่คุณต้องดำเนินการ db โดยใช้ไคลเอนต์แล้วปล่อยกลับไปที่พูล เมื่อการดำเนินการ db ทั้งหมดเสร็จสิ้นสิ้นสุดพูลโดยใช้pool.end()

โค้ดตัวอย่าง -

let pool = new pg.Pool(dbConfig);
pool.connect(function(err, client, done) {

if (err) {
    console.error('Error connecting to pg server' + err.stack);
    callback(err);
} else {
    console.log('Connection established with pg db server');

    client.query("select * from employee", (err, res) => {

            if (err) {
                console.error('Error executing query on pg db' + err.stack);
                callback(err);
            } else {
                console.log('Got query results : ' + res.rows.length);


               async.each(res.rows, function(empRecord) {   
                        console.log(empRecord.name);
                });
            }
            client.release();

        });
}

});  

สำหรับรายละเอียดเพิ่มเติมคุณสามารถอ้างถึงบล็อกโพสต์ของฉัน - ที่มา


0

ดังที่คุณเห็นจากเอกสารประกอบทั้งสองตัวเลือกนั้นถูกต้องดังนั้นควรเลือกตามที่คุณต้องการ ในฐานะคุณฉันจะเลือกตัวเลือกที่สอง


แล้วการเชื่อมต่อใหม่เมื่อการเชื่อมต่อลดลงล่ะ? เป็นไปโดยอัตโนมัติหรือไม่? หน้าวิกิเกี่ยวกับการจัดการข้อผิดพลาดคือ ... ว่างgithub.com/brianc/node-postgres/wiki/Error-handling
alltom

ฉันถามมันแยกต่างหาก: stackoverflow.com/questions/15619456/…
alltom

-1

ฉันสนใจตัวจัดการที่เรียบง่ายมากสำหรับสิ่งนี้ดังนั้นฉันจึงทำด้วยตัวเองโดยไม่ทำให้มันซับซ้อน ฉันไม่มีภาพลวงตาว่ามันธรรมดาสุด ๆ แต่มันช่วยให้บางคนเริ่มต้นได้ โดยทั่วไปจะเชื่อมต่อเรียกใช้แบบสอบถามและจัดการข้อผิดพลาดให้คุณ

function runQuery(queryString, callback) {
  // connect to postgres database
  pg.connect(postgresDatabase.url,function(err,client,done) {
    // if error, stop here
    if (err) {console.error(err); done(); callback(); return;}
    // execute queryString
    client.query(queryString,function(err,result) {
      // if error, stop here
      if (err) {console.error(err+'\nQuery: '+queryString); done(); callback(); return;}
      // callback to close connection
      done();
      // callback with results
      callback(result.rows);
    });
  });
}

จากนั้นคุณจะใช้โดยเรียกมันด้วยวิธีนี้:

runQuery("SELECT * FROM table", function(result) {
  // Whatever you need to do with 'result'
}

สิ่งนี้ไม่ได้ปล่อยการเชื่อมต่อกลับไปที่สระว่ายน้ำ มันจะหมดลงอย่างรวดเร็วจริง ตัวอย่างพื้นฐานในnode-postgresหน้าทำได้ดีกว่านี้
vitaly-t

-2

นี่คือวิธีที่ฉันทำโดยเป็น "แนวทางทั้งหมดข้างต้น"

Promise = require 'bluebird'
pg = module.exports = require 'pg'

Promise.promisifyAll pg.Client.prototype
Promise.promisifyAll pg.Client
Promise.promisifyAll pg.Connection.prototype
Promise.promisifyAll pg.Connection
Promise.promisifyAll pg.Query.prototype
Promise.promisifyAll pg.Query
Promise.promisifyAll pg

connectionString = process.env.DATABASE_URL

module.exports.queryAsync = (sql, values) ->
  pg.connectAsync connectionString
  .spread (connection, release) ->
    connection.queryAsync sql, values
    .then (result) ->
      console.log result.rows[0]
    .finally ->
      release()

1
ดังนั้นคุณจะไม่มีการจัดการการเชื่อมต่อไม่รองรับธุรกรรมและไม่รองรับงาน แล้วประเด็นคืออะไร?
vitaly-t

1
นั่นคือภาษาอะไร? กาแฟ? เบิก
caub
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.