Pertanyaan Kode libpq Postgresql sederhana terlalu lambat?


Saya sedang mengerjakan postgresql menggunakan libpq. Kode yang diberikan di bawah ini mengambil banyak waktu (pengaturan waktu diberikan pada akhir kode).

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

Contoh keluaran (dalam msecs):

Prepared 0.55
Inserted 5527.52
count

50000
Printed 7.58

Kueri di sini disiapkan terlebih dahulu, lalu dieksekusi. Penyisipan sederhana ini membutuhkan waktu sekitar 5,5 detik. Apakah ada cara yang lebih baik untuk melakukan hal yang sama atau apakah saya melakukan sesuatu yang salah di sini?


5
2018-05-09 05:55


asal


Jawaban:


Pada koneksi TCP, setiap INSERT akan menyebabkan perjalanan TCP ke database. Sisipan 50000 yang dilakukan dalam 5,5 detik berarti satu perjalanan pulang pergi TCP membutuhkan waktu ~ 0.1ms. Anda harus membandingkannya dengan tolok ukur TCP dengan peralatan jaringan Anda, tetapi mungkin Anda tidak bisa berharap untuk pergi lebih cepat dengan metode ini.

Anda harus mempertimbangkan COPY FROM STDIN sebagai ganti INSERT individu. Secara internal, itu akan menyangga konten dan Anda cenderung melihat peningkatan kecepatan yang cukup besar karena perjalanan pulang pergi ke server jauh lebih sedikit.

Lihat http://www.postgresql.org/docs/current/static/libpq-copy.html untuk API libpq yang terkait dengan bentuk COPY ini.


5
2018-05-09 09:05



Saya memiliki masalah serupa dan mengonversi seri sisipan saya menjadi satu sisipan multi-baris. Meskipun menambahkan banyak string pengubahan dan pemanggilan straping, ini meningkatkan kinerja secara signifikan:

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

Kode sudah di https://gist.github.com/Meekohi/11291680 (Juga menunjukkan contoh memasukkan data biner ke dalam satu kolom)


3
2018-04-25 14:35



Tentukan beberapa koneksi dalam berbagai utas dan distribusikan data Anda di antara untaian itu dan kirim perintah penyisipan untuk setiap item dari untaian ini. Saya telah melakukan ini dan memperoleh peningkatan kecepatan 5-10 kali. Beri tahu saya jika Anda perlu contoh kode C ++ 11 terbaru.


0
2017-12-29 20:34