DEV Community

Rodriguez Patrick
Rodriguez Patrick

Posted on

Seis estrategias de respaldo para bases de datos NoSQL, y qué falla de verdad

Seis estrategias de respaldo para bases de datos NoSQL, y qué falla de verdad

No uso MS SQL Server: el enunciado lo excluye y, además, el problema que quiero
estudiar es el de los motores NoSQL, donde cada familia de motor tiene una forma
distinta de persistir y, sobre todo, una forma distinta de romperse.

Lo que hice fue construir una aplicación que ejecuta seis estrategias de respaldo
sobre tres motores reales, restaura cada una y cuenta lo que vuelve. No me
conformé con que el comando terminara con código cero: varias veces terminó en
cero sin haber restaurado nada.

Enlaces del proyecto

Los tres motores

Motor Versión Modelo de datos Rol en el proyecto
MongoDB 7.0.14 documentos en colecciones base documental con índices
Redis 7.0.15 pares clave-valor en memoria claves de cinco tipos distintos
Neo4j 5.24.0 grafos (nodos y relaciones) grafo con tres tipos de arista

Las seis estrategias, con las cifras medidas

Cada número de esta tabla salió de una corrida real: se genera el respaldo, se
calcula su SHA-256, se destruyen los datos, se restaura y se cuenta lo que volvió.

Motor Estrategia Artefacto Tamaño Duración Round-trip
MongoDB logico_mongodump *.bson.gz 30.8 KiB 343 ms 1400 → 1400 docs
MongoDB fisico_wiredtiger snapshot_wiredtiger.tar.gz 1.11 MiB 2.44 s 1400 → 1400 docs
Redis rdb_dump dump.rdb 11.3 KiB 154 ms 240 → 240 claves
Redis aof_append appendonlydir/ 11.4 KiB 2.15 s 240 → 240 claves
Neo4j dump_binario neo4j.dump 509.1 KiB 3.48 s 200/594 → 200/594
Neo4j exportacion_cypher JSON Lines 104.0 KiB 420 ms 200/594 → 200/594

6 de 6 restauraron exactamente lo que había. Ese "exactamente" es lo que
importa: varias veces costó llegar ahí.

1. MongoDB, mongodump: el lógico

Exporta cada colección a BSON comprimido. Habla el protocolo de red, así que
funciona con el servidor encendido.

mongodump --uri mongodb://127.0.0.1:27017/dataforge_backup --out DIR --gzip
mongorestore --uri mongodb://127.0.0.1:27017 --gzip DIR/dataforge_backup/clientes.bson.gz
Enter fullscreen mode Exit fullscreen mode

Protege los documentos y los índices. No protege los usuarios, los roles, el
oplog ni el estado interno de WiredTiger: eso va aparte.

2. MongoDB, copia en frío de WiredTiger

Con el motor detenido, se copia el dbpath entero: los .wt, el catálogo,
WiredTiger.turtle, sizeStorer y el journal. Es una foto del almacenamiento.

Aquí está la primera limitación que vale la pena entender: el archivo es del
motor completo, no de una base
. Al probarlo, el dbpath tenía tres bases y el
respaldo se llevó las tres. Si lo que quieres es respaldar una base, esta
estrategia no sirve, y esa es exactamente la razón por la que existe
mongodump.

3. Redis, volcado RDB

redis-cli -p 6379 --rdb dump.rdb
Enter fullscreen mode Exit fullscreen mode

Un punto de control binario de toda la base. Redis 7 usa copy-on-write:
mientras se copia, las páginas modificadas se duplican, así que el archivo es
consistente aunque el servidor siga recibiendo escrituras.

No protege los comandos ejecutados entre puntos de control: la ventana de pérdida
es de segundos.

4. Redis, copia del AOF

El AOF es un registro de órdenes de escritura, no de estado. Su ventana de
pérdida es mucho menor que la del RDB.

redis-cli -p 6379 BGREWRITEAOF
# esperar a que termine:
redis-cli -p 6379 INFO persistence   # aof_rewrite_in_progress == 0
Enter fullscreen mode Exit fullscreen mode

Con Redis 7 el AOF ya no es un archivo suelto: es un directorio con un
manifiesto y varios .base.rdb e .incr.aof. Copiar solo el .aof deja fuera
los incrementales y se pierde lo escrito después.

