lunes, 28 de abril de 2008

Recuperando un Backup con poco y nada

Una de mis tareas en mi trabajo es mantener la base de datos de una de las aplicaciones de la organización, el RDBMS es MySQL en su versión 5.0 comunity.

Accidentalemte se borro una de las tablas en dicha base y obviamente me pidieron que recupere la tabla del backup, yo muy feliz fuí a buscar los backup's para recuperar la tabla y me encuentro con que el último backup completo hera del 25 de feberero y solo con ciertos incrementales (binlogs de mysql), la mayoría del las copias fallaron debido a que la unidad donde relizaba los respaldos se ocupaba para otras cuestiones y aveces no contaba con el espacio necesario.

Al darme cuenta de lo sucedido empezé a sudar como testigo falso, dado que nunca me puse a controlar si los backups se realizaban adecuadamente, mea culpa. Empezé a pensar como podía recuperar la información e inmediatamente fuí a ver si los binlog seguían en el servidor de producción, y sí efectivamente estaban todos y completos.

Fue ahí cuando decidí recuperar la base completa al 25 de febrero y aplicar los binlogs hasta el día de la catastrofe, 18 de abril.
Descomprimí el backup completo de la base, obtenido mediante un msyqldump, y el mismo pesaba 14 GB, esto es debido a que en el mismo estaban todas las bases de datos de esa instancia y yo solo quería una de ellas a la que llamaré XXX.

Probe tratar de editar el archivo con vi, nano, kedit, gedit pero ninguno podía abrirlo. Sin saber que hacer para extraer solo la base XXX del backup se me ocurrió utilizar la consola de linux. Lo primero que hice fue ver las lineas donde empezaban las bases, como?, fácil, en el dump de mysql la primera instrucción es CREATE DATABASE, por ello realizé un grep con dicha expresión sobre el archivo e indique me muestre los números de líneas.

grep -n "CREATE DATABASE" bkp-25-02-2008.sql

El mismo me arrojo algo así

10 CREATE DATABASE ....
1400 CREATE DATABASE ....
23456 CREATE DATABASE XXX
45234 CREATE DATABASE ...
....

Una vez que tenía la línea donde empezaba y terminaba el dumpeo de la base XXX, realizé un head hasta la línea 45234 para obtener la primera parte y luego apliqué un tail desde el final a la línea 21778 (45234 - 23456). El comando quedo algo así

#head -n 45234 25022008.sql |tail -n 21778 > recorte.sql

Una vez que termino el archivo resultante quedo de 2.7 GB. En una base de datos limpia cargue el dumpeo recortado.

mysql -u root -p < recorte.sql

Al finalizar tenía la base lista al 25 de febrero y solo restaba aplicar los binlogs de cada día. Como heran varios decidí hacer un pequeño script en python para que los procese de a uno. Básicamente hera un bucle for que iba desde el número de binlog incial (1828) al final (1976), este transforma cada bin log en un script sql (mysqlbinlog) y lo deja en un archivo temporal el cual es importado a la base.

#!/usr/bin/python
import os for i in range(1828,1976):
print i
file = 'mysql-bin.00%s' % str(i)
os.popen('rm tmp.sql')
os.popen('mysqlbinlog --database=XXX %s > tmp.sql' % file)

os.popen('mysql -u root --password=lapass <>

La primera vez que lo probe arrojo unos errores, lo primero que pense es que continuaba la importación después de el error pero había sido que no, la importación se corta directamente en el punto de error, en consecuencia faltaba muchisima información.

Al final de todo y después de varias pruebas tuve la suerte de poder recuperar la información.

Esta fue una linda experiencia para contar pero no para vivirla, todo esto se hubiese solucionado siendo un poco cuidadoso en chequear los backups generados automáticamente.