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(在客户端和服务器端),我观察到以下性能模式:
- PQexecParams(一次):78 毫秒
- PQsetSingleRowMode:3047 毫秒(大部分花费在 PQgetResult 中)
- PQexecParams + FETCH 1:3313 毫秒(大部分花费在 PQgetResult 中)
- PQexecParams + FETCH 10:219 毫秒
所以单行模式似乎比创建游标和逐行获取更有效...这是正确的行为吗?或者是否有其他一些选项来控制单行模式?
(在我的使用案例中,就某些查询的内存使用而言,一次获取所有内容会有风险,因此实际上是在单行模式或一次获取几条记录之间进行选择)
追问:有没有办法轻松中途中止获取单行模式?
当使用 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);
}
}
我目前正在使用 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(在客户端和服务器端),我观察到以下性能模式:
- PQexecParams(一次):78 毫秒
- PQsetSingleRowMode:3047 毫秒(大部分花费在 PQgetResult 中)
- PQexecParams + FETCH 1:3313 毫秒(大部分花费在 PQgetResult 中)
- PQexecParams + FETCH 10:219 毫秒
所以单行模式似乎比创建游标和逐行获取更有效...这是正确的行为吗?或者是否有其他一些选项来控制单行模式?
(在我的使用案例中,就某些查询的内存使用而言,一次获取所有内容会有风险,因此实际上是在单行模式或一次获取几条记录之间进行选择)
追问:有没有办法轻松中途中止获取单行模式? 当使用 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);
}
}