UPDATE
Ta odpowiedzi został od wyparte z tego artykułu: Data Imports, który prezentuje najbardziej podejście up-to-date.
W celu powtórzenia scenariusza użyłem pg-promise bibliotekę, i mogę potwierdzić, że próbuje on czołowo nigdy nie będzie działać, bez względu na to, która biblioteka użyć, jest to podejście, które liczy.
Poniżej jest zmodyfikowaną podejście gdzie partycje wkładki na kawałki, a następnie wykonać każdy klocek w ramach transakcji, która jest równoważenie obciążenia (aka dławienia):
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();
});
Ten wyprodukowany w około 1,000,000 rekordy 4 minuty, dramatycznie spowalniające po pierwszych 3 transakcjach. Używałem Node JS 0.10.38 (64-bit), który zużył około 340 MB pamięci. W ten sposób wprowadziliś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 spowolnienia, z Node JS zużywającym około 100 MB pamięci, co mówi nam, że partycjonowanie danych takich jak To bardzo dobry pomysł.
To nie ma znaczenia, który biblioteki użyć podejście powinno być takie same:
- partycji/przepustnica twoje wstawki na wiele transakcji;
- Lista wkładek powinna znajdować się w pojedynczej transakcji z około 10 000 rekordów;
- Wykonaj wszystkie transakcje w łańcuchu synchronicznym.
- Zwolnij połączenie z powrotem do puli po COMMIT każdej transakcji.
Jeśli złamiesz którąkolwiek z tych zasad, masz problem. Na przykład, jeśli złamiesz regułę 3, proces Node JS prawdopodobnie szybko wyczerpie pamięć i wygeneruje błąd. Zasada 4 w moim przykładzie została dostarczona przez bibliotekę.
Jeśli podążasz za tym schematem, nie musisz się kłopotać ustawieniami puli połączeń.
UPDATE 1
Późniejsze wersje pg-promise wsparcia takich sytuacjach doskonale, 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);
});
i jeśli nie chcą zawierać żadnych dodatków, takich jak tworzenie tabeli, to wygląda jeszcze prostsze:
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;
});
Aby uzyskać szczegółowe informacje, patrz: Synchronous Transactions.
Używanie jako biblioteki obietnic, na przykład, zajmuje 1m43 na maszynie produkcyjnej do wstawienia 1 000 000 rekordów (bez włączonych długich wątków stosu).
Po prostu otrzymasz żądanie zwrotu według metody factory
zgodnie z index
, dopóki nie pozostanie Ci nic prostszego.
A najlepsze jest to, że nie jest to tylko szybki proces, ale także powoduje niewielkie obciążenie procesu NodeJS. Proces testowania pamięci pozostaje poniżej 60 MB podczas całego testu, zużywając tylko 7-8% czasu procesora.
UPDATE 2
Począwszy od wersji 1.7.2, pg-promise obsługuje super-masywne transakcji z łatwością. Zobacz rozdział Synchronous Transactions.
Na przykład mogę wrzucić 10 000 000 rekordów w pojedynczej transakcji w ciągu zaledwie 15 minut na moim komputerze domowym, z systemem Windows 8.1 w wersji 64-bitowej.
Dla testu ustawiłem komputer na tryb produkcji i użyłem Bluebird jako biblioteki obietnicy. Podczas testu zużycie pamięci nie przekroczyło 75 MB dla całego procesu NodeJS 0.12.5 (64-bit), podczas gdy mój procesor i7-4770 wykazywał stałe 15% obciążenia.
Wstawienie 100-metrowych rekordów w ten sam sposób wymagałoby więcej cierpliwości, ale nie więcej zasobów komputera.
W międzyczasie poprzedni test na wkładki 1m spadł z 1m43s do 1m31s.
UPDATE 3
Poniższe rozważania mogą stanowić ogromną różnicę: Performance Boost.
UPDATE 4
Powiązane pytanie, z lepszym przykład realizacji: Massive inserts with pg-promise.
UPDATE 5
lepsze i nowsze przykład można znaleźć tutaj: nodeJS inserting Data into PostgreSQL error
Dzięki. Zgadzam się, że morał tej historii brzmi "nie rób tego w ten sposób". FWIW skończyłem używając postgres COPY FROM. –
Mam rację, zakładając, że 'ten.sekwencja (fabryka), this.one ('wybierz liczbę (*) z testu')' nie da mi prawidłowej liczby, ponieważ nie mogę się upewnić, że sekwencja jest w całości napisana podczas uruchamiania kwerendy liczenia? – stephanlindauer
@stephanlindauer, kiedy sekwencja się rozstrzyga, rozwiązuje się z obiektem '{total, duration}', więc total jest twoją liczbą elementów przetwarzanych przez sekwencję. –