SQL Azure: Backup de datos en el storage

Estos últimos días he estado investigando qué alternativas tenía a la hora de hacer copias de seguridad de una base de datos en SQL Azure y las opciones, por ahora, son pocas: SQL Azure no permite hacer backups a un fichero -como podemos hacer con cualquier otra base de datos SQL Server- y, además, tampoco hay soporte para replicación.

Aún así, tenemos algunas alternativas que, sin ser una solución definitiva, sí nos pueden permitir montar un sistema de respaldo adecuado a nuestro escenario particular hasta que Microsoft se ponga las pilas y libere nuevas funcionalidades que mejoren este aspecto de la plataforma.

En primer lugar, podemos hacer un snapshot de nuestra base de datos SQL Azure con el comando CREATE DATABASE AS COPY. La ventaja de este método es que obtenemos una copia exacta de nuestra BBDD en producción sin downtime.
Las desventajas son que este snapshot no se puede ir actualizando (no hay un update from database o algo así) y que esta base de datos copiada se factura como cualquier otra.
Me parece una buena opción si vamos a hacer algún tipo de proceso a la BBDD original que pueda provocar un desastre -podríamos tener una copia exacta a punto de una forma rápida- o si vamos a volcar la BBDD a un fichero y necesitamos un duplicado donde no se estén haciendo escrituras -luego veremos cómo-.

Otra alternativa es utilizar Sync Framework. Hay abundante información tanto en el sitio de Azure como en el Sync Framework Team blog con ejemplos de cómo se puede utilizar no sólo para sincronizar bases de datos SQL, sino cualquier repositorio, como puede ser un sistema de ficheros. Esta alternativa es muy flexible y potente, y puede ser interesante a la hora de sincronizar una BBDD on premises con una en Azure.

Sin embargo, sólo con estas dos alternativas no estamos aprovechando la posibilidad que nos brinda el Azure Storage de guardar grandes cantidades de datos (en Blobs) por un coste muy reducido. Por ejemplo, copias semanales durante el último trimestre. ¿Qué tal si guardamos el contenido de nuestras tablas como XML serializado en nuestro espacio de Storage? Podemos almacenar todas nuestras copias en la nube, por un coste muy reducido, y descargarlas sólo si es necesario.

El siguiente método recibe como parámetro una  cadena de conexión a la BBDD de origen, la cadena de conexión al storage y un nombre de backup, que será el nombre del container donde guardaremos los xml de datos:

        public static void Backup(string aDbConnString, string aBackupName, string aBlobConnString)
        {
            var lTargetStorageAccount = CloudStorageAccount.Parse(aBlobConnString);
            var lTargetClient = lTargetStorageAccount.CreateCloudBlobClient();
            var lTargetBlobContainer = lTargetClient.GetContainerReference(aBackupName);
            lTargetBlobContainer.CreateIfNotExist();
            var lTables = getDbTablesList(aDbConnString);

            using (var lConn = new SqlConnection(aDbConnString))
            {
                lConn.Open();
                foreach (var lTblName in lTables)
                {
                    var lDataTable = new DataTable(lTblName);
                    var lCmd = string.Format("select * from [{0}]", lTblName);
                    var da = new SqlDataAdapter(lCmd, lConn);
                    da.SelectCommand.CommandTimeout = 60 * 10;
                    da.Fill(lDataTable);

                    using (var lStream = new StringWriter())
                    {
                        lDataTable.WriteXml(lStream, XmlWriteMode.WriteSchema);
                        var lTargetBlob = getBlobReference(aBackupName, lTargetBlobContainer, lTblName);
                        var lStr = lStream.ToString();
                        lTargetBlob.UploadText(lStr);
                    }
                }
                lConn.Close();
            }
        }

        private static CloudBlob getBlobReference(string aBackupName, CloudBlobContainer aBlobContainer, string aTblName)
        {
            var lTargetBlobUri = string.Format("{0}/{1}.xml", aBackupName, aTblName);
            var lTargetBlob = aBlobContainer.GetBlobReference(lTargetBlobUri);
            return lTargetBlob;
        }

        private static List getDbTablesList(string aConnString)
        {
            var lTables = new List();
            using (var lConn = new SqlConnection(aConnString))
            {
                lConn.Open();
                var lCmdTbls = new SqlCommand(@"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE'", lConn);
                using (var dr = lCmdTbls.ExecuteReader())
                {
                    while (dr.Read())
                    {
                        lTables.Add(dr["TABLE_NAME"] as string);
                    }
                }
            }
            return lTables;
        }

