카테고리 없음

AWS DMS (Database Migration Service)를 못믿어 데이터베이스 테이블 비교 코드를 만들어 보았습니다.

jhansol 2025. 7. 9. 00:34

안녕하세요?
정말 오랜만에 블로그에 글을 올립니다. 그 동안 바쁘다는 핑계로 기억을 박제하지 못했네요. ㅠㅠ

저는 요즘 개발 업무보다 회사의 운영 방침에 따라 AWS 인프라 관리, 마이그레이션관련 일에 몰두하고 있습니다. 개발 업무를 하고픈데, 업무가 이상하게 흘러가는 듯 합니다. 암튼 지금은 이 일을 하고 있습니다.

마이그레이션 작업에서 가장 신경이 쓰이는 부분이 운영 중인 데이터베이스를 다른 서버 또는 다른 계정으로 마이그레이션 하는 부분이라고 생각합니다. 지금 이 작업을 하고 있는데, 몇일 전 하나의 프로젝트(서비스) 데이터를 이동하는 중 데이터가 모두 복재가 되지 않아 소스에서 덤프를 뜨서 대상 데이터베이스에 복원한 적이 있습니다. 이 후로 DMS로 마이그레이션하고, 복제하는 작업을 신뢰할 수 없게 되었습니다.

그래서 만들어 봤습니다.

아래의 코드는 간단하게 ChatGPT를 이용하여 코드를 작성하고, 약간 수정한 것입니다. 잘 동작합니다.

const mariadb = require('mariadb');

const db1Config = {
  host: '127.0.0.1',
  port: 63306,            // ✅ 포트 번호 추가
  user: 'admin',
  password: 'xxxxxxxx!',
  database: 'xxxxxxxx',
};

const db2Config = {
  host: '127.0.0.1',
  port: 33306,            // ✅ 포트 번호 추가
  user: 'admin',
  password: 'xxxxxxx!',
  database: 'xxxxxxx',
};

async function getTableRowCounts(pool, database) {
  const conn = await pool.getConnection();
  try {
    const tables = await conn.query(`
      SELECT table_name
      FROM information_schema.tables
      WHERE table_schema = ?
    `, [database]);

    const counts = {};
    for (const row of tables) {
      const table = row.table_name;
      const result = await conn.query(`SELECT COUNT(*) as count FROM \`${table}\``);
      counts[table] = result[0].count;
    }
    return counts;
  } finally {
    conn.release();
  }
}

async function compareDatabases() {
  const pool1 = mariadb.createPool(db1Config);
  const pool2 = mariadb.createPool(db2Config);

  try {
    const [counts1, counts2] = await Promise.all([
      getTableRowCounts(pool1, db1Config.database),
      getTableRowCounts(pool2, db2Config.database),
    ]);

    const allTables = new Set([...Object.keys(counts1), ...Object.keys(counts2)]);
    let identical = true;

    for (const table of allTables) {
      const count1 = counts1[table];
      const count2 = counts2[table];

      if (count1 === undefined) {
        console.log(`❌ Table '${table}' missing in DB1`);
        identical = false;
      } else if (count2 === undefined) {
        console.log(`❌ Table '${table}' missing in DB2`);
        identical = false;
      } else if (count1 !== count2) {
        console.log(`❌ Table '${table}' row count differs: DB1=${count1}, DB2=${count2}`);
        identical = false;
      } else {
        console.log(`✅ Table '${table}' matches with ${count1} rows`);
      }
    }

    console.log('\n✅ Databases are ' + (identical ? 'identical' : 'different') + ' in structure and row count.');
  } catch (err) {
    console.error('Error:', err);
  } finally {
    await pool1.end();
    await pool2.end();
  }
}

compareDatabases();

그리고 package.json은 아래와 같습니다.

{
  "name": "db_compare",
  "version": "1.0.0",
  "description": "Compare database",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "mariadb": "^3.4.4"
  }
}

잘 돌아갑니다. 그리고 테이블 이름과 테이블에 저장된 레코드 수가 모두 일치한다고 나요네요. 이로서 두 데이터베이스는 동일하다고 볼 수 있을 것 같습니다.

그리고 두 데이터베이스는 AWS 프라이빗 서브넷에 있어 SSH 명령을 이용하여 터널링하여 접속해서 체크했습니다.
결과는 아래와 같습니다.

