2012-05-09 11 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結果はミリ秒です。 – c0da

答えて

5

TCP接続では、各INSERTによってデータベースへのTCPラウンドトリップが発生します。 5.500秒で行われた50000個の挿入は、1回のTCP往復に約0.1msかかることを意味します。これをネットワーク機器のTCPベンチマークと比較する必要がありますが、おそらくこの方法ではさらに高速化することは期待できません。

個々のINSERTの代わりにCOPY FROM STDINを考慮する必要があります。内部的には内容がバッファされ、サーバーへのラウンドトリップが大幅に減り、速度が大幅に向上する可能性があります。

このCOPY形式に関連するlibpq APIについては、http://www.postgresql.org/docs/current/static/libpq-copy.htmlを参照してください。

+3

'COPY'を使うことができないなら、' VALUES'の後に複数のタプルを持つ挿入(例えば、 'VALUES(1,2,3)、(1) 、4,5)、(1,9,1); ')を使用して往復を減らします。私は 'libpq'で' PgJDBC'のようなバッチ処理をサポートしていたと思っていましたが、何の証拠も見つけられませんでした。 EnterpriseDBのlibpqには、データの配列に対する一括送信インタフェースがありますが、vanilla libpqには存在しません。 –

+0

これを試して結果をお知らせします...ありがとう... – c0da

+0

c0daこれを大幅にスピードアップする方法を見つけましたか? – Meekohi

3

私は同様の問題があり、私の一連の挿入物を1つの複数行挿入物に変換しました。ストリングマングリングとSTRCATコールの多くを追加するにもかかわらず、大幅にこの改良された性能:

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

コードhttps://gist.github.com/Meekohi/11291680である(これも一つの列にバイナリデータを挿入する例を示している)

0

は、異なるスレッドで複数の接続を定義しこれらのスレッド間でデータを配布し、これらのスレッドから各アイテムの挿入コマンドを送信します。私はこれを行い、5〜10倍のスピードアップを得ました。あなたが最新のC++ 11のコード例が必要かどうか私に教えてください。

関連する問題