Запрос большого набора данных в базе данных Oracle от NodeJS

В настоящее время я работаю над проектом с работы, где у меня есть таблица базы данных Oracle 10, где около 310 тыс. Дают или принимают 10-30 тыс. Строк.

objective состоит в том, чтобы отображать эти строки в угловом интерфейсе, однако возrotation всех из них через NodeJS занимает много времени.

Учитывая, что я использую как NodeJS, так и oracledb в первый раз, я предполагаю, что я должен что-то упустить?

var oracledb = require('oracledb'); var config = require(__dirname+'/../db.js'); function get(req,res,next) { var table = req.query.table; var meta; oracledb.getConnection(config.oracle) .then( function(connection) { var stream = connection.queryStream('SELECT * FROM '+table); stream.on('error', function (error) { console.error(error); return next(err); }); stream.on('metadata', function (metadata) { console.log(metadata); }); stream.on('data', function (data) { console.log(data); }); stream.on('end', function () { connection.release( function(err) { if (err) { console.error(err.message); return next(err); } }); }); }) .catch(function(err){ if(err){ connection.close(function(err){ if(err){ console.error(err.message); return next(err); } }); } }) } module.exports.get = get; 

    30 МБ – это много данных для загрузки в интерфейс. Он может работать в некоторых случаях, например, в настольных веб-приложениях, где преимущества «кэширования» данных компенсируют время, необходимое для его загрузки (и увеличение устаревших данных в порядке). Но это не сработает в других случаях, таких как мобильные.

    Имейте в виду, что 30 МБ необходимо перенести из БД в Node.js, а затем из Node.js в клиент. Сетевые соединения между ними значительно повлияют на производительность.

    Я укажу несколько вещей, которые могут помочь в производительности, хотя не все точно связаны с этим вопросом.

    Во-первых, если вы используете веб-сервер, вы должны использовать пул соединений, а не выделенные / разовые соединения. Как правило, вы создадите пул соединений в вашем index / main / app.js и запустите веб-сервер после того, как это будет сделано и готово.

    Вот пример:

     const oracledb = require('oracledb'); const express = require('express'); const config = require('./db-config.js'); const thingController = require('./things-controller.js'); // Node.js used 4 background threads by default, increase to handle max DB pool. // This must be done before any other calls that will use the libuv threadpool. process.env.UV_THREADPOOL_SIZE = config.poolMax + 4; // This setting can be used to reduce the number of round trips between Node.js // and the database. oracledb.prefetchRows = 10000; function initDBConnectionPool() { console.log('Initializing database connection pool'); return oracledb.createPool(config); } function initWebServer() { console.log('Initializing webserver'); app = express(); let router = new express.Router(); router.route('/things') .get(thingController.get); app.use('/api', router); app.listen(3000, () => { console.log('Webserver listening on localhost:3000'); }); } initDBConnectionPool() .then(() => { initWebServer(); }) .catch(err => { console.log(err); }); 

    Это создаст пул, который добавляется во внутренний кеш пула в драйвере. Это позволяет вам легко получить доступ к нему из других модhive (пример позже).

    Обратите внимание, что при использовании пулов соединений обычно рекомендуется увеличить пул streamов, ansible для Node.js, чтобы позволить каждому соединению в пуле работать одновременно. Пример этого приведен выше.

    Кроме того, я увеличиваю значение oracledb.prefetchRows . Этот параметр напрямую связан с вашим вопросом. Сетевые круглые поездки используются для перемещения данных между БД и Node.js. Этот параметр позволяет вам отрегулировать количество строк, полученных с каждой поездки в оба конца. Так как prefetchRows повышается, требуется меньшее количество раундов, и производительность возрастает. Просто будьте осторожны, чтобы вы не переходили на высокий уровень в соответствии с вашей памятью на вашем сервере Node.js.

    Я провел общий тест, издевавшийся над размером набора 30 Мбайт. Когда oracledb.prefetchRows осталось по умолчанию 100, тест заканчивается через 1 минуту 6 секунд. Когда я столкнулся с этим до 10 000, он закончил за 27 секунд.

    Хорошо, перейдем к «things-controller.js», который основан на вашем коде. Я обновил код, чтобы сделать следующее:

    • Утвердить, что таблица является допустимым именем таблицы. Ваш текущий код уязвим для SQL-инъекции.
    • Используйте цепочку обещаний, которая эмулирует блок try / catch / finally, чтобы закрыть соединение только один раз и вернуть первую обнаруженную ошибку (при необходимости).
    • Работайте, чтобы я мог выполнить тест.

    Вот результат:

     const oracledb = require('oracledb'); function get(req, res, next) { const table = req.query.table; const rows = []; let conn; let err; // Will store the first error encountered // You need something like this to preven SQL injection. The current code // is wide open. if (!isSimpleSqlName(table)) { next(new Error('Not simple SQL name')); return; } // If you don't pass a config, the connection is pulled from the 'default' // pool in the cache. oracledb.getConnection() .then(c => { return new Promise((resolve, reject) => { conn = c; const stream = conn.queryStream('SELECT * FROM ' + table); stream.on('error', err => { reject(err); }); stream.on('data', data => { rows.push(data); }); stream.on('end', function () { resolve(); }); }); }) .catch(e => { err = err || e; }) .then(() => { if (conn) { // conn assignment worked, need to close/release conn return conn.close(); } }) .catch(e => { console.log(e); // Just log, error during release doesn't affect other work }) .then(() => { if (err) { next(err); return; } res.status(200).json(rows); }); } module.exports.get = get; function isSimpleSqlName(name) { if (name.length > 30) { return false; } // Fairly generic, but effective. Would need to be adjusted to accommodate quoted identifiers, // schemas, etc. if (!/^[a-zA-Z0-9#_$]+$/.test(name)) { return false; } return true; } 

    Надеюсь, это поможет. Дайте мне знать, если у вас есть вопросы.