我使用Node将200万行从SQL Server复制到另一个数据库,所以当然我使用“streaming”选项,如下所示:
const sql = require('mssql')
...
const request = new sql.Request()
request.stream = true
request.query('select * from verylargetable')
request.on('row', row => {
promise = write_to_other_database(row);
})
我的问题是我已经对每一行进行了异步操作(插入另一个数据库),这需要时间。
读取速度比写入速度快,因此“on row”事件不断发生,内存最终填满待处理的Promises,并最终导致Node崩溃。这令人沮丧 - “流媒体”的重点是避免这种情况,不是吗?
我该如何解决这个问题?
答案 0 :(得分:1)
问题似乎是由于使用“行”事件读取流而导致的,这些事件不允许您控制流的流。这应该可以通过“管道”方法实现,但随后你最终会进入数据流并实现可写流 - 这可能很棘手。
一个简单的解决方案是使用Scramjet,这样您的代码就可以完成几行:
const sql = require('mssql')
const {DataStream} = require("scramjet");
//...
const request = new sql.Request()
request.stream = true
request.query('select * from verylargetable')
request.pipe(new DataStream({maxParallel: 1}))
// pipe to a new DataStream with no parallel processing
.batch(64)
// optionally batch the requests that someone mentioned
.consume(async (row) => write_to_other_database(row));
// flow control will be done automatically
Scramjet将使用promises来控制流量。您也可以尝试增加maxParallel方法,但请记住,在这种情况下,最后一行可以同时开始推送行。
答案 1 :(得分:0)
我自己的答案:我不是同时写入目标数据库,而是将每一行转换为“insert”语句,并将语句推送到消息队列(RabbitMQ,一个单独的进程)。这很快,并且可以跟上阅读速度。另一个节点进程从队列中拉出(更慢)并写入目标数据库。因此,行的大“后退日志”由消息队列本身处理,这很好。
答案 2 :(得分:0)
sql.connect(config, err => {
if (err) console.log(err);
const request = new sql.Request();
request.stream = true; // You can set streaming differently for each request
request.query('select * from dbo.YourAmazingTable'); // or
request.execute(procedure)
request.on('recordset', columns => {
// Emitted once for each recordset in a query
//console.log(columns);
});
let rowsToProcess = [];
request.on('row', row => {
// Emitted for each row in a recordset
rowsToProcess.push(row);
if (rowsToProcess.length >= 3) {
request.pause();
processRows();
}
console.log(row);
});
request.on('error', err => {
// May be emitted multiple times
console.log(err);
});
request.on('done', result => {
// Always emitted as the last one
processRows();
//console.log(result);
});
const processRows = () => {
// process rows
rowsToProcess = [];
request.resume();
}