数据库迁移migration

📦 数据库迁移(Database Migration)详解

数据库迁移 就像是数据库的版本控制系统(类似 Git 对代码的管理),用于管理数据库结构(表、字段、索引等)的变更历史!

🎯 核心作用

想象一个场景:

❌ 没有迁移的噩梦

复制代码
开发者A: "我加了一个 email 字段"
开发者B: "什么?我这边运行报错了!"
开发者C: "生产环境的表结构是什么样的?"
所有人: "😱 不知道啊..."

结果:
- 每个人的数据库结构不一样
- 无法追踪谁改了什么
- 部署到生产环境时不知道要改什么
- 回滚困难

✅ 有迁移的优雅

复制代码
开发者A: 创建迁移文件 "添加email字段"
团队成员: 运行 npm run migrate
生产环境: 运行 npm run migrate

结果:
- ✅ 所有环境数据库结构一致
- ✅ 每个变更都有记录
- ✅ 可以回滚
- ✅ 可追溯历史

📋 什么是数据库迁移?

数据库迁移 = 数据库结构变更的脚本文件

它记录了:

  • 📝 要做什么改动(up)
  • 🔙 如何撤销改动(down)
  • ⏰ 改动的时间
  • 📄 改动的描述

🔄 迁移的工作原理

1. 迁移文件结构

javascript 复制代码
// 20240219065551-add-email-field.js
module.exports = {
  // ⬆️ UP: 执行迁移(前进)
  up: async (queryInterface, Sequelize) => {
    await queryInterface.addColumn('users', 'email', {
      type: Sequelize.STRING,
      allowNull: false
    });
  },

  // ⬇️ DOWN: 回滚迁移(后退)
  down: async (queryInterface, Sequelize) => {
    await queryInterface.removeColumn('users', 'email');
  }
};

2. 迁移执行过程

复制代码
1. 数据库中有一个特殊表(SequelizeMeta)记录已执行的迁移
2. 运行 migrate 命令时,检查哪些迁移未执行
3. 按时间顺序执行未执行的迁移
4. 记录到 SequelizeMeta 表
5. 完成!

3. 迁移历史追踪

sql 复制代码
-- SequelizeMeta 表内容
+-------------------------------------+
| name                                |
+-------------------------------------+
| 20240219065551-init-users.js        |
| 20240219071334-add-platform.js      |
| 20240219074827-update-struct.js     |
+-------------------------------------+

🔄 迁移的完整生命周期

1. 创建迁移文件

bash 复制代码
# 你的 package.json 中已有命令
npm run generate-migration -- --name=add-email-field

# 会生成类似这样的文件
# 20251007120000-add-email-field.js

2. 编写迁移内容

注意: Sequelize 不支持根据 Model 自动生成迁移内容

很多人误以为迁移会根据 Model 自动生成,但 Sequelize 官方不支持这个功能

其他 ORM 的对比:

javascript 复制代码
// ❌ Sequelize - 不支持自动生成
npx sequelize migration:generate  // 只生成空模板

// ✅ TypeORM - 支持自动生成
npm run typeorm migration:generate  // 会对比 Entity 和数据库,自动生成迁移

// ✅ Django - 支持自动生成
python manage.py makemigrations  // 会对比 Model 和数据库,自动生成迁移

// ✅ Prisma - 支持自动生成
npx prisma migrate dev  // 会对比 schema 和数据库,自动生成迁移
javascript 复制代码
module.exports = {
  up: async (queryInterface, Sequelize) => {
    // 创建表
    await queryInterface.createTable('orders', {
      id: {
        type: Sequelize.INTEGER,
        primaryKey: true,
        autoIncrement: true
      },
      user_id: {
        type: Sequelize.INTEGER,
        allowNull: false
      },
      total_price: {
        type: Sequelize.DECIMAL(10, 2),
        allowNull: false
      },
      created_at: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW
      }
    });

    // 添加索引
    await queryInterface.addIndex('orders', ['user_id']);

    // 添加外键
    await queryInterface.addConstraint('orders', {
      fields: ['user_id'],
      type: 'foreign key',
      references: {
        table: 'users',
        field: 'id'
      }
    });
  },

  down: async (queryInterface, Sequelize) => {
    // 回滚:删除表
    await queryInterface.dropTable('orders');
  }
};

3. 执行迁移

bash 复制代码
# 你的项目中
npm run migrate

# 等同于
npx sequelize db:migrate

4. 回滚迁移

