PQsetSingleRowMode 低性能

PQsetSingleRowMode low performance

我目前正在使用 libpq 为 PostgreSQL 实现一个适配器,我想知道通过 PQsetSingleRowMode (https://www.postgresql.org/docs/9.6/static/libpq-single-row-mode.html) 设置的单行模式是否存在已知的性能问题?

我的印象是,在这种模式下,libpq 会使用 intelligent/efficient 比游标和显式 FETCH 所能达到的缓冲更多。

但是,当 运行ning 10x 一个简单查询时("select id from mytable",返回 5000 条记录)。使用 PostgreSQL 9.6.3(在客户端和服务器端),我观察到以下性能模式:

所以单行模式似乎比创建游标和逐行获取更有效...这是正确的行为吗?或者是否有其他一些选项来控制单行模式?

(在我的使用案例中,就某些查询的内存使用而言,一次获取所有内容会有风险,因此实际上是在单行模式或一次获取几条记录之间进行选择)

追问:有没有办法轻松中途中止获取单行模式? 当使用 cursor+fetch 时,这是微不足道的,但对于单行模式,您似乎需要获取所有结果或使用 PQcancel)

附录: 有另一个 运行 使用分析器,结果 PQgetResult 对 malloc 和 free 的调用是瓶颈(分别占 CPU 时间的 60% 和 30%),这两个函数都来自 MSVCR120 AFAICT(这是在 Win 10 下,服务器是本地主机)。我正在使用 "official" PostgreSQL zip 中的 libpq.dll。有趣的是,运行在基准测试之前进行其他查询可以 "sometimes" 使问题消失。看起来 PQgetResult 遇到了 malloc/free.

的弱点

真令人惊讶。

虽然重复调用 PQgetResult 肯定会产生一定的开销,但实际开销应该没有您观察到的那么多。

我 运行 一个查询 returns 500 万行的测试 localhost 上的数据库,单行模式需要稍微多于 CPU 的两倍用户时间(总执行时间主要由数据库服务器处理时间决定)。

5000 行 3 秒听起来有点可疑,可能还有其他问题。

尝试分析可执行文件以查看时间花在哪里。

您想知道其他人是否也会在单行模式下表现不佳:是的,我也是,在 Ubuntu Linux.

下使用 postgresql 10

我提交了一个返回约 300 万条文本的查询。 (一次作为函数的结果,一次只是 "SELECT primary_key FROM table"。)打开 PQsetSingleRowMode() 与正常的 PGexec() 一样快(事实上,慢 1%)。此外,postgresql-server 的 RAM 使用情况没有显着差异(即使我在每个获得的行上执行 PQclear())。

当文档说 PGexec() "can be unworkable for commands that return a large number of rows" 时,也许它们只是意味着对于您的应用程序来说,一次获得这么多结果可能是不切实际的。

参见https://www.postgresql.org/docs/current/libpq-single-row-mode.html and https://www.postgresql.org/docs/current/libpq-async.html . For cancellation, https://www.postgresql.org/docs/current/libpq-cancel.html

如果其他人想要进行基准测试,这是我的 C++ 代码:

void outputFirstColumn(PGconn* conn,
    const string& sql_query,
    bool request_single_row_mode
)
{
  if(! PQsendQuery(conn, sql_query.c_str()))
    throw runtime_error("(E) command could not be dispatched successfully: "
      + string(PQerrorMessage(conn)));

  const int m = request_single_row_mode ? PQsetSingleRowMode(conn) : 0;
  cerr << "single-row-mode activated? " << (m == 1 ? "yes" : "no")<< endl;

  PGresult* res;
  while((res = PQgetResult(conn)))
  {
    const int rows = PQntuples(res);
    for(int r = 0; r < rows; ++r)
      cout << PQgetvalue(res, r, 0) << "\n";

    PQclear(res);
  }
}