Professional Documents
Culture Documents
database administration
48
49
Un enfoque combinado
En la mayora de los casos, las secuencias de comandos de migracin se pueden ser desarrollar completamente con PowerShell o con T-SQL; sin embargo,
ambos tienen sus fortalezas y sus debilidades.
Por ejemplo: en T-SQL que necesitemos lidiar
con la concatenacin de cadenas para crear un
comando de restauracin si es necesario mover la
parte fsica a diferentes discos/directorios; en
PowerShell directamente no podemos emitir una
accin de verificacin de coherencia con pureza de
datos contra SMO.
Adems, uno ya puede tener sus secuencias de
comandos de T-SQL bien probadas y slo se desea
automatizar algunas otras acciones a travs de
PowerShell. Cualquiera que sea la razn, vamos a
ver cmo podemos aprovechar ambos entornos de
comandos para lograr nuestro objetivo.
La idea es crear un script de PowerShell para
cada base de datos que se desea migrar, y esta
secuencia de comandos se implementar las operaciones de backup y restore y, a continuacin, ejecutar una secuencia de comandos T-SQL para finalizar la migracin.
La secuencia de comandos
de migracin en PowerShell
En primer lugar, debe definir qu ensamblados
deben ser cargados y un controlador de error genrico para atrapar a nivel global las excepciones no
controladas:
Listado 1
#region Load Assemblies and Global Error Handling
[System.Reflection.Assembly]::LoadWithPartialName( `
Microsoft.SqlServer.SMO) | Out-Null;
[System.Reflection.Assembly]::LoadWithPartialName( `
Microsoft.SqlServer.SMOExtended) | Out-Null;
# Simple global exception handling to see SQL Server errors
trap {
$Exc = $_.Exception;
while ( $Exc.InnerException )
{
$Exc = $Exc.InnerException;
Write-Warning ((generic trap) + $Exc.Message);
};
break;
};
#endregion
thesolidqjournal
database administration
50
Listado 2
#region Event Handlers
$PercentCompleteHandler = `
[Microsoft.SqlServer.Management.Smo.PercentCompleteEventHandler] `
{
Write-Host ([string]$_.Percent + percent processed.);
};
$CompleteHandler = `
[Microsoft.SqlServer.Management.Common.ServerMessageEventHandler] `
{
Write-Host $_.Error.Message;
};
#endregion
Listado 3
BACKUP DATABASE successfully processed 665 pages in 0.415 seconds (12.518 MB/sec).
RESTORE DATABASE successfully processed 665 pages in 0.580 seconds (8.957 MB/sec).
Estos controladores son llamados directamente por SMO y vamos a implementar una notificacin de progreso simple, como la proporcionada
por SSMS o sqlcmd al lanzar la copia de seguridad
o la restauracin desde T-SQL. Tenga en cuenta
que el controlador de finalizacin simplemente
enva el mensaje de error habitual si no hay error
El siguiente paso es escribir una funcin genrica para conectarse a una instancia de SQL Server,
listado 4.
Se utiliza esta funcin para conectarse tanto a la
instancia de origen como a la de destino. Tenga en
cuenta que es compatible con los mtodos de
autenticacin de Windows y SQL Server (Listado 4).
Listado 4
#region Functions definition
function Get-SQLInstance($InstanceName, $Login, $Password)
{
$SQLInstance = New-Object Microsoft.SqlServer.Management.Smo.Server `
$InstanceName;
if ($Login -eq $null) {
$SQLInstance.ConnectionContext.LoginSecure = $true;
}
else {
$SQLInstance.ConnectionContext.LoginSecure = $false;
$SQLInstance.ConnectionContext.Login = $Login;
$SQLInstance.ConnectionContext.Password = $Password;
};
# Force connection to get an early error message
$SQLInstance.ConnectionContext.Connect();
return $SQLInstance;
};
51
Listado 5
Listado 6
thesolidqjournal
database administration
if
{
52
_ + $File.LogicalName + .mdf);
}
#Secondary Data File
elseif
($File.Type -eq D -and $DataFilesPath -ne )
{
$NewFile.PhysicalFileName = ($DataFilesPath + \ + $DatabaseName`
+
_ + $File.LogicalName + .ndf);
}
#Log File
elseif
($File.Type -eq L -and $LogFilesPath -ne )
{
$NewFile.PhysicalFileName = ($LogFilesPath + \ + $DatabaseName `
+
_ + $File.LogicalName + .ldf);
};
if ($NewFile.PhysicalFileName -ne $null) {
[void]$Restore.RelocateFiles.add($Newfile);
};
};
$Restore.Database = $DatabaseName;
$Restore.ReplaceDatabase = $true;
$Restore.NoRecovery = $false;
$Restore.Action = Database;
$Restore.add_PercentComplete($PercentCompleteHandler);
$Restore.add_Complete($CompleteHandler);
$Restore.SqlRestore($DestinationSQLInstance);
};
#endregion
Preparar y probar un
procedimiento de migracin
slido es importante, y las
secuencias de comandos
pueden ser muy tiles para
automatizar parte del proceso.
Consulting
thesolidqjournal
database administration
54
Listado 7
Script 1 Database Migration.ps1
Load Assemblies and Global Error Handling
Event Handlers
Functions definition
#region Main
#Parameters
$SourceSQLInstanceName = localhost\PROD1;
$DestinationSQLInstanceName = localhost\PROD2;
$BackupPath = \\M6500\Temp\;
$DataFilesPath = D:\MSSQL.PROD02\DATA;
$LogFilesPath = D:\MSSQL.PROD02\DATA;
$DatabaseNames = AdventureWorksLT, AdventureWorksDW, AdventureWorks;
#Uncomment to add sqlcmd path
#$env:Path += ;C:\Program Files\Microsoft SQL Server\100\Tools\Binn
#Main
$SourceSQLInstance = Get-SQLInstance $SourceSQLInstanceName;
$DestinationSQLInstance = Get-SQLInstance $DestinationSQLInstanceName;
foreach ($DatabaseName in $DatabaseNames)
{
Write-Host (`nBackup database [ + $DatabaseName + ]);
DatabaseFullBackup $SourceSQLInstance $DatabaseName $BackupPath;
Write-Host (`nRestore database [ + $DatabaseName + ]);
RestoreDatabaseFromFullBackup $DestinationSQLInstance $DatabaseName `
$BackupPath $DataFilesPath
$LogFilesPath;
Write-Host (`nExecuting post-migration script for database [`
+ $DatabaseName + ]);
$ScriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path;
sqlcmd.exe -S $DestinationSQLInstanceName `
-i $ScriptPath/Script 2 Post-Migration Actions.sql `
-v DatabaseName=$DatabaseName `
-o $ScriptPath/post-migration.log;
};
#endregion
55
la secuencia. Una manera de asignar valores a variables de secuencias de comandos sqlcmd es utilizar
el parmetro v, como se muestra en la secuencia
principal.
Suponiendo que la secuencia de comandos TSQL est en el mismo directorio que el de PowerShell, utilizamos la propiedad $MyInvocation.MyCommand.Path y el CmdLet Split-Path para obtener la
ruta de acceso a concatenar con el nombre del script
antes de pasarlos a la utilidad sqlcmd.exe. La misma
ruta se utiliza tambin para el archivo de registro
generada y pasada mediante el parmetro o.
Puede ocurrir, por alguna razn, que no tenga el
directorio de la utilidad sqlcmd.exe en la variable de
entorno PATH, obligando as a especificar tambin la
ruta para el comando. En su lugar, puede quitar el
comentario en el script y aadir el directorio siguiente:
Listado 8
[$(DatabaseName)];
sp_updatestats;
thesolidqjournal
database administration
56
BEGIN
IF (@@fetch_status <> -2)
BEGIN
DECLARE @sqlstmt nvarchar(max);
SET
@sqlstmt = DROP SCHEMA [ + @schema_name + ];
EXEC
sp_executesql @sqlstmt;
END
FETCH NEXT FROM #schemas INTO @schema_name;
END
CLOSE #schemas;
DEALLOCATE #schemas;
salida muy detallada. Esto es bueno, porque deseamos estar seguros de obtener toda la informacin
que necesitamos. Sin embargo, al automatizar una
migracin para varias iteraciones de prueba puede
ser tedioso hacerlo cada vez tener que ir a travs de
los registros de ejecucin T-SQL para ver si hay
algn error.
Para facilitar este paso, puede crearse un script
rpido de PowerShell que analice los registros basados en algunos patrones conocidos, como en los
ejemplos que mostramos en el listado 9.
La secuencia de comandos recorre todos los
archivos con el sufijo log en el nombre de archivo,
en un directorio determinado. Para cada archivo, primero busca la anteltima cadena en la salida generada por DBCC CHECKDB, para ver si se produjeron
errores de informacin en cualquier asignacin, o
errores de consistencia, y, a continuacin, busca
errores genricos de SQL Server.
Listado 9
Script 3 Check Post-Migration Logs.ps1
#region Regex search string definitions
$SearchDBCC = [regex] (CHECKDB found (?<AllocErr>\d*) allocation errors and `
+ (?<ConsErr>\d*) consistency errors in database (?<DBName>.*)\.);
$SearchSQLError = [regex] (Msg (?<ErrNum>\d*), Level (?<ErrLevel>\d*)`
+ , State (?<ErrState>\d*), Server (?<ErrServer>.*)`
+ , Line (?<ErrLine>\d*)\x0d\x0a(?<ErrMsg>.*)\x0d\x0a);
#endregion
dir D:\temp\*.log |
% {
Write-Output `n******************************************************;
Write-Output Searching for consistency/allocation errors in log $_
$TotalErrors = 0;
57
$log = [io.file]::ReadAllText($_);
$match = $SearchDBCC.Match($log);
while ($match.Success) {
if ($match.Groups[AllocErr].Value -ne 0 `
-or $match.Groups[ConsErr].Value -ne 0) {
Write-Output (Database + $match.Groups[DBName].Value `
+ allocation errors + $match.Groups[AllocErr].Value `
+ consistency errors + $match.Groups[ConsErr].Value);
$TotalErrors = [int]$match.Groups[AllocErr].Value `
+ [int]$match.Groups[ConsErr].Value;
};
$match = $match.NextMatch();
};
Write-Output ******************************************************;
Write-Output `Searching for generic SQL Server errors in log $_
$match = $SearchSQLError.Match($log);
while ($match.Success) {
Write-Output $match.Value;
$TotalErrors++;
$match = $match.NextMatch();
};
Write-Output ******************************************************;
if ($TotalErrors -eq 0) {
Write-Output No errors found.;
};
};
Listado 10
******************************************************
Searching for consistency/allocation errors in log D:\temp\post-migration.log
Database Northwind allocation errors 0 consistency errors 4
******************************************************
Searching for generic SQL Server errors in log D:\temp\post-migration.log
Msg 8928, Level 16, State 1, Server M6500\PROD2, Line 19
Object ID 149575571, index ID 1, partition ID 72057594039697408, alloc unit ID 72057594040942592 (type In-row data):
Page (1:283) could not be proce
ssed. See other errors for details.
Msg 8939, Level 16, State 98, Server M6500\PROD2, Line 19
Table error: Object ID 149575571, index ID 1, partition ID 72057594039697408, alloc unit ID 72057594040942592 (type
In-row data), page (1:283). Test
(IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 12584969 and -4.
Msg 8980, Level 16, State 1, Server M6500\PROD2, Line 19
Table error: Object ID 149575571, index ID 1, partition ID 72057594039697408, alloc unit ID 72057594040942592 (type
In-row data). Index node page (1
:309), slot 0 refers to child page (1:283) and previous child (0:0), but they were not encountered.
Msg 8978, Level 16, State 1, Server M6500\PROD2, Line 19
Table error: Object ID 149575571, index ID 1, partition ID 72057594039697408, alloc unit ID 72057594040942592 (type
thesolidqjournal
database administration
58
Conclusiones
Este mes hemos visto cmo utilizar una mezcla de
secuencias de comandos PowerShell y T-SQL, para
ayudarnos a automatizar la migracin de bases de
datos desde instancias de SQL Server antiguas a
otras ms recientes.
Recuerde siempre que el cdigo que figura en el
presente artculo es slo un ejemplo para mostrar
cmo se puede aprovechar PowerShell y no es adecuado para la produccin o uso de propsito general. Como de costumbre, hemos tenido que introducir algunas simplificaciones para mantener el artculo a un tamao razonable.
Por ejemplo, en el anlisis de los registros, confiamos en formatos de mensaje de error fijo. Si, por
cualquier la razn, esto cambia, se corre el riesgo de
pasar por alto algunos errores. Adems, ignoramos
deliberadamente los mensajes de Service Broker.
Por lo tanto, puede que desee crear una solucin
ms slida para su uso en produccin.
Otro ejemplo es el conjunto de acciones postmigracin. En un escenario de migracin real, normalmente es necesario llevar a cabo ms acciones,
algunas de las cuales dependen tambin de lgica
condicional.
La migracin es un tema muy interesante y hay
varias reas donde puede utilizarse PowerShell para