bash 复制代码
# 回滚最后一次迁移
npm run migrate:undo

# 等同于
npx sequelize db:migrate:undo

# 回滚所有迁移
npx sequelize db:migrate:undo:all

📊 常见迁移操作

1. 创建表

javascript 复制代码
up: async (queryInterface, Sequelize) => {
  await queryInterface.createTable('products', {
    id: {
      type: Sequelize.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    name: {
      type: Sequelize.STRING(100),
      allowNull: false
    },
    price: {
      type: Sequelize.DECIMAL(10, 2),
      allowNull: false
    }
  });
}

2. 添加字段

javascript 复制代码
// 你的项目中就有这个例子
up: async (queryInterface, Sequelize) => {
  await queryInterface.addColumn('collections', 'platform', {
    type: Sequelize.STRING,
    allowNull: false,
    defaultValue: 'taobao'  // 最好加默认值
  });
}

3. 修改字段

javascript 复制代码
up: async (queryInterface, Sequelize) => {
  await queryInterface.changeColumn('users', 'age', {
    type: Sequelize.INTEGER,
    allowNull: true
  });
}

4. 删除字段

javascript 复制代码
up: async (queryInterface, Sequelize) => {
  await queryInterface.removeColumn('users', 'old_field');
}

5. 重命名字段

javascript 复制代码
up: async (queryInterface, Sequelize) => {
  await queryInterface.renameColumn('users', 'username', 'user_name');
}

6. 添加索引

javascript 复制代码
up: async (queryInterface, Sequelize) => {
  await queryInterface.addIndex('orders', ['user_id', 'created_at'], {
    name: 'orders_user_created_idx'
  });
}

7. 删除表

javascript 复制代码
up: async (queryInterface, Sequelize) => {
  await queryInterface.dropTable('old_table');
}

🎯 迁移的最佳实践

✅ 好的迁移

  1. 总是写 down 方法
javascript 复制代码
module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.addColumn('users', 'email', {
      type: Sequelize.STRING
    });
  },
  down: async (queryInterface, Sequelize) => {
    await queryInterface.removeColumn('users', 'email'); // ✅ 能回滚
  }
};
  1. 处理已有数据
javascript 复制代码
up: async (queryInterface, Sequelize) => {
  // 先添加字段(允许 NULL)
  await queryInterface.addColumn('users', 'status', {
    type: Sequelize.STRING,
    allowNull: true
  });
  
  // 给已有数据设置默认值
  await queryInterface.sequelize.query(
    `UPDATE users SET status = 'active' WHERE status IS NULL`
  );
  
  // 再修改为 NOT NULL
  await queryInterface.changeColumn('users', 'status', {
    type: Sequelize.STRING,
    allowNull: false
  });
}
  1. 使用事务(保证原子性)
javascript 复制代码
up: async (queryInterface, Sequelize) => {
  const transaction = await queryInterface.sequelize.transaction();
  try {
    await queryInterface.addColumn('users', 'email', {
      type: Sequelize.STRING
    }, { transaction });
    
    await queryInterface.addIndex('users', ['email'], { transaction });
    
    await transaction.commit();
  } catch (err) {
    await transaction.rollback();
    throw err;
  }
}

🔧 你的项目配置

package.json 中的迁移命令

json 复制代码
{
  "scripts": {
    "generate-migration": "npx sequelize migration:generate --name=update-database-struct",
    "migrate": "npx sequelize db:migrate",
    "migrate:undo": "npx sequelize db:migrate:undo"
  }
}

🌍 多环境管理

你的配置支持不同环境:

bash 复制代码
# 开发环境(默认)
NODE_ENV=development npm run migrate

# 生产环境
NODE_ENV=production npm run migrate

两种数据库管理方式

方式 1️⃣: Model.sync()你现在用的
javascript 复制代码
// app.js
await this.app.model.sync({ force: false });

工作原理:

复制代码
1. 读取 Model 定义(如 user.js)
   ↓
2. 自动生成 CREATE TABLE SQL
   ↓
3. 对比数据库,如果表不存在就创建
   ↓
4. ✅ 表自动创建完成!

特点:

  • 自动化:根据 Model 自动创建表
  • 方便:不需要写迁移文件
  • 开发快速:改 Model 就自动更新表
  • 危险force: true 会删除所有数据!
  • 不可控:不知道具体改了什么
  • 无历史:没有变更记录
  • 生产环境不推荐:可能导致数据丢失
