sql >> Database >  >> RDS >> PostgreSQL

node-postgres met enorme hoeveelheid queries

UPDATE

Dit antwoord is sindsdien vervangen door dit artikel:Gegevensimport , wat de meest actuele benadering vertegenwoordigt.

Om je scenario te repliceren heb ik pg-promise gebruikt bibliotheek, en ik kan bevestigen dat het nooit zal werken, welke bibliotheek je ook gebruikt, het is de aanpak die ertoe doet.

Hieronder is een aangepaste benadering waarbij we inserts in chunks partitioneren en vervolgens elk chunk binnen een transactie uitvoeren, wat load balancing is (ook bekend als throttling):

function insertRecords(N) {
    return db.tx(function (ctx) {
        var queries = [];
        for (var i = 1; i <= N; i++) {
            queries.push(ctx.none('insert into test(name) values($1)', 'name-' + i));
        }
        return promise.all(queries);
    });
}
function insertAll(idx) {
    if (!idx) {
        idx = 0;
    }
    return insertRecords(100000)
        .then(function () {
            if (idx >= 9) {
                return promise.resolve('SUCCESS');
            } else {
                return insertAll(++idx);
            }
        }, function (reason) {
            return promise.reject(reason);
        });
}
insertAll()
    .then(function (data) {
        console.log(data);
    }, function (reason) {
        console.log(reason);
    })
    .done(function () {
        pgp.end();
    });

Dit leverde 1000.000 records op in ongeveer 4 minuten, een dramatische vertraging na de eerste 3 transacties. Ik gebruikte Node JS 0.10.38 (64-bit), die ongeveer 340 MB geheugen in beslag nam. Op deze manier hebben we 10 keer achter elkaar 100.000 records ingevoegd.

Als we hetzelfde doen, maar deze keer 10.000 records invoegen binnen 100 transacties, worden dezelfde 1.000.000 records toegevoegd in slechts 1m25s, zonder vertraging, waarbij Node JS ongeveer 100 MB geheugen verbruikt, wat ons vertelt dat het partitioneren van gegevens als deze een zeer goed idee.

Het maakt niet uit welke bibliotheek je gebruikt, de aanpak moet hetzelfde zijn:

  1. Verdeel/versmoor uw invoegingen in meerdere transacties;
  2. Houd de lijst met bijlagen in een enkele transactie op ongeveer 10.000 records;
  3. Voer al uw transacties uit in een synchrone keten.
  4. Verbreek de verbinding met de pool na COMMIT van elke transactie.

Als je een van die regels overtreedt, krijg je gegarandeerd problemen. Als u bijvoorbeeld regel 3 overtreedt, zal uw Node JS-proces waarschijnlijk snel onvoldoende geheugen hebben en een fout veroorzaken. Regel 4 in mijn voorbeeld werd geleverd door de bibliotheek.

En als u dit patroon volgt, hoeft u zich geen zorgen te maken over de instellingen van de verbindingspool.

UPDATE 1

Latere versies van pg-promise ondersteunen dergelijke scenario's perfect, zoals hieronder weergegeven:

function factory(index) {
    if (index < 1000000) {
        return this.query('insert into test(name) values($1)', 'name-' + index);
    }
}

db.tx(function () {
    return this.batch([
        this.none('drop table if exists test'),
        this.none('create table test(id serial, name text)'),
        this.sequence(factory), // key method
        this.one('select count(*) from test')
    ]);
})
    .then(function (data) {
        console.log("COUNT:", data[3].count);
    })
    .catch(function (error) {
        console.log("ERROR:", error);
    });

en als je niets extra's wilt toevoegen, zoals het maken van tabellen, dan ziet het er nog eenvoudiger uit:

function factory(index) {
    if (index < 1000000) {
        return this.query('insert into test(name) values($1)', 'name-' + index);
    }
}

db.tx(function () {
    return this.sequence(factory);
})
    .then(function (data) {
        // success;
    })
    .catch(function (error) {
        // error;
    });

Zie Synchrone transacties voor details.

Bluebird gebruiken als de beloftebibliotheek, bijvoorbeeld, duurt het 1m43s op mijn productiemachine om 1.000.000 records in te voegen (zonder dat long stack traces is ingeschakeld).

Je zou gewoon je factory . hebben methode retourverzoeken volgens de index , totdat je er geen meer over hebt, zo simpel is het.

En het beste is, dit is niet alleen snel, maar zorgt ook voor weinig belasting van uw NodeJS-proces. Het geheugentestproces blijft tijdens de hele test onder de 60 MB en verbruikt slechts 7-8% van de CPU-tijd.

UPDATE 2

Vanaf versie 1.7.2, pg-promise ondersteunt met gemak super-enorme transacties. Zie hoofdstuk Synchrone transacties .

Ik zou bijvoorbeeld 10.000.000 records in een enkele transactie in slechts 15 minuten kunnen invoegen op mijn pc thuis, met Windows 8.1 64-bit.

Voor de test zette ik mijn pc in de productiemodus en gebruikte ik Bluebird als de beloftebibliotheek. Tijdens de test kwam het geheugenverbruik niet hoger dan 75 MB voor het hele NodeJS 0.12.5-proces (64-bit), terwijl mijn i7-4770 CPU een consistente belasting van 15% liet zien.

Het op dezelfde manier invoegen van 100m-records zou alleen meer geduld vergen, maar niet meer computerbronnen.

In de tussentijd is de vorige test voor 1m wisselplaten gedaald van 1m43s naar 1m31s.

UPDATE 3

De volgende overwegingen kunnen een enorm verschil maken:Prestatieboost .

UPDATE 4

Gerelateerde vraag, met een beter implementatievoorbeeld:Massieve inserts met pg-promise .

UPDATE 5

Een beter en nieuwer voorbeeld is hier te vinden:nodeJS inserting Data in PostgreSQL-fout



  1. Incidentele NullPointerException in ResultSetImpl.checkColumnBounds of ResultSetImpl.getStringInternal

  2. SQLAlchemy create_all() maakt geen tabellen

  3. MYSQL-rijen met dezelfde veldnamen selecteren en een voorvoegsel toevoegen

  4. Failover-opties voor multi-cloud volledige databasecluster voor MariaDB-cluster