sql >> Base de Datos >  >> RDS >> Sqlserver

Cómo mover archivos de datos en SQL Server – Parte 1

Introducción

Hay una serie de situaciones que justificarían el movimiento de archivos de base de datos o archivos de registro de transacciones de un volumen a otro en el mismo servidor. Estos pueden incluir:

  1. La necesidad de formatear el volumen suponiendo que no se formateó correctamente cuando se instaló SQL Server . Recuerde que al instalar SQL Server, se recomienda utilizar un tamaño de unidad de asignación de 64 K para formatear los volúmenes. Si esto no se hace en el momento de la instalación y debe hacerse más tarde, obviamente será necesario conservar una copia de seguridad de la base de datos o crear un volumen nuevo con el formato adecuado y mover la base de datos a este nuevo volumen.
  2. La necesidad de usar un nuevo volumen suponiendo que se hayan alcanzado los límites para el almacenamiento subyacente . Un buen ejemplo sería el límite de 2 TB de un almacén de datos de VMware. Este es el caso a partir de VSphere 5.0. Las versiones superiores de VSphere tienen límites mucho más altos.
  3. La necesidad de mejorar el rendimiento mediante la gestión de IO . Una razón más por la que puede querer mover archivos de datos es el rendimiento. Hay casos en los que se crea una base de datos con múltiples archivos de datos, todos sentados en un disco hasta que se hace evidente, a medida que crece la base de datos, que ha creado una "región caliente" en la capa de almacenamiento. Una solución sería crear nuevos archivos de datos y reconstruir índices agrupados, otra sería mover archivos de datos.

Escenario uno:Mover bases de datos de usuarios

Los pasos necesarios para mover una base de datos de usuario implican lo siguiente:

  1. Desconectar la base de datos
  2. Actualice el catálogo del sistema con la nueva ubicación
  3. Copie el archivo de datos físicamente en la nueva ubicación
  4. Poner la base de datos en línea

El Listado 1 muestra los comandos ejecutados para lograr estos pasos.

Listado 1 Moviendo archivos de datos

-- 1. Run the following statement to check the current location of files.
SELECT name, physical_name AS CurrentLocation, state_desc FROM sys.master_files WHERE database_id = DB_ID(N'BranchDB');
-- 2. Take the database offline.
ALTER DATABASE BranchDB SET OFFLINE;
-- 3. Move the file or files to the new location (at OS level).
-- 4. For each file moved, run the following statement.
ALTER DATABASE BranchDB MODIFY FILE ( NAME = WWI_UserData, FILENAME = 'N:\MSSQL\Data\WWI_UserDataNew.ndf' );
-- 5. Run the following statement.
ALTER DATABASE BranchDB SET ONLINE;
-- 6. Verify the file change by running the following query.
SELECT name, physical_name AS CurrentLocation, state_desc FROM sys.master_files WHERE database_id = DB_ID(N'BranchDB');

Es importante tener en cuenta que al desconectar una base de datos, la cantidad de sesiones activas puede retrasar el proceso. Programar un tiempo de inactividad para realizar esta tarea sería una buena idea. Durante dicho tiempo de inactividad, el propietario de la aplicación debe detener la conexión de los servicios de la aplicación a la base de datos antes de que el DBA intente desconectar la base de datos. Hay casos en los que no es tan conveniente desconectar la base de datos, entonces cerrar la instancia sería la mejor opción. En tal caso, el enfoque sería ligeramente diferente:

  1. Actualice el catálogo del sistema con la nueva ubicación
  2. Cerrar la instancia
  3. Copie el archivo de datos deseado físicamente a la nueva ubicación
  4. Iniciar la instancia

En ambos enfoques, el concepto es el mismo:implica actualizar el catálogo del sistema en la base de datos maestra y luego reubicar físicamente el archivo de datos deseado. En ambos casos, el archivo de datos debe cerrarse limpiamente. Echemos un vistazo a los pasos involucrados en el primer enfoque.

Fig. 1 Verificar la ubicación de los archivos de datos

El primer paso sería comprobar el estado de las cosas para empezar. Luego proceda a establecer la base de datos fuera de línea y modifique el catálogo del sistema.

Fig. 2 Establecer base de datos fuera de línea y modificar catálogo

Como se ve en la Fig. 3, una vez que actualizamos el catálogo, consultar sys.master_files nos dice la nueva ubicación en la que la base de datos maestra espera que esté el archivo de datos, ya sea que hayamos movido físicamente el archivo o no. En la Fig. 4, también vemos que no es posible poner la base de datos en línea sin primero mover físicamente el archivo a la nueva ubicación (y cambiar el nombre del archivo para que coincida con el nuevo nombre especificado en el catálogo).

Fig. 3 Nuevas ubicaciones de archivos

Fig. 4 Archivo faltante

También nos gustaría señalar que una vez que copiamos el archivo, perdemos los permisos anteriores en el archivo y SQL Server no podrá abrir el archivo cuando intentemos poner la base de datos en línea. Debemos editar los permisos del archivo y agregar otorgar a la cuenta NT SERVICE\MSSQLSERVER permisos completos en el archivo.

