2012-05-09 5 views
5

Я работал над postgresql, используя libpq. Код, приведенный ниже, занимает много времени (тайминги приведены в конце кода).Простой 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 для отправки всех запросов в одно и то же время, что было бы быстрее. Кроме того, сервер на localhost? Если это не так, лучшая сеть может также иметь значение. – Geoffroy

+0

Действительно ли это работает 1,5 часа? – vyegorov

+0

@vyegorov Результаты приведены в msecs. – c0da

ответ

5

При подключении по протоколу TCP каждое сообщение INSERT вызывает обратную связь TCP с базой данных. 50000 вставок, сделанных за 5.5 секунд, означает, что одностороннее соединение TCP ~ 0,1 мс. Вам придется сравнить это с протоколами TCP с вашим сетевым оборудованием, но, вероятно, вы не можете ожидать от этого метода быстрее.

Вы должны рассмотреть COPY FROM STDIN вместо отдельных ВСТАВКИ. Внутренне это буферизирует содержимое, и вы, вероятно, увидите значительное увеличение скорости из-за гораздо меньшего количества поездок на сервер.

См. http://www.postgresql.org/docs/current/static/libpq-copy.html для API libpq, относящегося к этой форме COPY.

+3

Если вы не можете использовать 'COPY', вы можете использовать многострочный' INSERT' (то есть: вставка с более чем одним кортежем после 'VALUES', например' VALUES (1,2,3), (1 , 4,5), (1,9,1); ') для уменьшения круговых поездок. Я предположил, что 'libpq' поддерживает пакетные операции, такие как' PgJDBC', но я не могу найти никаких доказательств, так что, возможно, это не так! В libpq от EnterpriseDB есть массив send send для массивов данных, но он отсутствует в vanilla libpq. –

+0

Попробуй и узнай результаты ... Спасибо, хотя ... – c0da

+0

c0da Вы когда-нибудь находили способ значительно ускорить это? – Meekohi

3

У меня была аналогичная проблема и преобразовал мою серию вставок в одну многострочную вставку. Несмотря на добавление много строк коверкание и strcat вызовов, это улучшенные рабочие характеристики значительно:

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

код находится на https://gist.github.com/Meekohi/11291680 (также показывает пример вставки двоичных данных в один столбец)

0

Определение нескольких соединений в различных потоках и распределите свои данные между этими потоками и отправьте команды вставки для каждого элемента из этих потоков. Я сделал это и получил прирост в 5-10 раз. Дайте мне знать, если вам нужен современный пример кода C++ 11.