c++ - postgresql 在数据库服务器中消耗更多内存以进行长时间运行的连接

标签 c++ postgresql memory-leaks libpq

我们有一个 C++ 服务器应用程序,它使用 libpq 库连接到 postgresql 数据库。应用程序创建 100 秒的数据库连接,连接的大部分生命周期都是应用程序范围。

最初应用程序运行良好,但在一段时间后,postgres 服务器为长时间运行的连接消耗了更多内存。通过编写下面的示例程序,我了解到使用 PQsendPreparePQsendQueryPrepared 创建准备好的语句会导致数据库服务器中的内存消耗问题。

我们如何解决这个服务器内存问题?是否有任何 libpq 函数可以释放服务器中的内存?

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>

int main(int argc, char *argv[]) {

    const int LEN = 10;
    const char *paramValues[1];

    int paramFormats[1];
    int rowId = 7369;
    Oid paramTypes[1];
    char str[LEN];
    snprintf(str, LEN, "%d", rowId);
    paramValues[0] = str;
    paramTypes[0]=20;
    paramFormats[0]=0;
    long int c=1;

    PGresult* result;
    //PGconn *conn = PQconnectdb("user=scott dbname=dame");
    PGconn *conn = PQsetdbLogin ("", "", NULL, NULL, "dame", "scott", "tiger") ;

    if (PQstatus(conn) == CONNECTION_BAD) {
        fprintf(stderr, "Connection to database failed: %s\n",
            PQerrorMessage(conn));
    do_exit(conn);
    }
    char *stm = "SELECT coalesce(ename,'test') from emp where empno=$1";
    for(;;)
    {
        std::stringstream strStream ; 
        strStream << c++ ;
        std::string strStatementName = "s_" + strStream.str() ;
        if(PQsendPrepare(conn,strStatementName.c_str(), stm,1,paramTypes) )
        {
            result = PQgetResult(conn); 
            if (PQresultStatus(result) != PGRES_COMMAND_OK)
            {
                PQclear(result) ;
                result = NULL ;
                do
                {
                    result = PQgetResult(conn);
                    if(result != NULL)
                    {
                        PQclear (result) ;
                    }
                } while (result != NULL) ;
                std::cout<<"error prepare"<<PQerrorMessage (conn)<<std::endl;
                break;
            }
            PQclear(result) ;
            result = NULL ;
            do
            {
                result = PQgetResult(conn);
                if(result != NULL)
                {
                    PQclear (result) ;
                }
            } while (result != NULL) ;
        }
        else
        {
            std::cout<<"error:"<<PQerrorMessage (conn)<<std::endl;
            break;
        }

        if(!PQsendQueryPrepared(conn,
                strStatementName.c_str(),1,(const char* const *)paramValues,paramFormats,paramFormats,0))
        {
            std::cout<<"error:prepared "<<PQerrorMessage (conn)<<std::endl;
        }
        if (!PQsetSingleRowMode(conn))
        {
            std::cout<<"error singrow mode "<<PQerrorMessage (conn)<<std::endl;
        }
    result = PQgetResult(conn);
        if (result != NULL)
        {
            if((PGRES_FATAL_ERROR == PQresultStatus(result)) || (PGRES_BAD_RESPONSE == PQresultStatus(result)))
            {
                PQclear(result);
                result = NULL ;
                do
                {
                    result = PQgetResult(conn);
                    if(result != NULL)
                    {
                        PQclear (result) ;
                    }
                } while (result != NULL) ;
                break;
            }

            if (PQresultStatus(result) == PGRES_SINGLE_TUPLE)
            {
                std::ofstream myfile;
            myfile.open ("native.txt",std::ofstream::out |     std::ofstream::app);
                myfile << PQgetvalue(result, 0, 0)<<"\n";
                myfile.close();
                PQclear(result);
                result = NULL ;
                do
                {
                    result = PQgetResult(conn) ;
                    if(result != NULL)
                    {
                        PQclear (result) ;
                    }
                }
                while(result != NULL) ;
                sleep(10);
            }
            else if(PQresultStatus(result) == PGRES_TUPLES_OK || PQresultStatus(result) ==  PGRES_COMMAND_OK)
            {
                PQclear(result);
                result = NULL ;
                do
                {
                    result = PQgetResult(conn) ;
                    if(result != NULL)
                    {
                        PQclear (result) ;
                    }
                }
                while(result != NULL) ;
            }
       }

    }

    PQfinish(conn);
    return 0;

}

最佳答案

Initially application was running fine, but over a period of time postgres server consuming more memory for long running connections. By writing a below sample program I come to know creating prepared statements using PQsendPrepare and PQsendQueryPrepared is causing the memory consumption issue in database server.

嗯,这似乎不足为奇。您在外循环的每次迭代中生成一个新的准备语句名称,然后创建并执行该名称的准备语句。只要连接处于打开状态,所有由此产生的、名称不同的准备好的语句确实会保留在服务器的内存中。这是故意的。

How we can fix this server memory issue?

我将其描述为程序逻辑问题,而不是服务器内存问题,至少就测试程序而言是这样。您获取资源(准备好的语句),然后在您不再使用它们时允许它们闲逛。语句本身并没有泄漏,因为您可以重新创建算法生成的语句名称,但问题类似于资源泄漏。在您的程序中,而不是在 Postgres 中。

如果你想使用一次性准备好的语句,那么给它们一个空字符串,"",作为它们的名字。 Postgres 称这些为“未命名”语句。您准备的每个未命名语句都将替换属于同一连接的任何先前语句。

但即便如此,这也是一个 hack。首先,准备好的语句最重要的特性是它们可以重用。你的测试程序准备的每条语句都是相同的,所以你不仅在浪费内存,也在浪费 CPU 周期。你应该只准备一次——通过PQsendPrepare(),或者可能只是PQprepare()——当它成功准备好后,执行它的次数不限想要使用 PQsendQueryPrepared()PQqueryPrepared(),每次都传递相同的语句名称(但可能有不同的参数)。

is there any libpq function to free the memory in server?

The documentation for the synchronous versions of the query functions说:

Prepared statements for use with PQexecPrepared can also be created by executing SQL PREPARE statements. Also, although there is no libpq function for deleting a prepared statement, the SQL DEALLOCATE statement can be used for that purpose.

据我所知,在 Postgres 中只有一种预准备语句,同步函数和异步函数都使用类似的语句。所以不,libpq 没有提供专门用于删除与连接关联的准备好的语句的功能,但您可以在 SQL 中编写语句来完成这项工作。当然,创建一个新的、唯一命名的预处理语句来执行这样的语句是没有意义的。

大多数程序不需要这么多不同的准备语句来产生您报告的那种问题。

关于c++ - postgresql 在数据库服务器中消耗更多内存以进行长时间运行的连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48980858/

相关文章:

java - 从 JVM 线程局部空间卸载 Clojure 变量

c++ - inline 关键字对类成员模板函数的影响?

c++ - 使用 ifstream 和 ofstream 序列化二进制数据时遇到问题

c++ - 如何使用自定义位图将复选框添加到 MFC 中的工具栏?

python - PostgreSQL 中的 SQL 时间戳

sql - 在 PostgreSQL 整数列上生成序列号

python - Django 数组字段

java - 静态上下文保存在 Application 类中并在单例 Toast 构建器中使用,这是否会造成内存泄漏?

c++ - 命名空间错误 'Not a namespace-name'

c++ - C++中的内存泄漏