5. Neo4j, dump binario

neo4j-admin database dump neo4j --to-path=DESTINO
neo4j-admin database load neo4j --from-path=DESTINO --overwrite-destination
Enter fullscreen mode Exit fullscreen mode

Empaqueta todo en un .dump. En la edición Community exige que la base esté
detenida
para el dump y para la carga: es una ventana sin servicio, y la
ficha de la estrategia lo declara en vez de esconderlo.

6. Neo4j, exportación a JSON Lines

Nodos y aristas como texto, con los identificadores internos reemplazados por
claves de negocio. Es la única que corre en caliente y la única que se puede
revisar a mano, versionar en un repositorio o comparar entre versiones.

Lo que falló de verdad

Esta es la parte que más aporta, porque no aparece en los manuales.

Un respaldo que "tiene éxito" y no restaura nada

mongorestore no acepta un directorio. Si se le pasa la carpeta del dump
devuelve código de salida 0 y restaura cero documentos, con un mensaje
apodable: don't know what to do with file ... skipping. Hay que pasarle cada
.bson.gz individual.

Lo detecté porque conté los documentos después de restaurar. Si hubiera confiado
en el código de salida, habría publicado un respaldo que no restauraba nada.

Redis 7 ya no permite elegir dónde guardar el RDB

CONFIG SET dir falla con can't set protected config. La salida es usar
redis-cli --rdb DESTINO, que además no detiene el servidor: usa replicación
en modo rdb-only.

Neo4j: las etiquetas no se pueden añadir después

Neo4j 5 rechaza SET n:Etiqueta con un error de sintaxis: eso exige el plugin
APOC, que no viene instalado. La forma sin plugins es crear el nodo con sus
etiquetas desde el CREATE (CREATE (n:Uno:Dos {...})), lo que obliga a agrupar
las filas por su conjunto exacto de etiquetas.

Neo4j: MERGE colapsa las aristas repetidas

En un grafo puede haber varias aristas del mismo tipo entre el mismo par de
nodos
, con propiedades distintas. MERGE las funde en una sola. Al medir el
round-trip me faltaban 4 relaciones: 590 de 594. Parecía "casi bien", y por eso
lo investigué en vez de anotarlo como una pérdida menor. Con CREATE se
conservan todas.

La tentación era relajar la comparación para que el número cuadrara. Eso habría
convertido un defecto real en un falso positivo.

Una ventana sin servicio

Las dos estrategias físicas (fisico_wiredtiger y dump_binario) exigen el
motor detenido. En un sistema en producción eso no es un detalle de
implementación: es una decisión de arquitectura. Para la misma base, la
exportación lógica de Neo4j corría en caliente en 420 ms, frente a los 3.48 s del
dump con el servidor parado.

Integración continua

Cada push a main ejecuta la suite. Los casos que necesitan motores reales no
pueden correr en el runner de GitHub Actions, así que fallan de forma
explícita
en vez de devolver un resultado inventado; la verificación completa se
hace contra los motores locales. El despliegue en Render es un render.yaml con
autoDeploy por commit y el health check en /api/salud.

La aplicación usa solo la biblioteca estándar de Python: sin requirements.txt,
sin instalación, sin cadena de dependencias. En la nube los motores aparecen como
n/d, porque un servicio público no expone puertos de base de datos; en local
responden de verdad.

Conclusiones

  1. Un respaldo no es un archivo: es una cadena. Backup, verificación por hash, restauración y conteo. Saltaba un eslabón, el resultado no servía.
  2. El código de salida no es evidencia. Dos de los fallos más caros de este proyecto salían con 0. Contar lo que volvió sí lo es.
  3. Cada familia de motor tiene su propia restricción, y la operación más cara no siempre es la que parece: la física es más rápida de escribir pero te quita el servicio.
  4. El respaldo físico es del motor; el lógico, de la base. Esa diferencia decide qué estrategia sirve y cuál no.
  5. Documentar lo que no protege es más útil que documentar lo que sí. Una ficha de estrategia que solo enumera ventajas no sirve para decidir.

Código

El repositorio incluye los tres adaptadores, la batería de 27 pruebas con el
ciclo completo contra un motor real, el smoke test en navegador y el
render.yaml del despliegue.

Top comments (0)