node index.js
✅ Table 'dx_lecture_enrollments' matches with 0 rows
✅ Table 'member_companies' matches with 1 rows
✅ Table 'online_edu_lecture_study_logs' matches with 0 rows
✅ Table 'password_resets' matches with 0 rows
✅ Table 'performance_dx_job_transition_rates' matches with 0 rows
✅ Table 'experience_images' matches with 0 rows
✅ Table 'metabus_teaching_plans' matches with 0 rows
✅ Table 'ncs_code' matches with 13351 rows
✅ Table 'base_inspects' matches with 0 rows
✅ Table 'migrations' matches with 97 rows
✅ Table 'resume_activities' matches with 0 rows
✅ Table 'personal_access_tokens' matches with 0 rows
✅ Table 'online_edu_contents' matches with 0 rows
✅ Table 'type_inspects' matches with 0 rows
✅ Table 'failed_jobs' matches with 0 rows
✅ Table 'competences' matches with 0 rows
✅ Table 'metabus_classes' matches with 0 rows
✅ Table 'dx_lecture_schedules' matches with 17 rows
✅ Table 'univs' matches with 4 rows
✅ Table 'community_files' matches with 4 rows
✅ Table 'experience_folders' matches with 0 rows
✅ Table 'expert_certifications' matches with 0 rows
✅ Table 'users' matches with 13 rows
✅ Table 'member_company_representative_history' matches with 0 rows
✅ Table 'admins' matches with 4 rows
✅ Table 'metabus_class_times' matches with 0 rows
✅ Table 'report_logs' matches with 0 rows
✅ Table 'digital_badges' matches with 4 rows
✅ Table 'performance_adult_friendly_edu_indexes' matches with 0 rows
✅ Table 'inspect_questions' matches with 256 rows
✅ Table 'resume_languages' matches with 0 rows
✅ Table 'digital_badge_issued_users' matches with 0 rows
✅ Table 'univ_admins' matches with 6 rows
✅ Table 'recruit_job_codes' matches with 1297 rows
✅ Table 'communities' matches with 8 rows
✅ Table 'metabus_class_logs' matches with 0 rows
✅ Table 'performances' matches with 48 rows
✅ Table 'resume_certificates' matches with 0 rows
✅ Table 'metabus_matterports' matches with 4 rows
✅ Table 'expert_histories' matches with 0 rows
✅ Table 'resumes' matches with 0 rows
✅ Table 'resume_careers' matches with 0 rows
✅ Table 'cache' matches with 60 rows
✅ Table 'resume_educations' matches with 0 rows
✅ Table 'ncs_licenses' matches with 21299 rows
✅ Table 'inspect_answers' matches with 0 rows
✅ Table 'dx_lectures' matches with 4 rows
✅ Table 'performance_dx_governance_indexes' matches with 0 rows
✅ Table 'online_edu_content_media' matches with 0 rows
✅ Table 'self_inspects' matches with 0 rows
✅ Table 'performance_active_job_transition_indexes' matches with 0 rows
✅ Table 'metabus_class_users' matches with 0 rows
✅ Table 'cache_locks' matches with 0 rows
✅ Table 'resume_technologies' matches with 0 rows
✅ Table 'digital_badge_configs' matches with 3 rows
✅ Table 'performance_dx_stakeholder_satisfactions' matches with 0 rows
✅ Table 'dx_categories' matches with 21 rows
✅ Table 'sessions' matches with 59 rows
✅ Table 'member_company_certification_status' matches with 2 rows
✅ Table 'recruit_infos' matches with 64099 rows
✅ Table 'performance_dx_employed_startup_rates' matches with 0 rows
✅ Table 'resume_self_introductions' matches with 0 rows
✅ Table 'expert_experiences' matches with 0 rows
✅ Table 'experiences' matches with 0 rows
✅ Table 'resume_awards' matches with 0 rows
✅ Table 'community_comments' matches with 1 rows
✅ Table 'online_edu_lectures' matches with 0 rows
✅ Table 'experts' matches with 0 rows
✅ Table 'platform_histories' matches with 0 rows
✅ Table 'performance_dx_attainment_degree_rates' matches with 0 rows
✅ Table 'performance_dx_completes' matches with 0 rows
✅ Table 'ncs_recommend_keywords' matches with 1083 rows
✅ Table 'ncs_self_inspects' matches with 1114 rows

✅ Databases are identical in structure and row count.