MySQL nodejs 在从大表中选择数据时崩溃

Posted

技术标签:

【中文标题】MySQL nodejs 在从大表中选择数据时崩溃【英文标题】:MySQL nodejs crash upon selecting data from big table 【发布时间】:2018-03-13 10:44:50 【问题描述】:

我正在尝试将数据从一个数据库转换到另一个数据库,但在尝试从大表中获取数据、将其保存到对象并插入另一个数据库时遇到了一些问题。这是我的代码:

let sql;
let resultsToFetch = true;
while (resultsToFetch) 
  sql = `SELECT X FROM Y LIMIT $index, 1000`;
  DB1.query(sql, (err, result) => 
    if (err) 
      resultsToFetch = false;
      throw err;
     else if (result.length == 0) 
      resultsToFetch = false;
     else 
      result.forEach(res => 
        const obj = 
          id: res.id,
          name: res.name
        ;
        sql = "INSERT INTO X SET ?";
        DB2.query(sql, obj, (err, result) => 
          if (err) throw err;
        );
      );
    
  );
  index += 1000;

我正在尝试使用 LIMIT,所以我没有立即选择所有 600 万个条目,但我仍然收到 javascript 堆内存不足错误。我想我误解了一些与 Node.js 相关的东西,但我不太确定它是什么。这是错误:

<--- Last few GCs --->

[11256:000002A5D2CBB600]    22031 ms: Mark-sweep 1418.5 (1482.0) -> 1418.5 (1451.5) MB, 918.3 / 0.0 ms  last resort GC in old space requested
[11256:000002A5D2CBB600]    22947 ms: Mark-sweep 1418.5 (1451.5) -> 1418.5 (1451.5) MB, 915.2 / 0.0 ms  last resort GC in old space requested


<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 000000B356525529 <JSObject>
    1: /* anonymous */ [\index.js:~1] [pc=00000042DA416732](this=000000C326B04AD1 <Object map = 0000027D35B023B9>,exports=000000C326B04AD1 <Object map = 0000027D35B023B9>,require=000000C326B04A89 <JSFunction require (sfi = 00000229888651E9)>,module=000000C326B04A39 <Module map = 0000027D35B44F69>,__filename=000002298886B769 <String[52]\

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::DecodeWrite
 2: node_module_register
 3: v8::internal::FatalProcessOutOfMemory
 4: v8::internal::FatalProcessOutOfMemory
 5: v8::internal::Factory::NewUninitializedFixedArray
 6: v8::internal::WasmDebugInfo::SetupForTesting
 7: v8::internal::interpreter::BytecodeArrayRandomIterator::UpdateOffsetFromIndex
 8: 00000042DA2843C1

编辑:@Grégory NEUT

let query = DB1.query("SELECT * FROM X");
let index = 0;
query
  .on("error", function(err) 
    // Handle error, an 'end' event will be emitted after this as well
  )
  .on("fields", function(fields) 
    // the field packets for the rows to follow
  )
  .on("result", function(row) 
    // Pausing the connnection is useful if your processing involves I/O
    DB1.pause();
    const obj = 
      id: row.id,
    ;
    console.log(obj);
    const sql = `INSERT INTO X SET ?`;
    DB2.query(sql, obj, (err, result) => 
      if (err) 
        throw err;
      
      DB1.resume();
    );
    console.log(index);
    index++;
  )
  .on("end", function() 
    // all rows have been received
  );

【问题讨论】:

批量运行,每 x 个项目调用一次方法 @Veve 你的意思是流作为下面的答案吗? 不,但流似乎确实更好。 【参考方案1】:

我不知道 mysql 驱动程序是如何在 node.js 中完成的,但 也许 它会加载所有内容,然后限制数据。或者可能 1000 个条目太多了。


无论如何解决方法是使用streams

var query = connection.query('SELECT * FROM posts');

query
  .on('error', function(err) 
    // Handle error, an 'end' event will be emitted after this as well
  )
  .on('fields', function(fields) 
    // the field packets for the rows to follow
  )
  .on('result', function(row) 
    // Pausing the connnection is useful if your processing involves I/O
    connection.pause();

    processRow(row, function() 
      connection.resume();
    );
  )
  .on('end', function() 
    // all rows have been received
  );

所以它一次只会将处理过的数据加载到内存中。使用它,您将确保无论您拥有多少数据,您都不会遇到分配失败。

【讨论】:

这似乎可以完成工作,但速度很慢,我在下面发布了代码。有什么建议吗? 我认为这需要很长时间是正常的,因为您正在使其同步(1 个条目,1 个推送,1 个条目,1 个推送......)。如果你让它异步它会更快,但你必须处理插入的结束和并行请求的数量。 好吧,我现在在 on('results') 回调中创建了一个 'insert statement' 字符串,它在每次将 1000 个插入附加到 'insert statement' 字符串时暂停执行,然后执行它并恢复- 重复直到完成。这似乎运行速度快了 100 倍。

以上是关于MySQL nodejs 在从大表中选择数据时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

从大表中选择非空字段

MySQL查询优化从大表中获取8-10条记录

优化从大表中选择

从大表中有效地选择不同的(a,b)

从大表中检索聚合数据的更快方法?

如何优化限制查询以更快地从大表中访问数据?