PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

node-postgres z ogromną ilością zapytań

AKTUALIZUJ

Ta odpowiedź została zastąpiona tym artykułem:Import danych , który reprezentuje najbardziej aktualne podejście.

Aby odtworzyć Twój scenariusz, użyłem pg-promise i mogę potwierdzić, że próba bezpośredniego nigdy nie zadziała, bez względu na to, z której biblioteki korzystasz, to podejście ma znaczenie.

Poniżej znajduje się zmodyfikowane podejście, w którym dzielimy partycje na porcje, a następnie wykonujemy każdą porcję w ramach transakcji, co jest równoważeniem obciążenia (aka ograniczaniem):

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

To dało 100 000 rekordów w około 4 minuty, drastycznie spowalniając po pierwszych 3 transakcjach. Używałem Node JS 0.10.38 (64-bit), który zużywał około 340MB pamięci. W ten sposób wstawiliśmy 100 000 rekordów, 10 razy z rzędu.

Jeśli zrobimy to samo, tylko tym razem wstawimy 10 000 rekordów w ramach 100 transakcji, te same 1 000 000 rekordów zostanie dodanych w zaledwie 1m25s, bez spowalniania, z Node JS zużywającym około 100 MB pamięci, co mówi nam, że takie partycjonowanie danych jest bardzo dobry pomysł.

Nie ma znaczenia, której biblioteki używasz, podejście powinno być takie samo:

  1. Podziel/ogranicz swoje wstawki do wielu transakcji;
  2. Utrzymuj listę wstawek w jednej transakcji na około 10 000 rekordów;
  3. Wykonaj wszystkie transakcje w łańcuchu synchronicznym.
  4. Zwolnij połączenie z powrotem do puli po każdej transakcji COMMIT.

Jeśli złamiesz którąkolwiek z tych zasad, masz gwarancję kłopotów. Na przykład, jeśli złamiesz regułę 3, twój proces Node JS prawdopodobnie bardzo szybko zabraknie pamięci i zgłosi błąd. Zasada 4 w moim przykładzie została dostarczona przez bibliotekę.

A jeśli zastosujesz się do tego wzorca, nie musisz zawracać sobie głowy ustawieniami puli połączeń.

AKTUALIZACJA 1

Późniejsze wersje pg-promise doskonale obsługują takie scenariusze, jak pokazano poniżej:

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

a jeśli nie chcesz dodawać niczego dodatkowego, na przykład tworzenia tabel, wygląda to jeszcze prościej:

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

Zobacz Transakcje synchroniczne dla szczegółów.

Korzystanie z Bluebird na przykład jako biblioteka obietnicy, na mojej maszynie produkcyjnej potrzeba 1m43s, aby wstawić 1 000 000 rekordów (bez włączonych długich śladów stosu).

Po prostu miałbyś swoją factory metody zwracają żądania zgodnie z index , dopóki nie zostanie ci nic, tak proste.

A najlepsze jest to, że jest to nie tylko szybkie, ale także w niewielkim stopniu obciąża proces NodeJS. Proces testowania pamięci pozostaje poniżej 60 MB podczas całego testu, zużywając tylko 7-8% czasu procesora.

AKTUALIZACJA 2

Począwszy od wersji 1.7.2, pg-promise z łatwością obsługuje supermasywne transakcje. Zobacz rozdział Transakcje synchroniczne .

Na przykład mogę wstawić 10 000 000 rekordów w jednej transakcji w zaledwie 15 minut na moim domowym komputerze z 64-bitowym systemem Windows 8.1.

Do testu ustawiłem komputer w trybie produkcyjnym i użyłem Bluebird jako biblioteka obietnic. Podczas testu zużycie pamięci nie przekroczyło 75 MB dla całego procesu NodeJS 0.12.5 (64-bitowego), podczas gdy mój procesor i7-4770 wykazywał stałe 15% obciążenie.

Wstawienie 100-metrowych rekordów w ten sam sposób wymagałoby tylko więcej cierpliwości, ale nie więcej zasobów komputerowych.

W międzyczasie poprzedni test dla 1m wstawek spadł z 1m43s do 1m31s.

AKTUALIZACJA 3

Następujące kwestie mogą mieć ogromne znaczenie:Wzrost wydajności .

AKTUALIZACJA 4

Powiązane pytanie, z lepszym przykładem implementacji:Masywne wstawki z pg-promise .

AKTUALIZACJA 5

Lepszy i nowszy przykład można znaleźć tutaj:nodeJS wstawianie danych w błąd PostgreSQL



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nie można wybrać niektórych wierszy za pomocą npgsql w portugalskiej bazie danych

  2. Nie można połączyć się z bazą danych Postgres za pomocą stosu Bitnami Django

  3. Jak wstawić i pobrać obraz z PostgreSql za pomocą C#

  4. Postgres pg_toast w autovacuum - który stół?

  5. Jak zmapować org.postgresql.geometric.PGpoint na typ hibernacji?