使用node.js postgresql模块的正确方法是什么?


95

我正在Heroku上编写一个node.js应用程序,并使用pg模块。我无法弄清楚为查询数据库所需的每个请求获取客户端对象的“正确”方法。

该文档使用如下代码:

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的免费数据库实例仅限于一个连接,但是以这种方式进行操作是否有任何弊端?每次使用之前,是否需要检查我的客户端对象是否仍处于连接状态?

Answers:


158

我是node-postgres的作者。首先,我很抱歉文档未能阐明正确的选项:这是我的错。我会尝试改善它。我刚才写了一份Gist来解释这一点,因为对话对于Twitter来说时间太长了。

使用pg.connect要走的路在网络环境中。

PostgreSQL服务器每个连接一次只能处理1个查询。这意味着,如果您有1个全局new pg.Client()连接到后端,则根据postgres响应查询的速度,整个应用将处于瓶颈。它实际上将所有内容排列在一起,对每个查询进行排队。是的,它是异步的,所以没关系...但是您不是将吞吐量乘以10倍吗?使用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,但在理解这一点时仍然遇到问题,但是为什么我们不希望“丢掉pg.connect”调用呢?是出于简单性还是出于性能原因?例如,我在基本应用程序中的每条路由中都调用一次pg.connect(所有路由都具有相同的conString)。这个可以吗?直观地,每当我调用它时,就好像在建立与同一个数据库的新连接(我不想要),但是它在内部使用池化连接吗?谢谢。
user1164937

太棒了 为什么每个查询使用一个连接而不是每个请求使用一个连接?我一直在寻找一种在请求中的多个查询之间共享连接的合适方法,并且一直在考虑res.locals才能在此处找到答案。
2014年

2
等一下。您的解决方案似乎不支持交易。
乔·拉普

这应该永久链接到github。
瑞安·威利斯

1
请注意,pg.connect在v7的node-postgres aka pg之后被删除。见stackoverflow.com/questions/45174120/pg-connect-not-a-function
Colin D

23

我是pg-promise的作者,它通过promise 简化了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


我是大多数人的新手:javascript,promise,postgres等,而这正是我所需要的。谢谢!!
Ryan Rodemoyer

1
@ocram我刚刚通过做这个工作pgp.pg.defaults.ssl = true;
CharlieC

当我们向postgres发出多个查询请求时,这会创建多个连接以自动提高postgres吞吐量吗?
sundar

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操作时,都要使用客户端,然后将其释放回该池。完成所有数据库操作后,使用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();

        });
}

});  

有关更多详细信息,请参阅我的博客文章- 来源



-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 2015年

1
那是什么语言?咖啡?berk
caub
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.