Node.JS 将数据库<oracledb@4.0.1>返回的数据结构化为前端需求的结构,发现5000多条数据居然用了24秒,求推荐<并行>姿势
发布于 10 个月前 作者 LeavesSky 4837 次浏览 来自 问答

环境: nodejs@V12.6 、oracledb@4.0.1、oracle11g

试了 1. 将结构化封装在函数里执行,伪代码如下:

	exports.getSomethingByID = async ( params ) => { 
		let formatData = [];
		//..........
		try {
			let rel = connection.execute( sql, params, options);
			async function ceshi ( ) { 
				//......结构化的逻辑
				formatData.push( data );
			}
	
			for ( let i = 0; i < rel.rows.length, i++){
				await ceshi( rel.rows[i] );
			}
			return formatData
		}catch (error) {
   			console.log(error.stack);
    		return [];
  		} finally {
 			if (connection) {
     			 try {
        			await connection.close();
      			} catch (err) {
        			console.log('Error in closing connection:\n', "Module - ", module.id, " : \n", err);
      			}
    		}
		}
	}
  1. 用process.nextTick( ceshi, ele); 发现事件居然排在了finally后面; ( 事件循环基础太差 ); 求帮忙!!
22 回复

大佬们给个建议,或者学习方向; 最近在看nodejs设计模式和 狼叔的 卷一 ,都感觉吃力( 半路出家, 基础停留在吹牛皮的大概念上 );

新的写法, 依旧不行image.png

ceshi() 这个方法是调用远程服务还是本应用(进程)的一个方法?

@waitingsong 一些数据结构化( oracle 的spitial 数据query(oracledb.connection.execute(sql)) -> DBObject -> porxy -> object(JSON) )和计算

这样看看

const rel = connection.execute(sql, params, options);
const pmArr = rel.rows.map(row => ceshi(row))
return Promise.all(pmArr)


async function ceshi(row) {
  //......结构化的逻辑
  return data
}

rel.rows.map 会导致高并发。如果需要控制并发数量,可考虑使用 rxjsmergeMap 操作符(第二个参数)。 如果是单纯的数据计算(不涉及连接数据库等环境变量),可使用 runscript 的方式通过 fork 进程来多进程并发计算。

@ waitingsong 查到关键问题了浪费时间的就是oracledb的DBObject的一个方法toJSON 我空间数据是直接取出的( 存量数据有问题, 只能直接取了 ), 然后在 nodejs里面返回的空间数据是DBObject的实例, 然后我调用toJSON( ), 一个数据转换花了4-5ms; 我觉得官方的这个接口有问题( 我自己也写了个, 贼垃圾 6-8ms); 明天我再试试( promise好久没用了,忘了这货 );

@waitingsong image.png Promise.all和我上面写的效果差不多, 依然进不了10秒;

有没有注意过内存? 一条记录数据量有多少, 字段多么? 可以试试拼接json字符串, 数据量大 最好是用全双工的流来做 还可以减少一次序列化的过程.

@cnwhy 内存不很小, 可以忽略, 查出的3000条数据, 大概3mb不到; 至于拼接JSON字符串, 我测试过, 依然需要调用, DBObject的类的方法才能获取字符串, 本质差别不大(我甚至 JSON.parse(JSON.stringify()), 但更慢) 我查了下DBObject 是一个 普通的Object 的 proxy 也就是代理类, 因此, 我现在有点怀疑, ES6的proxy的性能; 晚上回去, 我在查下, OracleDB 的 OCI 出来的数据到底是什么, 看能不能有所收获.

@LeavesSky 序列化和反序列算是CPU密集型任务, 就算是异步并发也不会有多少改善. 用流来处理不会降低耗时 , 但可以加快反应时间

exports.getSomethingByID = params => {
	let rel = connection.execute(sql, params, options);
	let index = 0;
	// 如果ceshi是同步方法,就不要用 async
	async function ceshi() {
		//......结构化的逻辑
		return data;
	}
	async function read(size) {
		let self = this;
		if (index >= rel.rows.length) self.push(null); //流结束

		// 拼接json字符串
		let str = index === 0 ? '[' : '';
		str += JSON.stringify(await ceshi());
		str += index < rel.rows.length ? ',' : ']';
		index++;
		self.push(str);
	}
	var outs = new stream.Readable({
		destroy: function(err, callback) {
			callback(err);
		}
	});
	outs._read = read;
	return outs;   // 到时候 outs.pipe(res); 转给浏览器
};

为啥把 ceshi() ceshi2() 方法写在函数内部呢? (虽然不会影响性能)

序列化属于CPU密集型,你这个应该考虑用 worker 多线程来处理。

另外 json 不大适合处理超大(行)数量的数据。这种场景下 xml 有优势。

@cnwhy 流式处理 ; 我之前也想过, 但是我前端框架<openlayers 地图 >应该不支持流式加载 ( 一堆数据瞬间展示出来的, 不是管道一点一点出来的, 我仔细想了下, 此框架未做流式加载的原因, 是要做数据缓存<地图的矢量数据>, 我打算这个项目结束, 看下他的缓存方式, 争取扩展一个流式的接口 );

@waitingsong 为啥把 ceshi() ceshi2() 方法写在函数内部呢? (虽然不会影响性能) 因为在最外层我初始化了一个数据, 用于在ceshi() 中的结果记录; 多层async function 主要为了将同步的结构化和计算转入异步事件队列; 最后利用finally的机制返回出整个结果集; 我对多线程有阴影(以前玩易语言, 手撸多线程, 贼恐怖, nodejs 的新增的一直排斥, 晚上, 瞅瞅看其封装的怎么样; xml方向也试试

序列化和反序列算是CPU密集型任务 关于上述的知识, 是需要学习哪个方向(数据结构), 我是因爱好, 半路出家, 计算机基础匮乏, 最近也在想安排一下时间学习数据结构

lz确认下耗时主要在哪个方面。 因为最后你在输出结果(5k条数据)为 json 时也要做一次序列化(js对象到json字符串)这个也有耗时。 如果耗时主要在 ceshi 这个函数调用上,那就可以用 js 的 worker (nodejs 从 v12开始默认支持) 来多线程执行此调用从而提高速度。

找了个线程池模块: node-worker-threads-pool : https://www.npmjs.com/package/node-worker-threads-pool; 干了下, 发现报错了, 单步进去调试, 发现, Oracledb 的 DBObject 对象< 数据库返回的SHAPE字段对应的实例化值, 作为参数 >, 在进去 worker后,被强转为普通的对象<object : { }> ,多线程的代码我没跟的进去, 我现在下载node源码, 看下 worker 的执行机制; 还有一点; 令我诧异的是: 该线程池的任务执行, worker一直无值<isReady === false> , 可能是暂时没搞懂大牛的逻辑, 具体如下图 image.png

@waitingsong @cnwhy 此问题, 最终查出是: 单例模式返回单一实例+密集计算的问题,oracledb中的oci返回的是一个挂载在单一实例connection的结果集( 此结果集, 对空间类型字段,已经进行了结构化处理<DBObject>); 因此 多线程或异步队列无法解决这个问题; 我目前优化的方式是, 在oci的接口处,对row数据进行一次 JSON.parse(JSON.stringify(rows)); 与此同时, 提交了issue; 得到的回复如下: image.png 期待下个版本能够解决 咯

不知道 SharedArrayBuffer 是否能解决在多线程场景下的资源共享。

@waitingsong 不理解您说的方向 ! 目前想法就是多接口分流, 减少单一SQL返回的数据量

回到顶部