Fig. 5 Copie el archivo de datos

Fig. 6 Permisos en destino

Fig. 7a Permisos en la fuente

Fig. 7b Permisos en la fuente

Si intentáramos volver a poner la base de datos en línea sin estos permisos, obtendremos un error 0x5 (Acceso denegado). Si fuéramos a hacer algo como mover el archivo de datos usando un trabajo de agente, encontramos que la cuenta del Agente SQL Server adquiere la propiedad del archivo y podemos traer la base de datos solo porque la cuenta del Agente SQL Server es la misma que la Cuenta del Servidor SQL.

Fig. 8 Acceso denegado en nuevo archivo de datos

Suponiendo que estaba tratando de poner la base de datos en línea usando la GUI de SSMS, vería estos errores en el Visor de eventos, así como en el registro de errores de SQL Server si mira de cerca. Además, si estuviera utilizando el segundo enfoque (reiniciando toda la instancia), observaría que la base de datos se atascaría en la etapa de recuperación. Examinar el registro de errores le dirá lo que realmente está pasando.

Listado 2 Mover archivos de datos usando un trabajo de agente

/* ==Scripting Parameters==
Source Server Version : SQL Server 2017 (14.0.3023) Source Database Engine Edition : Microsoft SQL Server Standard Edition Source Database Engine Type : Standalone SQL Server
Target Server Version : SQL Server 2017 Target Database Engine Edition : Microsoft SQL Server Standard Edition Target Database Engine Type : Standalone SQL Server */
USE [msdb]
GO
/****** Object: Job [MoveDataFile] Script Date: 7/12/2018 12:33:55 AM ******/ BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT
	@ReturnCode = 0 /****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 7/12/2018 12:33:56 AM ******/
IF NOT EXISTS (SELECT
			name
		FROM msdb.dbo.syscategories
		WHERE name = N'[Uncategorized (Local)]'
		AND category_class = 1)
BEGIN
	EXEC @ReturnCode = msdb.dbo.sp_add_category @class = N'JOB'
											   ,@type = N'LOCAL'
											   ,@name = N'[Uncategorized (Local)]'
	IF (@@error <> 0
		OR @ReturnCode <> 0)
		GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name = N'MoveDataFile'
									  ,@enabled = 1
									  ,@notify_level_eventlog = 0
									  ,@notify_level_email = 0
									  ,@notify_level_netsend = 0
									  ,@notify_level_page = 0
									  ,@delete_level = 0
									  ,@description = N'No description available.'
									  ,@category_name = N'[Uncategorized (Local)]'
									  ,@owner_login_name = N'sa'
									  ,@job_id = @jobId OUTPUT
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback /****** Object: Step [MoveDataFile] Script Date: 7/12/2018 12:33:56 AM ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @jobId
										  ,@step_name = N'MoveDataFile'
										  ,@step_id = 1
										  ,@cmdexec_success_code = 0
										  ,@on_success_action = 1
										  ,@on_success_step_id = 0
										  ,@on_fail_action = 2
										  ,@on_fail_step_id = 0
										  ,@retry_attempts = 0
										  ,@retry_interval = 0
										  ,@os_run_priority = 0
										  ,@subsystem = N'PowerShell'
										  ,@command = N'Copy-Item -Path M:\MSSQL\Data\WWI_UserData1.ndf N:\MSSQL\Data\WWI_UserData1.ndf'
										  ,@database_name = N'master'
										  ,@flags = 0
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId
										 ,@start_step_id = 1
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId
											,@server_name = N'(local)'
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@trancount > 0)
	ROLLBACK TRANSACTION
EndSave:
GO

Fig. 9 Permisos en el archivo de datos al usar el trabajo del agente

Fig. 10 Base de datos en línea

Automatización del proceso

Solo por diversión, podemos decidir usar el trabajo del Agente SQL Server para todo el proceso. Configuramos un paso de trabajo para cada paso de nuestro proceso. Esto puede ser útil si desea ser un DBA superestrella y programar una migración de este tipo durante la noche mientras se va a casa y se relaja con la familia. Definitivamente querrás asegurarte de configurar una notificación para que se active cuando el trabajo se realice correctamente, de modo que estés seguro de que realmente se realiza mientras estás fuera.

Listado 3 Realización de la tarea usando un trabajo de agente

/* ==Scripting Parameters==
Source Server Version : SQL Server 2017 (14.0.3023) Source Database Engine Edition : Microsoft SQL Server Standard Edition Source Database Engine Type : Standalone SQL Server
Target Server Version : SQL Server 2017 Target Database Engine Edition : Microsoft SQL Server Standard Edition Target Database Engine Type : Standalone SQL Server */
USE [msdb]
GO
/****** Object: Job [MoveDataFile] Script Date: 7/12/2018 12:46:47 AM ******/ BEGIN TRANSACTION
DECLARE @ReturnCode INT
SELECT
	@ReturnCode = 0 /****** Object: JobCategory [[Uncategorized (Local)]] Script Date: 7/12/2018 12:46:47 AM ******/