Esta aproximación no tiene en cuenta algunos detalles, como por ejemplo:

  • La información de esquema no se está guardando al hacer la copia y, al restaurar, se asumirá que la BBDD está creada y con las tablas vacías.
  • Al hacer la copia de datos, si la BBDD está en uso podría haber escrituras de datos durante el proceso de backup, y por lo tanto a inconsistencia de datos. Utilizar la técnica del snapshot de BBDD que hemos visto antes y lanzar el backup sobre esa copia sería una solución.
  • Para tablas muy grandes, la lectura debería hacerse por fragmentos.

Idealmente, este código debe ejecutarse en el entorno de Azure, puesto que de esa manera no hay costosas descargas de datos. Si se desea llegar a programar una especie de SQL Agent para realizar las copias periódicamente, en el blog del equipo de SQL Azure hay una serie de posts muy interesantes sobre cómo implementar algo así con un worker role.

Para restaurar la información haremos uso de la clase XmlBulkImport. El método sería similar a esto:

public static void Restore(string aDbConnString, string aBackupName, string aBlobConnString)
        {
            var lTargetStorageAccount = CloudStorageAccount.Parse(aBlobConnString);
            var lTargetClient = lTargetStorageAccount.CreateCloudBlobClient();
            var lTargetBlobContainer = lTargetClient.GetContainerReference(aBackupName);
            lTargetBlobContainer.CreateIfNotExist();

            var lTables = getDbTablesList(aDbConnString);

            using (var tx = new TransactionScope())
            {
                var bc = new SqlBulkCopy(
                    aDbConnString
                    , SqlBulkCopyOptions.KeepIdentity | SqlBulkCopyOptions.TableLock
                    );

                foreach (var t in lTables)
                {
                    var lDt = new DataTable();
                    var lBlob = getBlobReference(aBackupName, lTargetBlobContainer, t);
                    using (var lStrRead = new StringReader(lBlob.DownloadText()))
                    {
                        lDt.ReadXml(lStrRead);
                    }
                    bc.DestinationTableName = t;
                    bc.WriteToServer(lDt);
                }
                tx.Complete();
            }
        }

En resumen, a día de hoy no hay una solución definitiva para gestionar las copias de respaldo e históricas de nuestras BBDD en Azure, así como para replicar bases de datos. Por lo tanto, hasta que Microsoft nos ofrezca una solución más completa, deberemos desarrollar la solución que mejor se adapte a nuestras necesidades según cada escenario.

Nos vemos en la nube ;P

Esta entrada fue publicada en Azure y etiquetada , , , . Guarda el enlace permanente.

2 respuestas a SQL Azure: Backup de datos en el storage

  1. It’s an interesting approach. We recently built some PowerShell cmdlets for backing up SQL Azure data (in our Azure Management Cmdlets product) and there we went «bcp» route to download the data in delimited format and once the data is downloaded on the disk in the form of files, we upload them in Blob Storage. Thought I should mention this approach as well.

    Thanks

    Gaurav

    • Thanks!
      I think the interesting point in my approach is that you don’t have to download the data from the cloud to store the backups into blobs. By doing this, you can avoid long download times and paying for data transfers (0,15$/GB) for every backup. Download the backup from the cloud only if you need to; to perform backup and restore it is not necessary.
      Anyway, I know your cmdlets and I think they are very very useful. In fact, (I think, not sure) we are using them to perform some deployment tasks 🙂 We also have a license of your storage and log utilities and we absolutely love them 😛

Deja un comentario