2

我想从 Sqlite 数据库中获取并导出大量(5 - 12 百万行)存档数据到 csv 文件。这样做时,整个服务器都被阻止了。服务器无法处理其他连接(例如,我无法在浏览器的另一个选项卡中打开网站)。

Node.JS 服务器部分:

function exportArchiveData(response, query){                                                                                                    
  response.setHeader('Content-type', 'text/csv');                             
  response.setHeader('Content-disposition', 'attachment; filename=archive.csv');                      
  db.fetchAllArchiveData(                                                     
      query.ID,                                                               
      function(error, data){
          if(!error)                                           
             response.write(data.A + ';' + data.B + ';' + data.C + '\n');           
      },                                                                      
      function(error, retrievedRows){
          response.end();                                                     
      });                                                                     
};            

Sqlite 数据库模块:

 module.exports.SS.prototype.fetchAllArchiveData = function (          
     a, callback, complete) {                                                  

     var self = this;                                                            

 //      self.sensorSqliteDb.all(                                                
         self.sensorSqliteDb.each(                                               
             'SELECT A, B, C '+                            
             'FROM AD WHERE '+                                          
             ' A="' + a + '"'+                                                
             ' ORDER BY C ASC' +                                         
             ';'                                                                 
             ,
             callback,                                                               
             complete                                                            
         );                                                                      
 };        

我还在 AD 上创建索引,如 CREATE INDEX IAD ON AD(A, C) 和 EXPLAIN QUERY PLAN 显示该索引由 sqlite 引擎使用。

尽管如此,当我调用 exportArchiveData 服务器正确发送数据但在此期间无法执行其他操作时。我有大量数据(要发送 5 到 12 百万行),所以大约需要 3 分钟。

我怎样才能防止它阻塞整个服务器?

我认为如果我使用 EACH 并且会有回调,服务器会响应更快。内存使用量也很大(大约 3GB 甚至更多)。我能以某种方式阻止这种情况吗?

在回答评论时,我想补充一些说明:

我使用developmentseed 的 node-sqlite3。它应该是异步和非阻塞的。它是。准备好声明后,我可以请求主页。但是当服务器开始提供数据时,Node.js 服务器被阻塞。我猜那是因为对主页的请求是一个调用某个回调的请求,而有数百万个请求回调处理存档数据“EACH”。

如果我从 linux 命令行使用 sqlite3 工具,我不会立即获得行,但只要 node-sqlite3 是非阻塞的,这不是问题。

是的。我正在达到 CPU 最大值。更糟糕的是,当我请求两倍的数据时,会使用整个内存,然后服务器永远冻结。

4

1 回答 1

1

好的。我就是这样处理这个问题的。

我没有使用 Database#each,而是使用 Database#prepare 和多个 Statement#get。

更重要的是,我调查内存不足是由响应缓冲区满引起的。所以现在,当我得到上一个并且响应缓冲区有新数据的位置时,我会调用下一行。工作完美。现在服务器没有被阻塞(仅在准备语句期间)。

Sqlite 模块:

module.exports.SS.prototype.fetchAllArchiveData = function (                  
 a) {                                                          

 var self = this;                                                                    
 var statement = self.Db.prepare(                                                                                          
         'SELECT A, B, C '+                                    
         'FROM AD WHERE '+                                                  
         ' A="' + a + '"'+                                                        
         ' ORDER BY C ASC' +                                                 
         ';'                                                                         
         ,                                                                           
         function(error){                                                            
             if(error != null){                                                      
                 console.log(error);                                                 
             }                                                                       
         }                                                                                                                                           
     );                                                                              
 return statement;                                                                   
};              

服务器端:

function exportArchiveData(response, query){                                    

 var respRet = null;                                                         
 var i = 0;                                                                  
 var statement = db.fetchAllArchiveData(                                     
     query.ID);                                                               
 var getcallback = function(err, row){                                       
     if(err != null){                                                        
         console.mylog(err);                                                 
         return;                                                             
     }                                                                       
     if(typeof(row) != 'undefined'){                                         
         respRet = response.write(row.A + ';' + row.B + ';' + row.C + '\n');
         console.log(i++ + ' ' + respRet);                                   
         if(respRet){                                                        
             statement.get(getcallback);                                     
         }else{                                                              
             console.log('should wait on drain');                            
             response.on('drain', function(){                                
                 console.log('drain - set on drain to null, call statement');
                 response.on('drain', function(){});                         
                 statement.get(getcallback);                                 
             });                                                             
         }                                                                   
     }else{                                                                  
         response.end();                                                     
     }                                                                       
 };                                                                          

 statement.get(function(err, row){                                           
     response.setHeader('Content-type', 'text/csv');                         
     response.setHeader('Content-disposition', 'attachment; filename=archive.csv');                   
     getcallback(err, row);                                                  
 });                                                                         
 };     
于 2013-06-21T12:06:37.743 回答