sql >> Databasteknik >  >> RDS >> PostgreSQL

node-postgres med enorma mängder frågor

UPPDATERA

Det här svaret har sedan dess ersatts av den här artikeln:Dataimport , som representerar den mest uppdaterade metoden.

För att replikera ditt scenario använde jag pg-promise biblioteket, och jag kan bekräfta att det aldrig kommer att fungera att prova det direkt, oavsett vilket bibliotek du använder, det är tillvägagångssättet som spelar roll.

Nedan är ett modifierat tillvägagångssätt där vi partitionerar infogar i bitar och sedan exekverar varje del i en transaktion, vilket är lastbalansering (aka strypning):

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();
    });

Detta producerade 1 000 000 poster på cirka 4 minuter, vilket avtog dramatiskt efter de första 3 transaktionerna. Jag använde Node JS 0.10.38 (64-bitars), som förbrukade cirka 340 MB minne. På så sätt infogade vi 100 000 poster, 10 gånger i rad.

Om vi ​​gör detsamma, bara den här gången infogar 10 000 poster inom 100 transaktioner, samma 1 000 000 poster läggs till på bara 1m25s, ingen sakta ner, med Node JS som förbrukar cirka 100 MB minne, vilket talar om för oss att partitionering av data som denna är mycket bra idé.

Det spelar ingen roll vilket bibliotek du använder, tillvägagångssättet bör vara detsamma:

  1. Partitionera/strypa dina skär i flera transaktioner;
  2. Håll listan över bilagor i en enda transaktion på cirka 10 000 poster;
  3. Utför alla dina transaktioner i en synkron kedja.
  4. Släpp tillbaka anslutningen till poolen efter varje transaktions COMMIT.

Om du bryter mot någon av dessa regler, är du garanterad problem. Till exempel, om du bryter mot regel 3, kommer din Node JS-process sannolikt att ta slut på minnet riktigt snabbt och orsaka ett fel. Regel 4 i mitt exempel tillhandahölls av biblioteket.

Och om du följer det här mönstret behöver du inte besvära dig med inställningarna för anslutningspoolen.

UPPDATERING 1

Senare versioner av pg-promise stödja sådana scenarier perfekt, som visas nedan:

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

och om du inte vill inkludera något extra, som att skapa tabeller, så ser det ännu enklare ut:

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

Se Synkrona transaktioner för detaljer.

Använder Bluebird som löftesbiblioteket, till exempel, tar det 1 m43s på min produktionsmaskin för att infoga 1 000 000 poster (utan långa stackspår aktiverade).

Du skulle bara ha din factory metod returnerar begäranden enligt index , tills du inte har några kvar, så enkelt är det.

Och det bästa, detta är inte bara snabbt, utan skapar också liten belastning på din NodeJS-process. Minnestestprocessen förblir under 60 MB under hela testet och förbrukar endast 7-8 % av CPU-tiden.

UPPDATERING 2

Från och med version 1.7.2, pg-promise stöder supermassiva transaktioner med lätthet. Se kapitlet Synkrona transaktioner .

Till exempel skulle jag kunna infoga 10 000 000 poster i en enda transaktion på bara 15 minuter på min hemdator, med Windows 8.1 64-bitars.

För testet ställde jag in min dator i produktionsläge och använde Bluebird som löftesbiblioteket. Under testet gick inte minnesförbrukningen över 75 MB för hela NodeJS 0.12.5-processen (64-bitars), medan min i7-4770 CPU visade konstant 15 % belastning.

Att infoga 100m-poster på samma sätt skulle kräva bara mer tålamod, men inte mer datorresurser.

Under tiden sjönk det tidigare testet för 1m skär från 1m43s till 1m31s.

UPPDATERING 3

Följande överväganden kan göra stor skillnad:Prestandaboost .

UPPDATERING 4

Relaterad fråga, med ett bättre implementeringsexempel:Massiva inlägg med pg-promise .

UPPDATERING 5

Ett bättre och nyare exempel finns här:nodeJS infogar data i PostgreSQL-fel



  1. Hämta LONGBLOB från MySQL i C#

  2. Distribuera MySQL, MariaDB, Percona Server, MongoDB eller PostgreSQL - enkelt med ClusterControl

  3. Indexerad vy underhåll i exekveringsplaner

  4. SELECT * från SQL-tabellen med förberedd sats