Wprowadzenie
Istnieje wiele sytuacji, które uzasadniają przenoszenie plików baz danych lub plików dzienników transakcji z jednego woluminu do drugiego na tym samym serwerze. Mogą to być:
- Konieczność sformatowania woluminu, zakładając, że nie został on prawidłowo sformatowany podczas instalacji programu SQL Server . Przypomnijmy, że podczas instalowania programu SQL Server do formatowania woluminów zaleca się użycie rozmiaru jednostki alokacji 64 KB. Jeśli nie zostanie to zrobione w momencie instalacji i trzeba będzie zrobić później, będzie to oczywiście wymagało zachowania kopii zapasowej bazy danych lub utworzenia nowego, odpowiednio sformatowanego woluminu i przeniesienia bazy danych do tego nowego woluminu.
- Konieczność użycia nowego wolumenu przy założeniu, że osiągnięto limity dla podstawowej pamięci . Dobrym przykładem może być limit 2 TB VMware Data Store. Tak jest w przypadku VSphere 5.0. Wyższe wersje VSphere mają znacznie wyższe limity.
- Potrzeba poprawy wydajności poprzez zarządzanie IO . Jeszcze jednym powodem, dla którego możesz chcieć przenieść pliki danych, jest wydajność. Zdarzają się przypadki, w których baza danych jest tworzona z wieloma plikami danych, które znajdują się na jednym dysku, dopóki wraz z rozwojem bazy danych nie stanie się oczywiste, że utworzyłeś „gorący region” w warstwie przechowywania. Jednym z rozwiązań byłoby tworzenie nowych plików danych i przebudowanie indeksów klastrowych, innym byłoby przenoszenie plików danych.
Scenariusz pierwszy:przenoszenie baz danych użytkowników
Kroki związane z przenoszeniem bazy danych użytkowników obejmują:
- Przełącz bazę danych w tryb offline
- Zaktualizuj katalog systemowy o nową lokalizację
- Skopiuj plik danych fizycznie do nowej lokalizacji
- Przenieś bazę danych online
Listing 1 pokazuje polecenia wykonane w celu wykonania tych kroków.
Lista 1 Przenoszenie plików danych
-- 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');
Należy zauważyć, że przy przejściu bazy danych w tryb offline liczba aktywnych sesji może opóźnić proces. Dobrym pomysłem byłoby zaplanowanie przestoju w celu wykonania tego zadania. Podczas takiego przestoju właściciel aplikacji powinien zatrzymać usługi aplikacji łączące się z bazą danych, zanim administrator DBA spróbuje przełączyć bazę danych w tryb offline. Zdarzają się przypadki, w których nie jest tak wygodnie przełączyć bazę danych w tryb offline, wtedy zamknięcie instancji byłoby najlepszą opcją. W takim przypadku podejście byłoby nieco inne:
- Zaktualizuj katalog systemowy o nową lokalizację
- Zamknij instancję
- Skopiuj żądany plik danych fizycznie do nowej lokalizacji
- Uruchom instancję
W obu podejściach koncepcja jest taka sama:obejmuje aktualizację katalogu systemowego w głównej bazie danych, a następnie fizyczne przeniesienie żądanego pliku danych. W obu przypadkach plik danych musi być czysto zamknięty. Rzućmy okiem na kroki związane z pierwszym podejściem.
Rys. 1 Sprawdź lokalizację plików danych
Pierwszym krokiem byłoby sprawdzenie stanu rzeczy na początku. Przejdź do ustawienia bazy danych w trybie offline i zmodyfikuj katalog systemowy.
Rys. 2 Ustaw bazę danych offline i zmodyfikuj katalog
Jak widać na rys. 3, po zaktualizowaniu katalogu zapytanie sys.master_files informuje nas o nowej lokalizacji, w której główna baza danych oczekuje, że plik danych będzie się znajdował, niezależnie od tego, czy fizycznie przenieśliśmy plik. Na rys. 4 widzimy również, że nie jest możliwe przełączenie bazy danych w tryb online bez uprzedniego fizycznego przeniesienia pliku do nowej lokalizacji (i zmiany nazwy pliku, aby odpowiadała nowej nazwie określonej w katalogu).
Rys. 3 Nowe lokalizacje plików
Rys. 4 Brakujący plik
Zwracamy również uwagę, że po skopiowaniu pliku tracimy poprzednie uprawnienia do pliku i SQL Server nie będzie mógł otworzyć pliku, gdy spróbujemy przełączyć bazę danych w tryb online. Musimy edytować uprawnienia do pliku i dodać nadać kontu NT SERVICE\MSSQLSERVER pełne uprawnienia do pliku.
Rys. 5 Skopiuj plik danych
Rys. 6 Uprawnienia w miejscu docelowym
Rys. 7a Uprawnienia w źródle
Rys. 7b Uprawnienia w źródle
Jeśli spróbujemy ponownie przełączyć bazę danych w tryb online bez tych uprawnień, otrzymamy błąd 0x5 (Odmowa dostępu). Gdybyśmy mieli zrobić coś takiego jak przeniesienie pliku danych za pomocą zadania agenta, okazałoby się, że konto agenta SQL Server przejmuje własność pliku i możemy przenieść bazę danych tylko dlatego, że konto agenta SQL Server jest takie samo jak konto SQL Server.
Rys. 8 Odmowa dostępu w nowym pliku danych
Zakładając, że próbujesz przełączyć bazę danych w tryb online za pomocą GUI SSMS, zobaczysz te błędy w Podglądzie zdarzeń, a także w dzienniku błędów programu SQL Server, jeśli przyjrzysz się uważnie. Ponadto, jeśli użyjesz drugiego podejścia (ponowne uruchomienie całej instancji), zauważysz, że baza danych utknęła na etapie odzyskiwania. Analiza dziennika błędów powie ci, co się naprawdę dzieje.
Lista 2 Przenoszenie plików danych za pomocą zadania agenta
/* ==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
Rys. 9 Uprawnienia do pliku danych podczas korzystania z zadania agenta
Rys. 10 Baza danych online
Automatyzacja procesu
Dla zabawy możemy zdecydować się na użycie SQL Server Agent Job do całego procesu. Konfigurujemy etap pracy dla każdego etapu naszego procesu. Może to być przydatne, jeśli chcesz zostać supergwiazdą DBA i zaplanować taką migrację na noc, gdy idziesz do domu i relaksujesz się z rodziną. Zdecydowanie chciałbyś upewnić się, że skonfigurujesz powiadomienie, które zostanie uruchomione, gdy zadanie się powiedzie, aby mieć pewność, że faktycznie zostanie wykonane podczas Twojej nieobecności.
Listing 3 Wykonywanie zadania za pomocą zadania agenta
/* ==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
Wniosek
W tym artykule widzieliśmy jeden ze sposobów przenoszenia plików baz danych użytkowników w SQL Server. Widzieliśmy również potrzebę zwrócenia uwagi na uprawnienia do pliku danych w nowej lokalizacji, aby nie napotkać błędów podczas przywracania bazy danych do trybu online. Widzieliśmy również, że możemy umieścić to wszystko w zadaniu SQL Server Agent przy użyciu podsystemów T-SQL i PowerShell. W kolejnym artykule zobaczymy dwie inne metody przenoszenia plików bazy danych do nowego woluminu.
Dalsza lektura:
Przenoszenie plików danych w SQL Server – część 2