数据迁移与版本控制
数据迁移的基本概念
数据迁移在数据库管理中指的是将数据从一个存储系统或结构转移到另一个的过程。在Mongoose中,数据迁移通常涉及模式变更、数据格式转换或数据库引擎更换。常见场景包括字段类型修改、字段重命名、新增索引或数据清洗。
// 示例:简单的数据迁移脚本
const migrateUserData = async () => {
const users = await User.find({});
for (const user of users) {
if (user.age && typeof user.age === 'string') {
user.age = parseInt(user.age);
await user.save();
}
}
};
版本控制的重要性
数据库模式变更需要严格版本控制,主要原因包括:
- 团队协作时避免冲突
- 追踪历史变更记录
- 支持回滚操作
- 保持开发、测试和生产环境的一致性
典型版本控制方案:
- 手动编写迁移脚本
- 使用专门的迁移工具(如migrate-mongoose)
- 结合Git进行版本管理
Mongoose迁移策略
增量式迁移
每次模式变更对应独立的迁移文件,按时间顺序执行:
// migrations/20230501-add-email-verification.js
module.exports = {
up: async (db) => {
await db.collection('users').updateMany(
{},
{ $set: { isEmailVerified: false } }
);
},
down: async (db) => {
await db.collection('users').updateMany(
{},
{ $unset: { isEmailVerified: "" } }
);
}
};
模式版本标记
在文档中添加版本字段便于追踪:
const userSchema = new mongoose.Schema({
// ...其他字段
schemaVersion: {
type: Number,
default: 1,
required: true
}
});
迁移工具实践
migrate-mongoose的典型用法:
- 安装工具包:
npm install migrate-mongoose
- 创建迁移配置:
// migrator.js
const { Migrator } = require('migrate-mongoose');
const migrator = new Migrator({
dbConnectionUri: 'mongodb://localhost/mydb',
migrationsPath: './migrations'
});
- 执行迁移:
// 创建新迁移
await migrator.create('add-user-profile');
// 执行待处理迁移
await migrator.up('all');
// 回滚最近迁移
await migrator.down('last');
复杂迁移场景处理
大数据量迁移
当处理百万级文档时需要考虑:
- 分批处理:
const batchSize = 1000;
let skip = 0;
let hasMore = true;
while (hasMore) {
const users = await User.find().skip(skip).limit(batchSize);
if (users.length === 0) {
hasMore = false;
continue;
}
// 处理逻辑...
skip += batchSize;
}
- 使用聚合管道直接操作:
await User.collection.aggregate([
{ $match: { status: 'inactive' } },
{ $set: { lastActive: new Date() } },
{ $merge: { into: 'users' } }
]);
字段类型变更
处理字段类型变更的完整流程:
- 添加新字段
- 编写迁移脚本转换数据
- 验证数据完整性
- 移除旧字段
// 将字符串类型的price转为数字
await Product.updateMany(
{ price: { $type: 'string' } },
[{
$set: {
price: {
$convert: {
input: "$price",
to: "decimal",
onError: 0
}
}
}
}]
);
测试与验证
完善的迁移测试应该包括:
- 单元测试验证迁移逻辑
- 预生产环境测试
- 性能基准测试
- 回滚测试
// 使用Jest测试迁移脚本
describe('User migration', () => {
let testUser;
beforeAll(async () => {
testUser = await User.create({
name: 'Test',
age: '25' // 故意使用字符串
});
});
it('should convert age to number', async () => {
await migrateUserData();
const updated = await User.findById(testUser._id);
expect(typeof updated.age).toBe('number');
});
});
生产环境最佳实践
- 维护迁移日志集合:
const migrationLogSchema = new mongoose.Schema({
name: String,
batch: Number,
runAt: {
type: Date,
default: Date.now
},
status: {
type: String,
enum: ['pending', 'success', 'failed']
}
});
- 实施零停机迁移策略:
- 双写机制
- 影子模式
- 蓝绿部署
- 监控关键指标:
// 监控迁移性能
const start = Date.now();
await runMigration();
const duration = Date.now() - start;
monitor.record('migration_duration', {
name: 'user_profile_update',
duration,
docsAffected
});
常见问题解决方案
处理迁移冲突
当多个开发者同时创建迁移时:
- 采用时间戳前缀命名:
202305011200-add-field.js
202305011230-remove-field.js
- 使用锁机制防止并发执行:
const lock = await Lock.findOne({ name: 'migration' });
if (lock && lock.status === 'running') {
throw new Error('Migration already in progress');
}
数据一致性保证
确保迁移后数据符合预期:
// 验证脚本示例
const verifyMigration = async () => {
const invalidDocs = await User.find({
$or: [
{ age: { $type: 'string' } },
{ email: { $exists: false } }
]
});
if (invalidDocs.length > 0) {
throw new Error(`Found ${invalidDocs.length} invalid documents`);
}
};
自动化部署集成
CI/CD管道中的迁移集成示例:
# .github/workflows/migrate.yml
name: Database Migration
on:
push:
branches: [ main ]
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm install
- run: npm run migrate:up
env:
DB_URI: ${{ secrets.PRODUCTION_DB_URI }}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:日志与审计功能实现
下一篇:高并发场景下的优化策略