2017-02-08 1 views
1

안녕하세요 저는 기존 데이터베이스를 nodejs를 사용하여 새 데이터베이스로 마이그레이션하려고합니다.nodejs 데이터베이스 마이그레이션에 힙 공간이 부족합니다.

일부 레거시 데이터베이스 테이블은 백만 행에 가깝기 때문에 메모리 소비가 높습니다.

현재 스크립트에서 힙 메모리가 상당히 빠릅니다. 나는이 문제를 해결할 수 있기를 바랐지만, 사용 된 공간은 여전히 ​​반복적으로 커지고있다.

다음 코드는 기본적으로 이전 테이블을 쿼리하고 일부 필드를 매핑 한 다음 새 데이터베이스에 삽입합니다. 변수 선언을 루프 내부에서 외부로 이동하여 이전 변수가 덮어 쓰여지고 새로운 공간이 필요하게되기를 바랍니다. 또한 배열에 .pop을 사용하면 나머지 행에 필요한 공간을 계속 줄이기를 기대합니다.

그러나 이미 말했듯이 필요한 공간은 반복적으로 커지고 있습니다. 아무도 왜 그런 생각을 가지고 있니?

function migrate_user_table(callback) { 
    // Migrate user table 
    logger.log('info', "Starting migration of user table..."); 

    let row = null; 
    let userid = null; 
    let fullname = null; 
    let active = null; 
    let imagepath = null; 
    let statusbase64 = null; 
    let gender = null; 
    let orientation = null; 
    let reports = null; 
    let reviewStatus = null; 
    let region = null; 
    let newReviewStatus = null; 
    let newgender = null; 
    let neworientation = null; 
    let newregion = null; 
    let banned = null; 
    let lastActive = null; 
    let numberOfRequests = null; 
    let requestsSend = null; 
    let moji = null; 
    let created = null; 
    let minAgeS = null; 
    let maxAgeS = null; 
    let minAgeC = null; 
    let maxAgeC = null; 
    let genderS = null; 
    let orientS = null; 
    let genderC = null; 
    let newgenderS = null; 
    let neworientS = null; 
    let newgenderC = null; 
    let user = null; 
    let user_has_social = null; 
    let user_has_data_username = null; 
    let user_has_data_status = null; 
    let user_has_data_report = null; 
    let user_has_data_sent = null; 
    let user_has_data_recv = null; 
    let user_has_moji = null; 
    let user_has_filter_searchage = null; 
    let user_has_filter_chatage = null; 
    let user_has_filter_searchgender = null; 
    let user_has_filter_chatgender = null; 
    let user_has_filter_searchorient = null; 

    legacy.query('SELECT * FROM user u LEFT JOIN behavior b ON (u.userid = b.userid) LEFT JOIN filter f ON (u.username = f.username)', (error, results) => { 
     if(error) throw error; 
     while (results.length > 0) { 
      row = results.pop(); 

      userid = row["userid"]; 
      kikname = row["username"]; 
      fullname = row["fullname"]; 
      active = row["active"]; 
      imagepath = row["img"]; 
      statusbase64 = row["status"]; 
      gender = parseInt(row["gender"]); 
      orientation = row["orientation"]; 
      reports = row["reports"]; 
      reviewStatus = parseInt(row["reviewStatus"]); 
      region = row["region"]; 

      // map to new reviewstatus 
      newReviewStatus = 1; 
      switch (reviewStatus) { 
       case 0 : 
        newReviewStatus = 1; 
        break; 
       case 1 : 
        newReviewStatus = 3; 
        break; 
       case 2 : 
        newReviewStatus = 4; 
        break; 
       case -1 : 
        newReviewStatus = 2; 
        break; 
      } 

      // map to new gender, orientation and region 
      newgender = gender +1; 
      neworientation = orientation +1; 
      newregion = 7; 
      if(region >= 0) { 
       newregion = region +1; 
      } 

      banned = row["banned"]; 
      lastActive = row["pendingSince"]; 
      numberOfRequests = row["numberOfRequests"]; 
      requestsSend = row["requestsSend"]; 
      moji = row["moji_idmoji"]; 
      created = row["created"]; 
      minAgeS = row["minAgeS"]; 
      maxAgeS = row["maxAgeS"]; 
      minAgeC = row["minAgeC"]; 
      maxAgeC = row["maxAgeC"]; 

      genderS = row["genderS"]; 
      orientS = row["orientS"]; 
      genderC = row["genderC"]; 

      newgenderS = genderS + 1; 
      if(newgenderS === 0) { 
       newgenderS = null; 
      } 

      neworientS = orientS + 1; 
      if(neworientS === 0) { 
       neworientS = null; 
      } 

      newgenderC = genderC + 1; 
      if(newgenderC === 0) { 
       newgenderC = null; 
      } 

      user = {iduser : userid, imageurl : imagepath, birthdate : null, active : active, banned : banned, reviewstatus_idreviewstatus : newReviewStatus, last_active : lastActive, 
       created : created, gender_idgender : newgender, orientation_idorientation : neworientation, region_idregion : newregion}; 
      connection.query('INSERT INTO user SET ?', user, (error) => { 
       if(error) throw error; 
       logger.log('debug', "User Insert successfull"); 
      }); 

      user_has_social = {user_iduser : userid, socialtype_idsocialtype : 1, value : kikname}; 
      connection.query('INSERT INTO user_has_social SET ?', user_has_social, (error) => { 
       if(error) throw error; 
       logger.log('debug', "User_has_social Insert successfull"); 
      }); 

      user_has_data_username = {user_iduser : userid, datatype_iddatatype : 5, value : fullname}; 
      user_has_data_status = {user_iduser : userid, datatype_iddatatype : 1, value : statusbase64}; 
      user_has_data_report = {user_iduser : userid, datatype_iddatatype : 7, value : reports}; 
      user_has_data_sent = {user_iduser : userid, datatype_iddatatype : 4, value : requestsSend}; 
      user_has_data_recv = {user_iduser : userid, datatype_iddatatype : 3, value : numberOfRequests}; 
      datainsert(connection, user_has_data_username); 
      datainsert(connection, user_has_data_status); 
      datainsert(connection, user_has_data_report); 
      datainsert(connection, user_has_data_sent); 
      datainsert(connection, user_has_data_recv); 

      user_has_moji = {user_iduser : userid, moji_idmoji : moji}; 
      connection.query('INSERT INTO user_has_moji SET ?', user_has_moji, (error) => { 
       if(error) throw error; 
       logger.log('debug', "User_has_moji" + 
        " Insert successfull"); 
      }); 

      user_has_filter_searchage = { user_iduser : userid, filtertype_idfiltertype : 1, value : minAgeS, add_value : maxAgeS}; 
      user_has_filter_chatage = { user_iduser : userid, filtertype_idfiltertype : 2, value : minAgeC, add_value : maxAgeC}; 
      user_has_filter_searchgender = { user_iduser : userid, filtertype_idfiltertype : 3, value : newgenderS, add_value : null}; 
      user_has_filter_chatgender = { user_iduser : userid, filtertype_idfiltertype : 4, value : newgenderC, add_value : null}; 
      user_has_filter_searchorient = { user_iduser : userid, filtertype_idfiltertype : 5, value : neworientS, add_value : null}; 
      filterinsert(connection, user_has_filter_searchage); 
      filterinsert(connection, user_has_filter_chatage); 
      filterinsert(connection, user_has_filter_searchgender); 
      filterinsert(connection, user_has_filter_chatgender); 
      filterinsert(connection, user_has_filter_searchorient); 

      logger.log('debug', results.length + " row to go"); 
     } 
     callback(); 
    }); 
} 
+0

