2

Sqlite データベースから膨大な量 (5 ~ 12 百万行) のアーカイブ データを取得して csv ファイルにエクスポートしたいと考えています。これを行っている間、サーバー全体がブロックされます。サーバーは他の接続を処理できません (たとえば、ブラウザーの別のタブで Web サイトを開くことができませんでした)。

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 DB モジュール:

 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                                                            
         );                                                                      
 };        

CREATE INDEX IAD ON AD(A, C) のように AD にインデックスも作成し、EXPLAIN QUERY PLAN は、このインデックスが sqlite エンジンによって使用されていることを示しています。

それでも、exportArchiveData サーバーを呼び出すと、データは適切に送信されますが、その間は他のアクションを実行できません。膨大な量のデータ (送信する行数が 5 ~ 1200 万行) があるため、3 分ほどかかります。

これがサーバー全体をブロックするのを防ぐにはどうすればよいですか?

EACHを使用し、コールバックがあると、サーバーの応答性が向上すると思いました。また、メモリ使用量が膨大です (約 3GB 以上)。どうにかしてこれを防ぐことはできますか?

コメントへの回答として、いくつかの説明を追加したいと思います。

developmentseed の node-sqlite3を使用しています。非同期でノンブロッキングでなければなりません。そしてそうです。声明が準備されたら、メインページをリクエストできます。しかし、サーバーがデータの提供を開始すると、Node.js サーバーがブロックされます。ホームページのリクエストは、コールバックを呼び出すための1つのリクエストであり、アーカイブデータ「各」を処理するコールバックのリクエストは無数にあるためだと思います。

Linux コマンド ラインから sqlite3 ツールを使用すると、すぐに行を取得できませんが、node-sqlite3 が非ブロッキングである限り、それは問題ではありません。

はい。私はCPU最大に達しています。さらに悪いことに、2 倍のデータを要求すると、メモリ全体が使用され、サーバーが永久にフリーズします。

4

1 に答える 1

1

わかった。私はこの問題をこのように扱います。

Database#each を使用する代わりに、複数の Statement#get で Database#prepare を使用します。

さらに、応答のバッファがいっぱいになったためにメモリが不足したことを調査しました。そのため、前の行を取得し、応答バッファーに新しいデータ用の場所がある場合に、次の行を呼び出します。完璧に動作します。そして今、サーバーはブロックされていません(ステートメントの準備中のみ)。

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 に答える