2012-05-09 5 views
5

Ich arbeitete an postgresql mit libpq. Der unten angegebene Code benötigt viel Zeit (Timings am Ende des Codes).Einfache PostgreSQL libpq Code zu langsam?

#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; 
} 

Eine Beispielausgabe (in msec):

Prepared 0.55 
Inserted 5527.52 
count 

50000 
Printed 7.58 

Die Abfrage hier hergestellt wird, zuerst, und dann ausgeführt. Dieses einfache Einfügen dauert etwa 5,5 Sekunden. Gibt es einen besseren Weg, das Gleiche zu tun, oder mache ich hier etwas falsch?

+2

Sie versuchen nur, 50000 Anfragen zu senden, das ist ganz normal! Vielleicht können Sie die lib einstellen, um die gesamten Anfragen in der gleichen Zeit zu senden, was schneller wäre. Ist der Server auf localhost? Wenn nicht, könnte ein besseres Netzwerk auch einen Unterschied machen. – Geoffroy

+0

War das wirklich für 1,5 Stunden? – vyegorov

+0

@vyegorov Die Ergebnisse sind in MSC. – c0da

Antwort

5

Bei einer TCP-Verbindung verursacht jedes INSERT einen TCP-Umlauf zur Datenbank. 50000 Einfügungen in 5,5 Sekunden bedeutet, dass ein TCP-Umlauf ~ 0,1 ms dauert. Sie müssten das mit TCP-Benchmarks mit Ihren Netzwerkgeräten vergleichen, aber Sie können wahrscheinlich nicht erwarten, dass Sie mit dieser Methode schneller arbeiten.

Sie sollten COPY FROM STDIN anstelle einzelner INSERTs in Betracht ziehen. Intern wird das den Inhalt puffern und Sie werden wahrscheinlich eine beträchtliche Geschwindigkeitserhöhung aufgrund viel weniger Rundreisen zum Server sehen.

Siehe http://www.postgresql.org/docs/current/static/libpq-copy.html für die API libpq in Bezug auf diese Form von COPY.

+3

Wenn Sie 'COPY' nicht verwenden können, haben Sie die Möglichkeit, ein mehrzeiliges' INSERT' zu verwenden (dh ein Insert mit mehr als einem Tupel nach 'VALUES' zB' VALUES (1,2,3), (1 , 4,5), (1,9,1); ') um Hin- und Rückfahrten zu reduzieren. Ich nahm "libpq" unterstützte Stapeloperationen wie 'PgJDBC' an, aber ich kann keinen Beweis dafür finden, also vielleicht nicht! Die libpq von EnterpriseDB hat eine Bulk-Send-Schnittstelle für Datenfelder, aber sie ist in vanilla libpq nicht vorhanden. –

+0

Probier es aus und lass dir die Ergebnisse wissen ... Danke, aber ... – c0da

+0

c0da hast du jemals einen Weg gefunden, dies deutlich zu beschleunigen? – Meekohi

3

Ich hatte ein ähnliches Problem und konvertierte meine Serie von Inserts in einen mehrreihigen Einsatz. Trotz Zugabe einer Menge Zeichenfolge Mangeln und strcat Anrufe, diese verbesserte Leistung deutlich:

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

-Code ist bei https://gist.github.com/Meekohi/11291680 (auch zeigt Beispiel für das Einfügen von Binärdaten in einer Spalte)

0

definieren mehrere Verbindungen in verschiedenen Threads und Verteilen Sie Ihre Daten auf diese Threads und senden Sie Einfügebefehle für jedes Element aus diesen Threads. Ich habe dies getan und 5-10 mal Geschwindigkeitszunahme gewonnen. Lassen Sie mich wissen, wenn Sie aktuelles C++ 11 Codebeispiel benötigen.