方式 2️⃣: Migration(迁移)应该用的
bash 复制代码
# 1. 手动生成迁移文件(空模板)
npm run generate-migration -- --name=create-users

# 2. 手动编写迁移内容
# 20251007-create-users.js
up: async (queryInterface, Sequelize) => {
  await queryInterface.createTable('users', {
    id: {
      type: Sequelize.INTEGER,
      primaryKey: true,
      autoIncrement: true
    },
    username: {
      type: Sequelize.STRING(50),
      allowNull: true
    }
    // ... 手动定义所有字段
  });
}

# 3. 执行迁移
npm run migrate

工作原理:

复制代码
1. 开发者手动创建迁移文件
   ↓
2. 开发者手动编写 SQL 操作(up/down)
   ↓
3. 执行 npm run migrate
   ↓
4. 记录到 SequelizeMeta 表
   ↓
5. ✅ 可追溯、可回滚!

特点:

  • 可控:精确控制每个变更
  • 可追溯:所有变更都有记录
  • 可回滚:可以撤销变更
  • 团队协作:大家同步数据库结构
  • 生产环境安全:不会意外删除数据
  • 麻烦:需要手动编写迁移文件
  • 不自动:Model 改了,还要手动写迁移

📊 对比总结

特性 sync() Migration
自动化 ✅ 自动根据 Model 创建表 ❌ 需手动编写
开发速度 ⚡ 快 🐢 慢
生产环境 ❌ 危险 ✅ 安全
变更历史 ❌ 无记录 ✅ 完整记录
回滚 ❌ 不支持 ✅ 支持
团队协作 ⚠️ 困难 ✅ 容易
数据安全 ⚠️ 可能丢失 ✅ 安全

📊 迁移 vs 直接改表

❌ 直接改表(危险)

sql 复制代码
-- 直接在数据库执行 SQL
ALTER TABLE users ADD COLUMN email VARCHAR(100);

问题:
- 其他开发者不知道
- 生产环境可能忘记执行
- 无法回滚
- 无法追溯历史

✅ 使用迁移(安全)

javascript 复制代码
// 迁移文件
up: async (queryInterface, Sequelize) => {
  await queryInterface.addColumn('users', 'email', {
    type: Sequelize.STRING(100)
  });
}

优势:
- ✅ 版本控制(Git)
- ✅ 团队同步
- ✅ 可回滚
- ✅ 可追溯
- ✅ 自动化部署

🚀 部署流程

bash 复制代码
# 1. 本地开发
npm run generate-migration -- --name=add-new-feature
# 编写迁移文件
npm run migrate  # 本地测试

# 2. 提交代码
git add database/migrations/
git commit -m "feat: 添加新功能的数据库迁移"
git push

# 3. 服务器部署
ssh production-server
cd /path/to/project
git pull
npm run migrate  # 执行迁移
pm2 restart app  # 重启应用

🎓 总结

数据库迁移是:

  • 📦 数据库结构的版本控制系统
  • 📝 记录所有结构变更的历史
  • 🔄 支持前进(up)和回滚(down)
  • 👥 让团队成员的数据库保持同步
  • 🚀 让部署过程可追溯、可重复

就像:

  • Git 管理代码的变更
  • Migration 管理数据库的变更
相关推荐
shixian10304113 小时前
Django 学习日志
数据库·学习·sqlite
IT 小阿姨(数据库)4 小时前
PostgreSQL通过pg_basebackup物理备份搭建流复制备库(Streaming Replication Standby)
运维·服务器·数据库·sql·postgresql·centos
小蒜学长4 小时前
springboot基于javaweb的小零食销售系统的设计与实现(代码+数据库+LW)
java·开发语言·数据库·spring boot·后端
云边有个稻草人5 小时前
从内核调优到集群部署:基于Linux环境下KingbaseES数据库安装指南
linux·数据库·金仓数据库管理系统
EnCi Zheng5 小时前
JPA 连接 PostgreSQL 数据库完全指南
java·数据库·spring boot·后端·postgresql
Raymond运维5 小时前
MySQL包安装 -- RHEL系列(Yum资源库安装MySQL)
linux·数据库·mysql
他们叫我技术总监6 小时前
Oracle数据库常见问题实战:从连接错误到自动清理空闲会话
数据库·oracle
菲兹园长8 小时前
MySql(SQL)
数据库·sql·mysql
一只小bit8 小时前
MySQL表的操作:创建—修改—删除流程解析
数据库·mysql·oracle