2012-05-09 80 views
5

我正在使用libpq處理postgresql。下面給出的代碼花費了很多時間(代碼結束時給出的時間)。簡單Postgresql libpq代碼太慢?

#include "stdafx.h" 
#include <stdlib.h> 
#include <libpq-fe.h> 
#include <windows.h> 

static void exit_nicely(PGconn *conn) 
{ 
    PQfinish(conn); 
    exit(1); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    const TCHAR *conninfo; 
    PGconn  *conn; 
    PGresult *res; 
    int nFields, i, j; 

    if (argc > 1) 
     conninfo = argv[1]; 
    else 
     conninfo = _T("hostaddr=192.168.4.171 port=12345 dbname=mydb user=myname password=mypass"); 

    conn = PQconnectdb(conninfo); 
    if (PQstatus(conn) != CONNECTION_OK) 
    { 
     fprintf(stderr, "Connection to database failed: %s", 
       PQerrorMessage(conn)); 
     exit_nicely(conn); 
    } 

    /* Start a transaction block */ 
    res = PQexec(conn, "BEGIN"); 
    if (PQresultStatus(res) != PGRES_COMMAND_OK) 
    { 
     fprintf(stderr, "BEGIN command failed: %s", PQerrorMessage(conn)); 
     PQclear(res); 
     exit_nicely(conn); 
    } 

    TCHAR szVal1[200]; 
    TCHAR szVal2[200]; 
    TCHAR szBuffer[200]; 

    TCHAR *paramValues[2]; 
    int paramLengths[2]; 
    int paramFormats[2] = {0,0}; 

    ExecStatusType eStatus; 

    LARGE_INTEGER li; 
    QueryPerformanceFrequency(&li); 
    double dAppFreq = double(li.QuadPart)/1000.0; 

    QueryPerformanceCounter(&li); 
    LONGLONG siStartCounter = li.QuadPart; 

    TCHAR szStmt[512] = {0}; 
    _tcscpy_s(szStmt, 512, _T("Insert50k")); 
    Oid oidTypes[2] = {0,0}; 

    PGresult *pRes = PQprepare(conn, 
         szStmt, 
         _T("insert into details values($1,$2);"), 
         2, 
         oidTypes); 
    QueryPerformanceCounter(&li); 
    LONGLONG siEndCounter = li.QuadPart; 
    LONGLONG siLoop = 0; 

    double dDiff = (siEndCounter - siStartCounter)/dAppFreq; 
    printf("Prepared %.2lf\n", dDiff); 

    for(int i=0; i<50000; i++) 
    { 
     _stprintf_s(szVal1, 200, _T("%d"), i); 
     _stprintf_s(szVal2, 200, _T("Detail%d"), i); 

     paramValues[0] = szVal1; 
     paramValues[1] = szVal2; 

     paramLengths[0] = _tcslen(szVal1); 
     paramLengths[1] = _tcslen(szVal2); 

     siStartCounter = siEndCounter; 
     pRes = PQexecPrepared(conn, 
         szStmt, 
         2, 
         paramValues, 
         paramLengths, 
         paramFormats, 
         0); 
     QueryPerformanceCounter(&li); 
     siEndCounter = li.QuadPart; 
     siLoop += (siEndCounter - siStartCounter); 

     eStatus = PQresultStatus(res); 
     if (!res || (eStatus != PGRES_COMMAND_OK)) 
     { 
      PQclear(res); 
      exit_nicely(conn); 
     } 
    } 

    dDiff = siLoop/dAppFreq; 
    printf("Inserted %.2lf\n", dDiff); 

    siStartCounter = siEndCounter; 


    _tcscpy_s(szBuffer,200, _T("select count(*) from programdetails;")); 
    res = PQexec(conn, szBuffer); 

    eStatus = PQresultStatus(res); 
    if (!res || (eStatus != PGRES_TUPLES_OK)) 
    { 
     PQclear(res); 
     exit_nicely(conn); 
    } 

    /* first, print out the attribute names */ 
    nFields = PQnfields(res); 
    for (i = 0; i < nFields; i++) 
     printf("%-15s", PQfname(res, i)); 
    printf("\n\n"); 

    /* next, print out the rows */ 
    for (i = 0; i < PQntuples(res); i++) 
    { 
     for (j = 0; j < nFields; j++) 
      printf("%-15s", PQgetvalue(res, i, j)); 
     printf("\n"); 
    } 

    QueryPerformanceCounter(&li); 
    siEndCounter = li.QuadPart; 
    dDiff = (siEndCounter - siStartCounter)/dAppFreq; 
    printf("Printed %.2lf\n", dDiff); 

    /* end the transaction */ 
    res = PQexec(conn, "COMMIT"); 
    PQclear(res); 

    /* close the connection to the database and cleanup */ 
    PQfinish(conn); 

    return 0; 
} 

樣品輸出(在毫秒):

Prepared 0.55 
Inserted 5527.52 
count 

50000 
Printed 7.58 

這裏查詢是首先製備,然後被執行。這個簡單的插入大約需要5.5秒。有沒有更好的方法來做同樣的事情還是我在這裏做錯了什麼?

+2

你只是嘗試發送50000個請求,這很正常!也許你可以調整lib來同時發送整個請求,這會更快。另外,是本地主機上的服務器?如果不是,一個更好的網絡也可以有所作爲。 – Geoffroy

+0

這真的運行了1.5個小時? – vyegorov

+0

@vyegorov結果以msecs爲單位。 – c0da

回答

5

在TCP連接上,每個INSERT將導致數據庫的TCP往返。在5.5秒內完成50000次插入意味着一次TCP往返需要約0.1ms。您必須將其與TCP基準與您的網絡設備進行比較,但您可能不希望採用這種方法更快。

你應該考慮COPY FROM STDIN而不是單獨的INSERT。在內部,這將緩衝內容,並且由於到服務器的往返次數少得多,您可能會看到速度顯着增加。對於與這種形式的COPY相關的libpq API,請參閱http://www.postgresql.org/docs/current/static/libpq-copy.html

+3

如果你不能使用'COPY',你可以選擇使用多行'INSERT'(例如:'VALUES'後有多個元組的插入,例如'VALUES(1,2,3),(1 ,4,5),(1,9,1);')來減少往返。我假設'libpq'支持像'PgJDBC'這樣的批處理操作,但我找不到任何證據,所以它可能不會! EnterpriseDB的libpq具有用於數據陣列的批量發送接口,但它不存在於香草libpq中。 –

+0

將試用它,並讓你知道結果...雖然...多謝, – c0da

+0

c0da你有沒有找到一種方法來顯着加快? – Meekohi

3

我有一個類似的問題,並將我的系列插入到一個多行插入。儘管加入了大量的串的mangling和strcat的呼叫,這個改善的性能顯著:

1000 rows: 
Individual Inserts: 22.609s 
Multirow Insert: 1.217s 

代碼是在https://gist.github.com/Meekohi/11291680(還示出了例如將二進制數據的成一列)

0

定義在不同線程中的多個連接和在這些線程之間分發數據併爲這些線程中的每個項目發送插入命令。我已經完成了這個,並且增加了5-10倍的速度。讓我知道你是否需要最新的C++ 11代碼示例。