IF NOT EXISTS (SELECT
			name
		FROM msdb.dbo.syscategories
		WHERE name = N'[Uncategorized (Local)]'
		AND category_class = 1)
BEGIN
	EXEC @ReturnCode = msdb.dbo.sp_add_category @class = N'JOB'
											   ,@type = N'LOCAL'
											   ,@name = N'[Uncategorized (Local)]'
	IF (@@error <> 0
		OR @ReturnCode <> 0)
		GOTO QuitWithRollback
END
DECLARE @jobId BINARY(16)
EXEC @ReturnCode = msdb.dbo.sp_add_job @job_name = N'MoveDataFile'
									  ,@enabled = 1
									  ,@notify_level_eventlog = 0
									  ,@notify_level_email = 3
									  ,@notify_level_netsend = 0
									  ,@notify_level_page = 0
									  ,@delete_level = 0
									  ,@description = N'No description available.'
									  ,@category_name = N'[Uncategorized (Local)]'
									  ,@owner_login_name = N'sa'
									  ,@notify_email_operator_name = N'DBA'
									  ,@job_id = @jobId OUTPUT
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback
/****** Object: Step [Set Database Offline] Script Date: 7/12/2018 12:46:47 AM ******/ EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @jobId
																																 ,@step_name = N'Set Database Offline'
																																 ,@step_id = 1
																																 ,@cmdexec_success_code = 0
																																 ,@on_success_action = 3
																																 ,@on_success_step_id = 0
																																 ,@on_fail_action = 2
																																 ,@on_fail_step_id = 0
																																 ,@retry_attempts = 0
																																 ,@retry_interval = 0
																																 ,@os_run_priority = 0
																																 ,@subsystem = N'TSQL'
																																 ,@command = N'ALTER DATABASE BranchDB SET OFFLINE;'
																																 ,@database_name = N'master'
																																 ,@flags = 0
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback /****** Object: Step [MoveDataFile] Script Date: 7/12/2018 12:46:47 AM ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @jobId
										  ,@step_name = N'MoveDataFile'
										  ,@step_id = 2
										  ,@cmdexec_success_code = 0
										  ,@on_success_action = 3
										  ,@on_success_step_id = 0
										  ,@on_fail_action = 2
										  ,@on_fail_step_id = 0
										  ,@retry_attempts = 0
										  ,@retry_interval = 0
										  ,@os_run_priority = 0
										  ,@subsystem = N'PowerShell'
										  ,@command = N'Copy-Item -Path M:\MSSQL\Data\WWI_UserData1.ndf N:\MSSQL\Data\WWI_UserData1.ndf'
										  ,@database_name = N'master'
										  ,@flags = 0
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback /****** Object: Step [ModifyFile and Bring Online] Script Date: 7/12/2018 12:46:47 AM ******/
EXEC @ReturnCode = msdb.dbo.sp_add_jobstep @job_id = @jobId
										  ,@step_name = N'ModifyFile and Bring Online'
										  ,@step_id = 3
										  ,@cmdexec_success_code = 0
										  ,@on_success_action = 1
										  ,@on_success_step_id = 0
										  ,@on_fail_action = 2
										  ,@on_fail_step_id = 0
										  ,@retry_attempts = 0
										  ,@retry_interval = 0
										  ,@os_run_priority = 0
										  ,@subsystem = N'TSQL'
										  ,@command = N' ALTER DATABASE BranchDB MODIFY FILE ( NAME = WWI_UserData, FILENAME = ''N:\MSSQL\Data\WWI_UserDataNew.ndf'' );
ALTER DATABASE BranchDB SET ONLINE;'
										  ,@database_name = N'master'
										  ,@flags = 0
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_update_job @job_id = @jobId
										 ,@start_step_id = 1
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback
EXEC @ReturnCode = msdb.dbo.sp_add_jobserver @job_id = @jobId
											,@server_name = N'(local)'
IF (@@error <> 0
	OR @ReturnCode <> 0)
	GOTO QuitWithRollback
COMMIT TRANSACTION
GOTO EndSave
QuitWithRollback:
IF (@@trancount > 0)
	ROLLBACK TRANSACTION
EndSave:
GO

Conclusión

En este artículo, hemos visto una forma de mover archivos de bases de datos de usuarios en SQL Server. También hemos visto la necesidad de asegurarnos de prestar atención a los permisos en el archivo de datos en la nueva ubicación para que no encontremos errores al volver a poner la base de datos en línea. También hemos visto que podemos poner todo esto en un trabajo del Agente SQL Server usando los subsistemas T-SQL y PowerShell. En un artículo posterior, veremos otros dos métodos para mover archivos de bases de datos a un nuevo volumen.

Lecturas adicionales:

Mover archivos de datos en SQL Server:parte 2