코드에 문제가있을 수 있다는 점 외에도 많은 양의 결과를 가져 오는 것을 고려할 때 메모리가 부족한 것이 정상이라고 생각합니다. "yourScript your node"를 사용하여 메모리를 늘릴 수 있습니다.js - max_old_space_size = 4096 "(숫자는 원하는 메가 바이트 수입니다) – yBrodsky

답변

2

당신이 query(stmt, function(error, results) {...}) 부하 RAM에 기존 테이블에서 설정 한 전체 결과를 사용하는 방법. 그런 다음 해당 결과 집합의 내용을 행 단위로 반복합니다 (행을 pop으로 가져 오는 것).

SQL의 목적이 RAM에 비해 너무 큰 데이터 양을 처리한다는 점을 고려하면 놀랍지 않습니다. 소진하다.

RAM 낭비가 있습니다. SELECT *. SELECT userid, username, ...으로 필요한 열만 열거하면 행이 짧아 지므로 RAM에 더 많은 수를 넣을 수 있습니다.

하지만 문제가 해결되지 않을 수도 있습니다. 지연하십시오.

두 가지 사항이 있습니다. 하나는 청크로 레거시 테이블을 처리하는 것입니다.

예를 들어 청크로 데이터를 검색 할 수 있습니다. 당신은 이러한 쿼리

SELECT whatever ORDER BY user_id LIMIT 1000 OFFSET 1000 
    SELECT whatever ORDER BY user_id LIMIT 1000 OFFSET 2000 

이 쿼리

SELECT whatever ORDER BY user_id LIMIT 1000 OFFSET 0 

하고 다음 덩어리 최초의 덩어리를 얻을이 당신에게 천 개 행 각각의 덩어리를 제공합니다. 행을 가져올 때까지 계속 가십시오.

두 번째 선택 : 결과 집합의 행을 하나씩 스트리밍합니다. 이렇게하려면 query()을 사용하기 위해 약간 다른 설정이 필요합니다. 여기에 적혀 있습니다. https://github.com/mysqljs/mysql#streaming-query-rows

은 기본적으로는 다음과 같이 진행됩니다

var stream = legacy.query('SELECT whatever'); 
stream 
    .on('result', function(row) { 
     // Pausing the connnection is useful if your processing involves I/O 
     legacy.pause(); 
     // handle your row of data here... 
     legacy.resume(); 
    }) 
    .on('end', function() { 
    // all rows have been received 
    }); 

이 한 번에 데이터 행을 처리 할 수있게된다.

+0

고마워요 스트리밍 방식을 시도하고 도움이되었습니다. 그러나 어떤 이유로 인서트를하기 전에 모든 행을 반복하고 있습니다. 필요한 공간이 여전히 너무 커지는 이유가 될 수 있습니다. –

관련 문제