Migraciones de datos en MongoDB | Nicolás Garzón
Inicio Wiki MongoDB Migraciones de datos Volver a MongoDBMongoDB
Migraciones de datos Explica cómo planificar y ejecutar migraciones de datos en MongoDB mediante backfills idempotentes, lotes, checkpoints, validación y rollback operativo.
Última actualización Actualizada 25 de jul de 2026
Una migración de datos cambia documentos existentes sin detener necesariamente la aplicación. Debe ser . El objetivo no es procesar lo más rápido posible, sino llegar al estado final sin comprometer latencia, replication, backups ni posibilidad de rollback.
Nota anteriorEvolución del schema Nota siguiente Índices y migraciones reanudable, idempotente, observable y limitada por capacidad
Texto
Copiar preparar lectores y writers
→ seleccionar lote
→ transformar con precondition
→ guardar checkpoint
→ validar
→ repetir
→ reconciliar y limpiar
formato de origen y destino;
cantidad y tamaño de documentos;
query para encontrar pendientes;
transformación pura;
índices necesarios;
throughput máximo;
checkpoint;
criterio de éxito;
rollback o compensación;
owner y runbook.
Toma un backup restaurable y prueba el restore. Un backup no sirve si solo existe como archivo no verificado.
Primero despliega código que pueda leer ambos formatos. Después cambia writers para producir el formato nuevo. Solo entonces inicia el backfill.
TypeScript
Copiar const phone = document. phoneNumber ?? document. phone ?? null ; Si writers antiguos siguen activos, pueden recrear documentos viejos mientras migras. Usa feature flags, versionado o dual write temporal.
Usa una condición explícita:
JavaScript
Copiar {
schemaVersion : { $lt : 3 }
} JavaScript
Copiar {
migratedAt : { $exists : false }
} Evita depender únicamente de una fecha de creación si documentos antiguos pueden modificarse después.
TypeScript
Copiar const batch = await collection. find (
{
schemaVersion: { $lt: 3 } ,
_id: { $gt: checkpoint }
} ,
{
sort: { _id: 1 } ,
limit: 500
}
) . toArray ( ) ; El tamaño correcto depende de:
tamaño de documentos;
cantidad de índices;
latencia objetivo;
oplog window;
red;
replication lag;
capacidad de rollback.
Más grande no siempre es más eficiente.
Guarda el último _id procesado, rango temporal o partición:
JavaScript
Copiar {
migration : 'orders-v3' ,
lastId : ObjectId ( '...' ) ,
processed : 125000 ,
failed : 12 ,
updatedAt : new Date ( )
} El checkpoint debe persistirse después de confirmar el batch. Si se guarda antes, puede saltar documentos; si se guarda después, el último lote puede repetirse, por lo que la transformación debe ser idempotente.
La migración debe producir el mismo resultado al repetirse:
JavaScript
Copiar db. orders. updateOne (
{
_id,
schemaVersion : 2
} ,
{
$set : {
phoneNumber : normalizedPhone,
schemaVersion : 3 ,
migratedAt : new Date ( )
} ,
$unset : {
phone : ''
}
}
) ; La precondition evita volver a transformar una versión ya actualizada.
Para transformaciones expresables en servidor:
JavaScript
Copiar db. users. updateMany (
{ schemaVersion : 1 } ,
[
{
$set : {
fullName : {
$trim : {
input : {
$concat : [ '$firstName' , ' ' , '$lastName' ]
}
}
} ,
schemaVersion : 2
}
}
]
) ; Un updateMany gigante puede generar picos. Divide por rangos aunque la expresión sea server-side.
Agrupa operaciones distintas:
TypeScript
Copiar await collection. bulkWrite (
batch. map ( ( document) => ( {
updateOne: {
filter: {
_id: document. _id,
schemaVersion: document. schemaVersion
} ,
update: buildMigration ( document)
}
} ) ) ,
{ ordered: false }
) ; ordered: false permite continuar tras errores independientes, pero debes registrar qué operaciones fallaron. El bulk no es una transacción all-or-nothing.
Aplica throttling dinámico. Pausa o reduce velocidad cuando aumenten:
latencia p95/p99;
replication lag;
cache eviction;
disk queue;
CPU;
oplog consumption;
error rate.
No ejecutes migraciones pesadas al mismo tiempo que index builds, backups, resharding o campañas de tráfico.
Un usuario puede modificar un documento entre lectura y escritura. Incluye versión o estado esperado:
JavaScript
Copiar {
_id,
schemaVersion : 2 ,
updatedAt : originalUpdatedAt
} Si no coincide, vuelve a leer o deja el documento para una segunda pasada. No sobrescribas cambios recientes con una snapshot antigua.
Cuando existe una proyección derivada:
Texto
Copiar capturar punto inicial
→ backfill histórico
→ consumir cambios desde ese punto
→ alcanzar tiempo realDebes evitar huecos y duplicados. Usa resume tokens, versiones e idempotencia.
datos inválidos;
duplicate key;
timeout transitorio;
documento modificado concurrentemente;
transformación no soportada;
error permanente.
Guarda ID, versión, categoría y mensaje sanitizado. No detengas toda una migración por pocos documentos corruptos si existe un proceso de revisión.
No basta contar modifiedCount. Comprueba:
cantidad pendiente;
distribución por versión;
tipos BSON;
invariantes;
duplicados;
documentos sampleados;
queries críticas;
índices;
proyecciones y eventos.
JavaScript
Copiar db. orders. countDocuments ( {
schemaVersion : { $lt : 3 }
} ) ; Debe llegar a cero o a una lista explícita de excepciones.
volver al código anterior compatible;
restaurar campos originales conservados temporalmente;
ejecutar migración inversa;
restaurar backup;
aplicar compensación.
No toda transformación es reversible. Eliminar información o combinar campos puede perder detalle. Conserva el original durante una ventana cuando sea razonable.
Separa migración de limpieza:
Texto
Copiar fase 1: crear formato nuevo
fase 2: validar uso
fase 3: retirar formato viejoEsto permite rollback. $unset masivo debe tratarse como otra migración con su propio impacto.
procesa por shard key o rangos targeted;
evita scatter-gather constante;
observa balancer;
limita operaciones cross-shard;
considera tenant outliers;
coordina con migrations de rangos.
documentos típicos;
missing y null;
tipos incorrectos;
arrays vacíos y enormes;
documentos cerca de 16 MiB;
duplicados;
concurrent writers;
interrupción a mitad;
reejecución completa;
restore de backup.
ejecutar desde un endpoint HTTP;
usar un updateMany sin límites;
no guardar checkpoint;
transformación no idempotente;
ignorar oplog y secondaries;
migrar antes del código compatible;
no capturar errores por documento;
borrar campos antiguos inmediatamente;
declarar éxito sin reconciliación.
Ensaya en copia de producción.
Mide throughput seguro.
Interrumpe y reanuda.
Repite batches.
Simula writers concurrentes.
Observa lag y oplog.
Valida conteos e invariantes.
Ejecuta queries críticas.
Prueba rollback.
Documenta resultados y cleanup.
Una migración es un proceso operacional, no un script desechable. Debe avanzar por batches, usar preconditions y checkpoints, tolerar reintentos, proteger el workload normal y terminar con validación y reconciliación. La compatibilidad se despliega antes; la limpieza ocurre al final.
Comprueba lo aprendido
¿Por qué el checkpoint se guarda después del batch?
¿Qué hace idempotente una migración?
¿Cómo evitas sobrescribir cambios concurrentes?
¿Qué métricas controlan el throttling?
¿Por qué separar backfill y cleanup?
¿Cómo confirmas que terminó correctamente?