【问题标题】:postgresql consume more memory in db server for long running connectionpostgresql 在数据库服务器中消耗更多内存以进行长时间运行的连接
【发布时间】:2018-02-26 03:03:02
【问题描述】:

我们有一个 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;

}

【问题讨论】:

    标签: c++ postgresql memory-leaks libpq


    【解决方案1】:

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

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

    我们如何解决这个服务器内存问题?

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

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

    但即使这样也是一种技巧。首先,准备好的语句最重要的特点是它们可以重用。您的测试程序准备的每条语句都是相同的,因此您不仅在浪费内存,而且还在浪费 CPU 周期。你应该只准备一次——通过PQsendPrepare(),或者只是PQprepare()——当它成功准备好后,用PQsendQueryPrepared()PQqueryPrepared()执行任意多次,传递相同的语句每次都命名(但参数可能不同)。

    有没有 libpq 函数 释放服务器内存?

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

    PQexecPrepared 一起使用的准备好的语句也可以通过以下方式创建 执行 SQL PREPARE 语句。另外,虽然没有libpq 用于删除准备好的语句的函数,SQL DEALLOCATE 声明可以用于此目的。

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

    大多数程序不需要这么多不同的预处理语句来产生您报告的问题。

    【讨论】:

    • 感谢您的回答。这是一个演示该问题的示例程序。我们的实际代码创建了 100 条具有不同查询的命名语句。
    • @Srini2k6,正如我在回答中已经介绍的那样,您可以通过 SQL DEALLOCATE 语句释放(指定的)准备好的语句占用的服务器内存,但是没有专用的 libpq 函数可以做到这一点。您还应该考虑 (1) 是否需要应用程序范围的连接,以及 (2) 对于您很少执行的一些查询,您是否可以放弃准备好的语句。并且要认识到这可能只是因为您的数据库服务器对您的工作负载不够强大。
    • 谢谢。你的回答很有帮助。
    猜你喜欢
    • 2014-07-15
    • 2021-11-07
    • 2014-09-22
    • 1970-01-01
    • 1970-01-01
    • 2021-04-01
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多