sql >> Base de Datos >  >> RDS >> PostgreSQL

node-postgres con una gran cantidad de consultas

ACTUALIZAR

Desde entonces, esta respuesta se reemplazó con este artículo:Importaciones de datos , que representa el enfoque más actualizado.

Para replicar su escenario, utilicé pg-promise biblioteca, y puedo confirmar que intentarlo de frente nunca funcionará, sin importar qué biblioteca use, lo que importa es el enfoque.

A continuación se muestra un enfoque modificado en el que particionamos las inserciones en fragmentos y luego ejecutamos cada fragmento dentro de una transacción, que es equilibrio de carga (también conocido como limitación):

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

Esto produjo 1.000.000 registros en aproximadamente 4 minutos, lo que se desaceleró drásticamente después de las primeras 3 transacciones. Estaba usando Node JS 0.10.38 (64 bits), que consumía alrededor de 340 MB de memoria. De esta forma insertamos 100.000 registros, 10 veces seguidas.

Si hacemos lo mismo, solo que esta vez insertamos 10,000 registros dentro de 100 transacciones, los mismos 1,000,000 de registros se agregan en solo 1m25s, sin ralentizar, con Node JS consumiendo alrededor de 100 MB de memoria, lo que nos dice que particionar datos como este es muy buena idea.

No importa qué biblioteca use, el enfoque debe ser el mismo:

  1. Dividir/acelerar sus inserciones en múltiples transacciones;
  2. Mantener la lista de inserciones en una sola transacción en alrededor de 10 000 registros;
  3. Ejecute todas sus transacciones en una cadena síncrona.
  4. Libere la conexión de nuevo al grupo después del COMMIT de cada transacción.

Si rompe alguna de esas reglas, tiene problemas garantizados. Por ejemplo, si infringe la regla 3, es probable que su proceso de Node JS se quede sin memoria muy rápido y genere un error. La regla 4 en mi ejemplo fue proporcionada por la biblioteca.

Y si sigue este patrón, no necesita preocuparse por la configuración del conjunto de conexiones.

ACTUALIZACIÓN 1

Versiones posteriores de pg-promise admite tales escenarios perfectamente, como se muestra a continuación:

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

y si no desea incluir nada adicional, como la creación de tablas, parece aún más simple:

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

Consulte Transacciones síncronas para detalles.

Usando Bluebird como la biblioteca de promesa, por ejemplo, mi máquina de producción tarda 1 minuto y 43 segundos en insertar 1 000 000 de registros (sin habilitar los seguimientos de pila larga).

Solo tendrías tu factory solicitudes de devolución de métodos de acuerdo con el index , hasta que no te quede nada, así de simple.

Y la mejor parte, esto no solo es rápido, sino que también genera poca carga en su proceso de NodeJS. El proceso de prueba de memoria permanece por debajo de los 60 MB durante toda la prueba, consumiendo solo entre el 7 y el 8 % del tiempo de la CPU.

ACTUALIZACIÓN 2

A partir de la versión 1.7.2, pg-promise admite transacciones súper masivas con facilidad. Consulte el capítulo Transacciones síncronas .

Por ejemplo, podría insertar 10 000 000 de registros en una sola transacción en solo 15 minutos en la PC de mi hogar, con Windows 8.1 de 64 bits.

Para la prueba, puse mi PC en modo de producción y usé Bluebird como la biblioteca de la promesa. Durante la prueba, el consumo de memoria no superó los 75 MB para todo el proceso de NodeJS 0.12.5 (64 bits), mientras que mi CPU i7-4770 mostró una carga constante del 15 %.

Insertar 100 millones de registros de la misma manera requeriría más paciencia, pero no más recursos informáticos.

Mientras tanto, la prueba anterior para insertos de 1 m se redujo de 1 min 43 s a 1 min 31 s.

ACTUALIZACIÓN 3

Las siguientes consideraciones pueden marcar una gran diferencia:Performance Boost .

ACTUALIZACIÓN 4

Pregunta relacionada, con un mejor ejemplo de implementación:Inserciones masivas con pg-promise .

ACTUALIZACIÓN 5

Puede encontrar un ejemplo mejor y más nuevo aquí:nodeJS inserting Data en el error de PostgreSQL