Einfache Postgresql libpq-code zu langsam ist?
Arbeitete ich auf postgresql mit libpq. Den unten angegebenen code nimmt eine Menge Zeit (Zeiten am Ende des Codes).
#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;
}
Ein Beispiel für die Ausgabe (in MSEK):
Prepared 0.55
Inserted 5527.52
count
50000
Printed 7.58
Die Abfrage hier bereit ist, den ersten, und dann ausgeführt. Dieses einfache Montage dauert etwa 5,5 Sekunden. Gibt es eine bessere Möglichkeit, das gleiche zu tun oder mache ich etwas falsch hier?
- Sie nur versuchen, zu senden 50000 Anfragen, das ist ganz normal ! Vielleicht kann man mit tune up die lib zu schicken, die ganzen Anfragen in der gleichen Zeit, das wäre schneller. Außerdem ist der server auf localhost? Wenn nicht, ein besseres Netzwerk könnte auch einen Unterschied machen.
- War das wirklich läuft für 1,5 Stunden?
- Die Ergebnisse sind in MSEK.
- In diesem Fall wurde der server auf meiner eigenen Maschine. Aber es kann aus der Ferne zugegriffen werden, wie gut. Es ist auf einer 1Gbps-Verbindung. Und wie muss ich tune die lib zum senden wird die gesamte Abfrage in der gleichen Zeit? Irgendwelche Vorschläge?
- versuchen Sie, um die Suche für die bulk-Einfügung ist das, was Sie tun wollen.
- 1 gigabit oder 1 megabit, es spielt keine Rolle, viel, wenn kleine Pakete beteiligt sind; Ihr Hauptthema sein wird Latenz Durchsatz nicht. Sie senden jede Anfrage, dann auf eine Antwort warten, bevor weitere arbeiten. Sie können nicht tun, effizient; Sie müssen Dosierungs-Anfragen, die haben mehrere im Flug, etc. Pg nicht unterstützt mehrere gleichzeitige Anfragen (leider), aber Sie können sicher batch-oder in diesem Fall verwenden Sie
COPY
oder multi-row-inserts.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Auf eine TCP-Verbindung, jede INSERT-dazu, dass ein TCP round-trip-für die Datenbank. 50000 Einsätze erfolgt in 5,5 Sekunden bedeutet, dass eine TCP round-trip dauert ~0,1 ms. Man müsste vergleichen, die auf TCP-benchmarks, die mit Ihrem Netzwerk-equipment, aber wahrscheinlich können Sie nicht erwarten, um schneller mit dieser Methode.
Sollten Sie überlegen
COPY FROM STDIN
insteads der einzelnen Einsätze. Intern wird der Puffer-Inhalt und Sie sind wahrscheinlich, um zu sehen, eine beträchtliche Erhöhung der Geschwindigkeit wegen sehr viel weniger round-trips zum server.Sehen http://www.postgresql.org/docs/current/static/libpq-copy.html für die libpq-API im Zusammenhang mit dieser form von KOPIE.
COPY
Sie haben die Möglichkeit, über ein multi-row -INSERT
(sprich: ein insert mit mehr als einem Tupel nachVALUES
zBVALUES (1,2,3), (1,4,5), (1,9,1);
) zur Verringerung von round-trips. Ich nahmlibpq
unterstützt batch-Operationen wiePgJDBC
aber ich finde keine Beweise dafür, also vielleicht ist es noch nicht! EnterpriseDB ist libpq hat eine bulk-senden-Schnittstelle für Daten-arrays, aber es gibt es nicht in vanilla libpq.Ich hatte ein ähnliches Problem und konvertiert meine Serie der Einsätze in einem multi-row-insert. Trotz Zugabe eine Menge von string mangling und strcat fordert, diese Leistung wesentlich verbessert:
Code ist an https://gist.github.com/Meekohi/11291680 (zeigt auch Beispiel für das einfügen von binären Daten in einer Spalte)
Definieren von mehreren verbindungen in verschiedenen threads und verteilen Sie Ihre Daten zwischen threads und senden Sie einfügen-Befehle für jedes Element von diesen threads. Ich habe das getan und gewonnen 5-10-fache Geschwindigkeit zu erhöhen. Lassen Sie mich wissen, wenn Sie brauchen up-to-date-C++11 code Beispiel.