Posts tagged administracja

[PL] SQL Server – Przerwany łańcuch backupów różnicowych

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Ostatnio po raz pierwszy w życiu spotkałem się z przypadkiem przerwanego łańcucha backupów różnicowych w SQL Server. Zjawisko może i ciekawe, ale potrafi mocno zestresować DBA. Jak to wygląda i w czym problem?

Wyobraźmy sobie scenariusz. Masz hurtownię danych, VLDB o rozmiarze ponad 1TB. Baza pracuje w SIMPLE recovery model. Wykonanie jej pełnego backupu zajmuje jakieś 2-3 godziny. Backup pełen bazy został zrobiony kilka godzin wcześniej. Teraz po dokonaniu pewnych zmian w konfiguracji bazy decydujesz się na wykonanie backupu różnicowego (differential backup). Ale przy próbie wykonania takiego backupu pojawia się komunikat:

Msg 3035, Level 16, State 1, Line 1
Cannot perform a differential backup for database "DataWarehouse", because a current database backup does not exist.
Perform a full database backup by reissuing BACKUP DATABASE, omitting the WITH DIFFERENTIAL option.
Msg 3013, Level 16, State 1, Line 1
BACKUP DATABASE is terminating abnormally.

Ale jak to? Przecież Ty wiesz, że pełen backup bazy został wykonany i leży sobie na dysku! Co się wydarzyło przez te parę godzin, że SQL Server nie pozwala Ci wykonać kopii różnicowej? Pierwszą podpowiedź znajdziesz w errorlogu SQL Servera. W czasie, gdy wykonywał się pełen backup zapewne pojawiły się w errorlogu komunikaty w stylu:

I/O is frozen on database DataWarehouse. No user action is required. However, if I/O is not resumed promptly, you could cancel the backup.

Powyżej pewnie zobaczysz (pewnie nawet zalogowane w tej samej sekundzie):

I/O was resumed on database DataWarehouse. No user action is required.

Hmmm… Pierwsza myśl – coś z dyskiem :-) Ale to nie to.

Sprawdźmy historię backupów naszej bazy danych (nie pamiętam, skąd wziąłem skrypt, ale w razie czego można posłużyć się fajnym kawałkiem kodu z bloga Roberta Kubalskiego):

SELECT
  bs.backup_finish_date,
  DATEDIFF(second, bs.backup_start_date, bs.backup_finish_date) AS time_taken_sec,
  CASE bs.type
    WHEN 'D' THEN 'Full'
    WHEN 'L' THEN 'Log'
    WHEN 'I' THEN 'Differential'
    ELSE ''
  END AS backup_type,
  CONVERT(decimal(15, 2), bs.backup_size / (1024. * 1024)) AS backup_size_mb,
  bmf.physical_device_name
FROM   msdb.dbo.backupmediafamily  as bmf
   INNER JOIN msdb.dbo.backupset as bs ON bmf.media_set_id = bs.media_set_id
WHERE (CONVERT(datetime, bs.backup_start_date, 102) >= GETDATE() - 7)
AND bs.database_name = N'DataWarehouse'
ORDER BY bs.backup_finish_date DESC;

Przykładowy wynik:

Co widzimy? Ano, zapewne wśród wyświetlonych informacji pojawią się backupy, które w kolumnie physical_device_name będą miały dziwaczne “szlaczki” w stylu {CBE47A8D-BF0C-4C9E-B3D4-841E16793FA8}10. Ponadto, owe podejrzane backupy będą miały równie podejrzany maleńki rozmiar (w prezentowanym powyżej przykładowym wyniku pełen backup bazy o rozmiarze 1GB zajął ponoć 0.01MB). O czym świadczą te objawy (komunikaty w errorlogu, dziwne wpisy w historii backupów oraz problem z wykonaniem backupu differential)?

Problem okazał się powszechnie (?) znany i dość wiekowy, bo nawet doczekał się swojego KB: http://support.microsoft.com/kb/903643 (a jak poszukać, to jest pewnie sporo pokrewnych KB, np. http://support.microsoft.com/kb/937683). A więc wszystkiemu winien jest działający spod systemu operacyjnego program do robienia kopii zapasowych plików, który potrafi używać Volume Shadow Copy do backupowania plików baz danych nawet, gdy są one używane przez SQL Server. Niestety, robi to kosztem backupów natywnych SQL Servera. Jeśli zdarzy się, że oba backupy – natywny backup w SQL Serverze i backup z poziomu systemu operacyjnego (niekoniecznie ntbackup, bo okazuje się, że narzędzia firm trzecich też używają tego samego mechanizmu) – wykonują się w tym samym czasie, może zostać przerwany łańcuch backupów różnicowych i nie będziesz w stanie zrobić backupu differential. A to może być czasem fatalne w skutkach. Rozwiązaniem oczywistym jest – bazy danych SQL Servera backupować wyłącznie natywnym mechanizmem (z kodu T-SQL) lub narzędziami dedykowanymi do backupowania baz SQL Servera. Jednak czasem nie da się wytłumaczyć administratorowi sieci korporacyjnej, że nie powinien używać innych narzędzi do backupowania plików baz danych. Wówczas pomocne może okazać się wyłączenie usługi SQL Server VSS Writer, dzięki której możliwe jest właśnie backupowanie plików działających baz przez “windowsowe” narzędzia.

Na koniec wypadałoby napisać, jak wykrywać takie anomalie. Próbowałem przeglądać strony DIFF bazy potraktowanej narzędziem NTBackup, ale nie znalazłem żadnej (nie-)prawidłowości, która sugerowałaby, że łańcuch backupów differential jest przerwany. Jedyne, co przychodzi mi więc do głowy, to sprawdzać w historii backupów danej bazy, czy physical_device_name jednego z ostatnich backupów zawiera coś innego niż ścieżkę do pliku. Jeśli zamiast ścieżki widzisz wspomniane wyżej “krzaki”, to może się okazać, że właśnie dotknął Cię opisany problem. Jeśli ktoś ma lepszy pomysł na detektor zerwanego łańcucha backupów differential, niech się podzieli :-)

[PL] SQL Server dla DBA – Zdań kilka o SQL Trace

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Pomyślałem, że napiszę krótki wpis o SQL Trace, czyli o mechanizmie, który powinien znaleźć się w arsenale każdego DBA pracującego z SQL Serverem.

Namierzyć Profilera

Zakładam, że każdy DBA prędzej czy później musi nauczyć się używać aplikacji SQL Server Profiler. Czasem jednak śledzenie przy użyciu Profilera nie jest mile widziane na serwerze produkcyjnym. Zwłaszcza, jeśli do tego dodać, że śledzenie może odbić się negatywnie na wydajność serwera. Niejednokrotnie zdarzało mi się, że przechwytując Profilerem za dużo zdarzeń (lub zbyt ciężkich zdarzeń – np. plany wykonania), obserwowałem “zamulanie” serwera.

Jak znaleźć trace’y otwarte z użyciem Profilera? Ano tak:

SELECT id, path, file_position, reader_spid
FROM sys.traces
WHERE is_rowset = 1;

Przykładowy wynik:

image

W zasadzie po dłuższych obserwacjach doszedłem do wniosku, że można też pytać o reader_spid o wartości innej niż NULL.

Kolumny, które wybrałem w powyższym zapytaniu są w moim odczuciu najbardziej przydatne:

  • id – unikalny numer trace’a, dzięki niemu można dowiedzieć się więcej o danej sesji śledzenia (o tym za moment) lub po prostu zakończyć sesję śledzenia odpowiednimi wywołaniami procedury sp_trace_setstatus,
  • path - jeśli zapisujemy do pliku .trc (najczęstszy scenariusz poza “zwykłym” śledzeniem za pomocą Profilera), ścieżkę do aktualnego pliku znajdziemy właśnie w tej kolumnie,
  • file_position - rozmiar pliku, jeśli trace prowadzi zapis do takowego,
  • reader_spid – jeśli Profiler czyta trace, tu znajdziemy identyfikator sesji otwartej przez aplikację (i można dzięki temu poleceniem KILL sprawnie zakończyć sesję Profilera).

Co właściwie śledzi trace?

Jednym z najbardziej niedocenianych źródeł informacji dla DBA jest default trace. Jest to systemowy trace uruchamiany domyślnie wraz ze startem instancji. W widoku katalogowym sys.traces zawsze ma id równe 1. Można go włączać / wyłączać zmieniając opcję ‘default trace’. Poniżej włączanie wspomnianej opcji:

EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'default trace enabled', 1;
RECONFIGURE;

Co śledzi taki domyślny trace? Przekonajmy się:

SELECT DISTINCT e.trace_event_id, e.name
FROM sys.traces AS t
CROSS APPLY sys.fn_trace_geteventinfo(t.id) AS i
INNER JOIN sys.trace_events AS e
ON i.eventid = e.trace_event_id
WHERE t.is_default = 1
ORDER BY e.trace_event_id;

Wynik:

trace_event_id name
-------------- ------------------------------
18             Audit Server Starts And Stops
20             Audit Login Failed
22             ErrorLog
46             Object:Created
47             Object:Deleted
55             Hash Warning
69             Sort Warnings
79             Missing Column Statistics
80             Missing Join Predicate
81             Server Memory Change
92             Data File Auto Grow
93             Log File Auto Grow
94             Data File Auto Shrink
95             Log File Auto Shrink
102            Audit Database Scope GDR Event
103            Audit Schema Object GDR Event
104            Audit Addlogin Event
105            Audit Login GDR Event
106            Audit Login Change Property Event
108            Audit Add Login to Server Role Event
109            Audit Add DB User Event
110            Audit Add Member to DB Role Event
111            Audit Add Role Event
115            Audit Backup/Restore Event
116            Audit DBCC Event
117            Audit Change Audit Event
152            Audit Change Database Owner
153            Audit Schema Object Take Ownership Event
155            FT:Crawl Started
156            FT:Crawl Stopped
157            FT:Crawl Aborted
164            Object:Altered
167            Database Mirroring State Change
175            Audit Server Alter Trace Event
218            Plan Guide Unsuccessful

Sporo, bo aż 35 rodzajów zdarzeń. Ale są to na ogół zdarzenia, które nie występują z dużą częstotliwością.

W podobny sposób, a używając jedynie innych funkcji i widoków katalogowych, można dowiedzieć się o tym, jakich kolumn dla każdego zdarzenia używa trace, oraz jakie są w nim ustawione filtry.

Default trace w użytku

Kilka przykładowych zastosowań default trace’a, niektóre podpatrzone w Management Studio ;-)

Zmiany w konfiguracji serwera

SELECT
  e.StartTime,
  e.SessionLoginName,
  e.TextData
FROM sys.traces AS t
CROSS APPLY sys.fn_trace_gettable(
  LEFT(t.path, LEN(t.path)-PATINDEX('%\%', REVERSE(t.path))) + '\log.trc',
  DEFAULT
) AS e
WHERE t.is_default = 1
AND (
  -- Error = 15457 - common sp_configure message
  (e.EventClass = 22 AND e.Error = 15457)
  OR
  -- DBCC event
  (e.EventClass = 116 AND e.TextData LIKE '%TRACEO%(%')
)
ORDER BY e.StartTime DESC;

Wynik:

image

Warte uwagi jest chyba tylko to, że pierwszy plik default trace’a ma nazwę log.trc. Ścieżkę da się odczytać z kolumny path widoku sys.traces. Reszta zapytania bez historii – wybrać dwa rodzaje zdarzeń (Errorlog oraz DBCC) dla trace’a, dla którego w kolumnie is_default w widoku sys.traces widnieje wartość 1.

DDL wykonywany na obiektach w bazach danych

SELECT
  e.StartTime,
  e.SessionLoginName,
  e.DatabaseName,
  e.ObjectName,
  v.subclass_name AS ObjectType,
  CASE e.EventClass
    WHEN 46 THEN 'CREATE'
    WHEN 47 THEN 'ALTER'
    WHEN 164 THEN 'DROP'
  END AS SchemaChange
FROM sys.traces AS t
CROSS APPLY sys.fn_trace_gettable(
  LEFT(t.path, LEN(t.path)-PATINDEX('%\%', REVERSE(t.path))) + '\log.trc',
  DEFAULT
) AS e
INNER JOIN sys.trace_subclass_values AS v
ON    v.trace_event_id = e.EventClass
  AND v.trace_column_id = 28
  AND v.subclass_value = e.ObjectType
WHERE t.is_default = 1
AND e.EventClass IN (46, 47, 164)
AND e.EventSubClass = 0 -- eliminate junk
AND e.DatabaseID <> 2 -- not tempdb
AND e.ObjectType <> 21587 -- not stats
ORDER BY e.StartTime DESC;

Wynik:

image

Problemem może być odczytanie schematu obiektu, jeśli obiekt skasowano. Komentarz “eliminate junk” dodałem obok filtra, który pozwolił na odfiltrowanie wierszy mówiących o operacjach na statystykach.

Notka: dwa powyższe przykłady wziąłem ze standardowych raportów dostępnych w Management Studio pod prawym przyciskiem na folderze Databases w oknie Object Explorer.

Automatyczne zmiany rozmiaru plików

SELECT
  e.StartTime,
  CASE e.EventClass
    WHEN 92 THEN 'Data File Auto Grow'
    WHEN 93 THEN 'Log File Auto Grow'
    WHEN 94 THEN 'Data File Auto Shrink'
    WHEN 95 THEN 'Log File Auto Shrink'
  END AS EventDesc,
  e.DatabaseName,
  e.FileName
FROM sys.traces AS t
CROSS APPLY sys.fn_trace_gettable(
  LEFT(t.path, LEN(t.path)-PATINDEX('%\%', REVERSE(t.path))) + '\log.trc',
  DEFAULT
) AS e
WHERE t.is_default = 1
AND e.EventClass IN (92, 93, 94, 95)
ORDER BY e.StartTime DESC;

Wynik:

image

Czarna skrzynka

SQL Server daje możliwość uruchomienia szybkiego w konfiguracji trace’a – tzw. BlackBox trace’a.

Kod do jego stworzenia i uruchomienia jest naprawdę krótki:

DECLARE @trace_id int;
EXEC sp_trace_create @trace_id OUT, 8;
EXEC sp_trace_setstatus @trace_id, 1;

Co śledzi taki trace?

SELECT DISTINCT e.trace_event_id, e.name
FROM sys.traces AS t
CROSS APPLY sys.fn_trace_geteventinfo(t.id) AS ei
CROSS APPLY sys.fn_trace_getinfo(t.id) AS i
INNER JOIN sys.trace_events AS e
ON ei.eventid = e.trace_event_id
WHERE i.property = 1 AND i.value = 8
ORDER BY e.trace_event_id;

Zapytanie zwraca raptem cztery rodzaje zdarzeń:

trace_event_id name
-------------- -----------------
11             RPC:Starting
13             SQL:BatchStarting
16             Attention
33             Exception

Do czego można użyć takiego śledzenia? Szybkie uruchomienie, dość zgrubne zdarzenia… Jeśli już, używałbym w sytuacji, gdy chciałbym dowiedzieć się, co stało się tuż przed “reprodukowalnym” problemem :-) Tak czy owak, jeśli ktoś kiedyś tego użył produkcyjnie, chętnie poczytam o innych zastosowaniach.

BlackBox trace’a znajdujemy w widoku sys.traces używając funkcji sys.fn_trace_getinfo (@options = 8). Swoje dane zapisuje on do dwóch plików o łącznym maksymalnym rozmiarze 10 MB (to mniej niż default trace, który zapisuje do 5 plików po maks. 20 MB każdy). Nowe zdarzenia w obu trace’ach – default i BlackBox – nadpisują stare zdarzenia, jeśli osiągnięty został maksymalny rozmiar ostatniego z dostępnych dla danego trace’a plików. Pliki obu trace’ów zapisywane są w folderze Log w ścieżce roboczej SQL Servera (a więc tam, gdzie ERRORLOG).

Podsumowanie

Sądzę, że warto poznać możliwości obu wymienionych trace’ów. Choćby po to, by pobawić się w wyciąganie metadanych trace’ów albo poznać lepiej procedury do ich obsługi. Nie polecam też wyłączania default trace’a, chyba, że polityka bezpieczeństwa firmy lub organizacji wyraźnie mówi o tym, że należy go wyłączyć.

Jeśli masz jakieś ciekawe przemyślenia na temat obu trace’ów albo znasz jakieś ciekawostki dotyczące trace’ów ogólnie, podziel się nimi (mogę je opublikować jako dodatek do tego wpisu).

[PL] SQL Server dla DBA – sys.dm_os_performance_counters

VN:F [1.7.9_1023]
Rating: 5.0/5 (2 votes cast)

W komentarzach do pierwszego wpisu z serii SQL Server dla DBA Robert Kubalski przypomniał o istnieniu widoku sys.dm_os_performance_counters, który może być źródłem cennych informacji o instancji SQL Server i bazach danych istniejących w ramach owej instancji. Pomyślałem, że warto by było napisać, w jaki sposób DBA może interpretować wartości zwracane w wyniku zapytania do wspomnianego widoku.

Pierwsze podejście – typy liczników

Widok dynamiczny sys.dm_os_performance_counters umożliwia podejrzenie wartości liczników monitora wydajności (perfmon.exe) dedykowanych konkretnej instancji SQL Server (tej, z której zadajemy zapytanie do widoku). Jednak nie wszystkie wartości liczbowe można bezpośrednio interpretować. Aby dowiedzieć, w jaki sposób należy interpretować wartości zwracane dla poszczególnych liczników, trzeba poznać typy liczników.

Istnieje kilka typów liczników (są to typy liczników WMI), a listę ich kodów można uzyskać w prosty sposób:

SELECT DISTINCT cntr_type
FROM sys.dm_os_performance_counters
ORDER BY cntr_type;

Oto owe typy liczników i ich interpretacja:

  • 65792 - PERF_COUNTER_LARGE_RAWCOUNT – obrazuje ostatnio przechwyconą wartość licznika; tego typu liczniki interpretujemy bezpośrednio – odczytana wartość może być od razu interpretowana (bez przeliczania)
  • 272696576 - PERF_COUNTER_BULK_COUNT – oznacza licznik, którego wartości są liczone per zadany interwał czasowy; do wyliczenia sensownej wartości takiego potrzebne są dwa pomiary (a następnie różnicę między pomiarami należy podzielić przez ilość jednostek czasu, np. ilość sekund)
  • 537003264PERF_LARGE_RAW_FRACTION – taki licznik używany jest do obliczenia ilorazu reprezentującego wskaźnik procentowy (jest podstawiany w liczniku dzielenia, a więc do pełni szczęścia potrzebujemy mianownika – patrz typ PERF_LARGE_RAW_BASE poniżej); niektóre liczniki (np. Buffer Cache Hit Ratio) są reprezentowane jako wartości procentowe i powstają jako wynik dzielenia licznika typu 537003264 przez licznik typu 1073939712
  • 1073874176 - PERF_AVERAGE_BULK – ten licznik jest używany do obliczenia ilorazu reprezentującego średnią wartość(podstawiany w liczniku dzielenia – znów potrzebny jest mianownik w postaci licznika typu PERF_LARGE_RAW_BASE); niektóre liczniki (np. Average Wait Time (ms)) reprezentują wartości średnie i powstają jako wynik dzielenia wartości licznika typu 1073874176 przez wartości odpowiedniego licznika typu 1073939712
  • 1073939712 - PERF_LARGE_RAW_BASE – licznik używany do obliczenia ilorazu (podstawiany w mianowniku dzielenia); stanowi parę z licznikiem typu 537003264 lub 1073874176.

A zatem, jeśli zobaczysz takie dwa liczniki:

image

znając typy liczników możesz wywnioskować, że należy podzielić wartość z kolumny cntr_value dla licznika Buffer cache hit ratio przez wartość z tej samej kolumny dla licznika Buffer cache hit ratio base (w tym konkretnym przypadku otrzymujemy wartość 1, co oznacza, że instancja notuje idealnie 100% trafień w bufor danych).

Drugie podejście – licznik, obiekt, instancja

Liczniki są podzielone pomiędzy obiekty. Obiekt można traktować jak kategorię. Oczywiście, są to te same obiekty, które instancja SQL Server dodaje do listy obiektów do użycia w monitorze wydajności. I jednocześnie trzeba pamiętać, że widok sys.dm_os_performance_counters zwraca wyłącznie wartości tych liczników, które pochodzą od danej instancji, na której wykonujemy zapytanie. Dla niektórych liczników, tak jak w perfmon.exe istnieje wiele instancji. Przykładem mogą być liczniki mówiące o rozmiarze dzienników transakcji w bazach danych oraz ilości miejsca zajętego w każdym z dzienników transakcji:

SELECT *
FROM sys.dm_os_performance_counters
WHERE counter_name LIKE '%Log File%';

image

Trzecie podejście – przykładowe zapytania i liczniki

O ile zapytanie o wszystkie liczniki nie obciąża praktycznie serwera (liczba odczytów i zużycie procesora są niemal zerowe, a sam widok zwraca co najwyżej kilka tysięcy wierszy – liczba wierszy zależy od ilości baz danych na instancji), o tyle zazwyczaj interesuje nas konkretny licznik lub grupa liczników.

Page Life Expectancy

Page Life Expectancy z obiektu Buffer Manager czyli czas życia stron w pamięci mierzony w sekundach. Ten licznik przydaje się, by zweryfikować, czy przypadkiem strony danych z pamięci nie są zrzucane do pliku stronicowania. Gwałtowny spadek tego licznika zazwyczaj mówi o opróżnieniu bufora danych – czy to celowym, czy wymuszonym przez system (prosty sposób na stwierdzenie braku ustawionego uprawnienia Lock pages in memory).

SELECT
  object_name,
  counter_name,
  cntr_value
FROM sys.dm_os_performance_counters
WHERE [object_name] LIKE '%Buffer Manager%'
AND counter_name = 'Page Life Expectancy';

Przykładowy wynik (niski, bo zmierzony tuż po restarcie usługi):

image

Przy okazji warto zauważyć, że kolumny tekstowe widoku sys,dm_os_performance_counters są typu nchar(128), a więc nazwy obiektów, liczników oraz instancji są dopełniane do 128 znaków spacjami. Jest to istotna sprawa przy pisaniu zapytań, zwłaszcza z użyciem operatora LIKE (warto dokładać znak % na końcu wzorca, by nie nadziać się na pułapkę związaną z dodatkowymi spacjami w nazwach).

Buffer cache hit ratio

Buffer cache hit ratio z obiektu Buffer Manager czyli procent trafień zapytań w strony znajdujące się w buforze danych. Im bliżej wartości 1 (oznaczającej 100%), tym lepiej.

SELECT
  c.object_name,
  c.counter_name,
  CASE
    WHEN b.cntr_value = 0 THEN 0
    ELSE CONVERT(numeric(38,2), c.cntr_value * 1.0 / b.cntr_value)
  END AS value
FROM sys.dm_os_performance_counters AS c
INNER JOIN sys.dm_os_performance_counters AS b
ON c.object_name = b.object_name
AND b.counter_name LIKE RTRIM(c.counter_name) + '%'
AND c.instance_name = b.instance_name
AND b.cntr_type = 1073939712
WHERE c.[object_name] LIKE '%Buffer Manager%'
AND c.counter_name = 'Buffer cache hit ratio';

Przykładowy wynik:

image

Powyższą technikę odszukiwania licznika i mianownika do obliczania ilorazu można stosować do większości liczników, których wartości wyliczamy z ilorazu. Są jednak przypadki, kiedy nazwa licznika i nazwa współczynnika używanego w mianowniku ilorazu różnią się nie tylko słowem “Base”. Przykładem może być licznik Avg. Time Between Batches (ms) z obiektu Broker TO Statistics:

image

Połączenia i aktywne transakcje

Liczniki Active Transactions z obiektu Databases oraz User Connections z obiektu General statistics pozwalają zorientować się, jaka jest aktualna liczba połączeń do instancji SQL Server oraz ile aktywnych transakcji jest otwartych w kontekście każdej bazy danych.

SELECT
  [object_name],
  counter_name,
  instance_name,
  cntr_value
FROM sys.dm_os_performance_counters
WHERE counter_name IN (
'Active Transactions',
'User Connections'
);

Przykładowy wynik:

image

Podsumowanie

Widok sys.dm_os_performance_counters daje DBA możliwość śledzenie pokaźnej ilości liczników mówiących wiele o kondycji instancji SQL Server. Co ważne, administrator nie musi korzystać z żadnych zewnętrznych narzędzi. Może wykorzystać widok do zbierania danych wydajnościowych do celów analizy, budowania tzw. baseline, klasycznego monitorowania, itd.

Widok ten jest szeroko wykorzystywany przez różne narzędzia firm trzecich (np. Quest Spotlight), jak również przez narzędzia dostępne w SQL Server (np. Performance Studio). Warto się nim zainteresować, zwłaszcza, jeśli naszym celem jest zbudowanie własnego rozwiązania do monitorowania serwera (patrz także wpis pt. SQL Server – Interaktywny performance dashboard dla ubogich).

[PL] SQL Server dla DBA – zapomniany widok sys.master_files

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Pomyślałem ostatnio, że fajnie by było, jakbym częściej umieszczał nieco prostsze, acz użyteczne kawałki kodu T-SQL, głównie dedykowane administratorom baz danych. Na dobry początek wykorzystanie często zapominanego i niedocenianego widoku systemowego sys.master_files istniejącego w SQL Server 2005 i nowszych wersjach.

Widok ten jest znakomitym źródłem informacji na temat plików baz danych. Można się dzięki niemu szybko zorientować w takich tematach, jak:

  • czy pliki bazy leżą na tym samym dysku logicznym,
  • jaki jest rozmiar oraz parametry automatycznego powiększania każdego z plików,
  • ile plików jakiego typu wchodzi w skład każdej z baz danych,
  • jaki jest status każdego pliku (OFFLINE / ONLINE, READ_ONLY, etc.),
  • jaki był ostatni log sequence number (LSN) backupu wykonanego na pliku.

Z mojej perspektywy najbardziej przydatna informacja to (oczywiście?) lokalizacja i rozmiar plików (kolumny physical_name oraz size). Rozmiar w kolumnie size liczony jest w 8-kilobajtowych stronach, więc po drodze trzeba wykonać kilka prostych obliczeń, by uzyskać rozmiar pliku na przykład wyrażony w megabajtach. Poniższe zapytanie jest przykładem, jak z widoku sys.master_files można odczytać łączny rozmiar plików każdego rodzaju (log, dane, etc.) dla każdej z baz danych.

SELECT
DB_NAME(database_id) AS database_name,
CONVERT(
numeric(38,2),
SUM(
CASE type
WHEN 0 THEN size * 8192.0 / (1024 * 1024)
ELSE 0
END
)
) AS data_size_mb,
CONVERT(
numeric(38,2),
SUM(
CASE type
WHEN 1 THEN size * 8192.0 / (1024 * 1024)
ELSE 0
END
)
) AS log_size_mb,
CONVERT(
numeric(38,2),
SUM(
CASE type
WHEN 2 THEN size * 8192.0 / (1024 * 1024)
ELSE 0
END
)
) AS filestream_size_mb,
CONVERT(
numeric(38,2),
SUM(
CASE type
WHEN 4 THEN size * 8192.0 / (1024 * 1024)
ELSE 0
END
)
) AS fulltext_size_mb
FROM sys.master_files
GROUP BY database_id
ORDER BY DB_NAME(database_id);

Powyższy skrypt umożliwia natychmiastową odpowiedź na pytania w stylu: “która baza jest największa?”, “która baza używa filestream?”, “gdzie log jest większy niż dane?”.

[EDYCJA 2011-01-26]: Czytelnik o nicku wacio słusznie zwrócił mi uwagę, że dla kontenerów FILESTREAM w widoku sys.master_files kolumna size zawsze przyjmuje wartość 0. Dzięki za czujność. Dla mnie to oczywisty bug, ale na szczęście oczywiste obejście tego problemu jest podane w BOL dla Denali: This field is populated as zero for FILESTREAM containers. Query the sys.database_files catalog view for the actual size of FILESTREAM containers. Mimo to, nadal uważam, że sys.master_files jest ciekawym i przydatnym widokiem :-)

[PL] SQL Server – Logowanie wejść sysadminów na serwer

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Wstęp

Jakiś czas temu napisałem, dlaczego uważam, że logon trigger nie jest dobrym rozwiązaniem w sytuacji, gdy zależy nam na logowaniu udanych prób logowania do instancji SQL Servera członków roli sysadmin. Napisałem wówczas, że mechanizmem, który nadaje się do takiego zadania, są Extended Events. Ale okazało się, że Extended Events nie dysponują zdarzeniem, które takie logowanie by umożliwiło. W międzyczasie wpadłem na pomysł, by zastosować w takim przypadku events notifications (dostępne od SQL Server 2005).

Rozwiązanie

Zadanie: zapisywać do tabeli wszystkie udane próby logowania członków roli sysadmin.

Rozwiązanie:

-- (0) Wlaczamy Service Brokera, jezeli jest wylaczony
IF EXISTS (SELECT * FROM sys.databases WHERE name = N'msdb' AND is_broker_enabled = 0)
  ALTER DATABASE msdb SET ENABLE_BROKER WITH ROLLBACK IMMEDIATE;
GO

-- (1) Wybieramy baze msdb, bo zawsze jest...
USE msdb;
GO

-- (2) Kasujemy obiekty, ktore zaraz utworzymy
IF EXISTS (SELECT * FROM sys.server_event_notifications WHERE name = N'AuditSysadminLoginNotification')
  DROP EVENT NOTIFICATION AuditSysadminLoginNotification ON SERVER;
GO
IF EXISTS (SELECT * FROM sys.services WHERE name = N'AuditSysadminLoginService')
  DROP SERVICE AuditSysadminLoginService;
GO
IF EXISTS (SELECT * FROM sys.service_queues WHERE name = N'AuditSysadminLoginQueue' AND [schema_id] = 1)
  DROP QUEUE dbo.AuditSysadminLoginQueue;
GO

-- (3) Tworzymy obiekty: kolejke, usluge i powiadomienie o zdarzeniu
CREATE QUEUE dbo.AuditSysadminLoginQueue WITH STATUS = OFF;
GO
CREATE SERVICE AuditSysadminLoginService
ON QUEUE AuditSysadminLoginQueue
(
[http://schemas.microsoft.com/SQL/Notifications/PostEventNotification]
);
GO
CREATE EVENT NOTIFICATION AuditSysadminLoginNotification
ON SERVER
FOR AUDIT_LOGIN
TO SERVICE 'AuditSysadminLoginService', 'current database';
GO

-- (4) Tworzymy tabele na potrzeby logowania
IF OBJECT_ID(N'dbo.SysadminLogins', N'U') IS NOT NULL
  DROP TABLE dbo.SysadminLogins;
GO
CREATE TABLE dbo.SysadminLogins (
  LoginID int NOT NULL IDENTITY(1,1) PRIMARY KEY,
  LoginName sysname NOT NULL,
  ApplicationName nvarchar(256) NULL,
  HostName nvarchar(128),
  LoginDate datetime NOT NULL DEFAULT(GETDATE())
);
GO

-- (5) Tworzymy procedure do logowania
IF  OBJECT_ID(N'dbo.usp_LogSysadminLogin', N'P') IS NOT NULL
  DROP PROCEDURE dbo.usp_LogSysadminLogin
GO
CREATE PROC dbo.usp_LogSysadminLogin
AS
DECLARE
  @ErrorMessage nvarchar(4000),
  @EventData xml,
  @LoginName sysname,
  @ApplicationName nvarchar(256),
  @HostName nvarchar(128),
  @ConversationHandle uniqueidentifier;
WHILE (1=1)
BEGIN
  BEGIN TRAN;
  BEGIN TRY;
    RECEIVE TOP (1)
      @EventData = CAST(message_body AS xml),
      @ConversationHandle = [conversation_handle]
    FROM dbo.AuditSysadminLoginQueue;
    IF @@ROWCOUNT = 0
    BEGIN
      IF @@TRANCOUNT > 0
      BEGIN
          ROLLBACK;
      END;
      BREAK;
    END;
    SELECT
      @LoginName = @EventData.value('(/EVENT_INSTANCE/LoginName/text())[1]', 'sysname'),
      @ApplicationName = @EventData.value('(/EVENT_INSTANCE/ApplicationName/text())[1]', 'nvarchar(256)'),
      @HostName = @EventData.value('(/EVENT_INSTANCE/HostName/text())[1]', 'nvarchar(128)');
    IF IS_SRVROLEMEMBER(N'sysadmin', @LoginName) = 1 BEGIN
      INSERT INTO dbo.SysadminLogins (LoginName, ApplicationName, HostName)
      SELECT @LoginName, @ApplicationName, @HostName;
    END;
    IF @@TRANCOUNT > 0
      COMMIT;
  END TRY
  BEGIN CATCH;
    IF @@TRANCOUNT > 0
    BEGIN
        ROLLBACK;
        END CONVERSATION @ConversationHandle;
        BREAK;
    END;
  END CATCH;
END;
GO

-- (6) Przypinamy procedure do kolejki i wlaczamy kolejke
ALTER QUEUE dbo.AuditSysadminLoginQueue
  WITH STATUS = ON,
  RETENTION = OFF,
  ACTIVATION (
    STATUS = ON ,
    PROCEDURE_NAME = msdb.dbo.usp_LogSysadminLogin,
    MAX_QUEUE_READERS = 2,
    EXECUTE AS OWNER
  );
GO

-- (7) Testujemy, czy cos sie zalogowalo
SELECT * FROM msdb.dbo.SysadminLogins;

Komentarz do kodu:

  • jako bazę docelową, w której zapisuję informacje o zdarzeniach, wybieram bazę msdb (zawsze jest na serwerze, domyślnie ma włączonego Service Brokera),
  • w kroku (0) włączam na bazie msdb Service Brokera, jeżeli jeszcze nie jest on włączony,
  • w kroku (2) dbam o to, by obiekty, które zamierzam stworzyć, nie istniały na serwerze i w bazie msdb,
  • w kroku (3) tworzę obiekty niezbędne do asynchronicznego przechwytywania zdarzeń: kolejkę, usługę oraz event notification (na poziomie serwera, przechwytuje zdarzenia AUDIT_LOGIN – logowania do instancji SQL Servera),
  • w kroku (4) tworzę tabelę, do której będą zrzucane informacje o logowaniu sysadminów (login, nazwa użytej aplikacji klienckiej, nazwa maszyny klienckiej oraz data i czas logowania),
  • w kroku (5) tworzę procedurę składowaną, która zostanie użyta do wybrania informacji z tego, co przechwyci event notification i zwróci za pomocą funkcji EVENTDATA, wybrane informacje zostaną zapisane do tabeli utworzonej w kroku (4), do wychwycenia loginów będących członkami roli sysadmin używam funkcji IS_SRVROLEMEMBER,
  • w kroku (6) przypinam procedurę z kroku (5) do kolejki z kroku (3) i włączam kolejkę, od tej pory zdarzenia klasy AUDIT_LOGIN są już przechwytywane, a stosowne informacje zapisywane w tabeli z kroku (4),
  • w kroku (7) prezentuję zapytanie, dzięki któremu możesz sprawdzać, czy jakieś zdarzenie zostało już zalogowane w tabeli z kroku (4).

Przykładowe informacje o zdarzeniach logowania sysadminów:

image

Podsumowanie

Zastosowanie event notification ma kilka zalet: nie ingeruje w proces logowania (nie ma takiej możliwości, by sysadmin nie zalogował się z powodu błędu w procesie logowania zdarzeń, jak to mogło mieć miejsce w przypadku zastosowania logon triggera), asynchroniczność zapewnia niezłą skalowalność i działanie mechanizmu nawet w przypadku dużego natężenia zdarzeń, ewentualne informacje o błędach są do znalezienia w errorlogu instancji SQL Servera (np. błędy wynikające z wykrycia “zatrutej wiadomości” – poison message) lub w widoku systemowym sys.transmission_queue (w kolumnie transmission_status znajdziesz informację, dlaczego komunikat o zdarzeniu utknął w czasie przesyłania). Co ciekawe, wydaje mi się, że można znaleźć naprawdę sporo kategorii zdarzeń, dla przechwytywania których event notifications nadają się idealnie (m.in. DEACLOCK_GRAPH i BLOCKED_PROCESS_REPORT). Używając event notifications możesz zbudować całkiem niezły framework do monitorowania SQL Servera. Polecam zabawę tym mechanizmem.

[PL] SQL Server 2008 – Szybkie tworzenie polis

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Ostatnio zdarzyło mi się parę razy użyć mechanizmu Policy-Based Management dostępnego w SQL Server 2008 (i 2008 R2). Zacząłem eksperymentować i szukać wszelkich opcji, które umożliwiłyby w miarę szybkie i sprawne tworzenie polis takiemu leniwemu DBA, jak ja :-)

Jedną z takich opcji jest opcja dostępna w menu kontekstowym okna Object Explorer w Management Studio. Klikamy na przykład prawym przyciskiem myszy na nazwie instancji SQL Servera i wybieramy opcję Facets.

Facets

Pojawia się okno właściwości obiektu (w tym przypadku serwera) w kontekście wybranego szablonu (pole Facet). A na samym dole okna… Tada! Przycisk Export Current State as Policy :-)

Export as policy

Klikamy, zapisujemy polisę do pliku XML albo od razu na instancję SQL Server, otwieramy świeżutko utworzoną polisę, edytujemy. Czysta przyjemność! A wszystko w oparciu o przygotowany wzorzec serwera, bazy, obiektu w bazie danych (można sprawdzić, że pozycja Facets pojawia się w menu kontekstowym po kliknięciu w zasadzie na cokolwiek w oknie Object Explorer).

Nie powiem, zabawka w sam raz dla takich leniuszków, jak ja. Łatwiej zrobić polisę opartą o nieco bardziej rozbudowany warunek i usuwać zbędne właściwości z warunku, niż tworzyć wszystko rękami w pocie czoła ;-)

[PL] SQL Server – Kontrola dzienników transakcji

VN:F [1.7.9_1023]
Rating: 5.0/5 (1 vote cast)

Jednymi z problemów najczęściej poruszanych na forum poświęconym SQL Serverowi na portalu WSS.pl są wszelkiej maści kłopoty wynikające z zaniedbania dziennika transakcji bazy danych. Rekordziści prześcigają się w wielkości plików dziennika transakcji (185 GB dla bazy kilkugigabajtowej to dobry przykład). Często padają pytania, jak to wszystko (recovery model, dziennik transakcji, backup dziennika transakcji) działa. Nie zamierzam się rozpisywać o podstawach logowania operacji DML czy o tym, jak zbudowany jest dziennik transakcji. Pomyślałem za to, że fajnie byłoby napisać skrypt dla DBA lub konsultanta, dzięki któremu będzie można, wchodząc na “cudzy” (lub, o zgrozo, także swój!) serwer, zdiagnozować podstawowe problemy związane z higieną dziennika transakcji (a w zasadzie z brakiem tej higieny). Wspomniany skrypt prezentuje się tak:

SET NOCOUNT ON;
IF OBJECT_ID('tempdb.dbo.#VLFs', 'U') IS NOT NULL
  DROP TABLE #VLFs;
CREATE TABLE #VLFs (
  FileId int NULL,
  FileSize bigint NULL,
  StartOffset bigint NULL,
  FSeqNo bigint NULL,
  Status tinyint NULL,
  Parity smallint NULL,
  CreateLSN numeric(25,0) NULL
);
IF OBJECT_ID('tempdb.dbo.#NumberOfVLFs', 'U') IS NOT NULL
  DROP TABLE #NumberOfVLFs;
CREATE TABLE #NumberOfVLFs (
  DatabaseName sysname NOT NULL,
  NumberOfVLFs int NOT NULL
);
DECLARE @DBName sysname, @SQL nvarchar(4000);
DECLARE DBCursor CURSOR LOCAL STATIC
FOR
  SELECT name FROM sys.databases WHERE state_desc = 'ONLINE';
OPEN DBCursor;
FETCH NEXT FROM DBCursor INTO @DBName;
WHILE @@FETCH_STATUS = 0 BEGIN
  DELETE FROM #VLFs;
  SET @SQL =
  'INSERT INTO #VLFs
   EXEC ' + QUOTENAME(@DBName)
     + '.dbo.sp_executesql N''DBCC LOGINFO WITH NO_INFOMSGS'';
   INSERT INTO #NumberOfVLFs (DatabaseName, NumberOfVLFs)
   SELECT ' + QUOTENAME(@DBName, '''') + ', COUNT(*)
   FROM #VLFs;';
  EXEC (@SQL);
  FETCH NEXT FROM DBCursor INTO @DBName;
END
CLOSE DBCursor;
DEALLOCATE DBCursor;
IF OBJECT_ID('tempdb.dbo.#LogHealth', 'U') IS NOT NULL
  DROP TABLE #LogHealth;
CREATE TABLE #LogHealth (
  DatabaseName sysname NULL,
  LogSizeMB numeric(38,2) NULL,
  LogSpaceUsed numeric(4,2) NULL,
  Status tinyint NULL
);
INSERT INTO #LogHealth EXEC ('DBCC SQLPERF(LOGSPACE) WITH NO_INFOMSGS');
SELECT
  v.DatabaseName AS [Database name],
  d.recovery_model_desc AS [Recovery model],
  CASE
    WHEN d.recovery_model = 1
      AND r.last_log_backup_lsn IS NOT NULL
    THEN 1
    ELSE 0
  END AS [Was backup taken],
  q.number_of_files AS [Number of log files],
  l.LogSizeMB AS [Log size (MB)],
  l.LogSpaceUsed AS [Log space used (%)],
  v.NumberOfVLFs AS [Number of VLFs],
  d.log_reuse_wait_desc AS [Log reuse wait cause]
FROM #NumberOfVLFs AS v
INNER JOIN #LogHealth AS l
ON v.DatabaseName = l.DatabaseName
INNER JOIN sys.databases AS d
ON v.DatabaseName = d.name
INNER JOIN sys.database_recovery_status AS r
ON d.database_id = r.database_id
INNER JOIN (
  SELECT
    m.database_id,
    COUNT(*) AS number_of_files
  FROM sys.master_files AS m
  WHERE m.type = 1
  GROUP BY m.database_id
) AS q
ON d.database_id = q.database_id;
GO

Wynikiem działania powyższego kodu jest coś takiego:

Wynik

Co oznaczają kolejne kolumny?

  • Database name – nazwa bazy danych (powinny pokazać się wszystkie bazy)
  • Recovery model – tryb pracy dziennika transakcji (dla produkcyjnych baz zalecany FULL)
  • Was backup taken – wskazuje, czy został wykonany backup rozpoczynający działanie bazy w trybie FULL
  • Number of log files – ilość plików dziennika transakcji, w zasadzie zawsze powinien być 1 plik (wyjątkiem są sytuacje, gdy musimy dodać drugi lub kolejny plik dziennika transakcji z uwagi na brak miejsca na dysku przechowującym pierwszy plik dziennika)
  • Log size (MB) – rozmiar dziennika transakcji w megabajtach (wiadomo – duży może być zwłaszcza, gdy dziennik transakcji jest zaniedbany)
  • Log space used (%) – ile procent dziennika transakcji jest zajęte przez zapisane w nim transakcje (tak dowiemy się, ile brakuje do tego, by dziennik transakcji automatycznie się rozrósł)
  • Number of VLFs – ilość wirtualnych plików dziennika transakcji (ang. Virtual Log File); zalecane jest poniżej 50, ale rząd wielkości więcej – 300-500 to też nie jest wielka tragedia; ale jeżeli liczba idzie w dziesiątki / setki tysięcy, można spodziewać się problemów przy starcie bazy oraz dłuższego czasu wykonywania backupów i przywracania bazy
  • Log reuse wait cause – informacja na temat przyczyny braku możliwości ponownego zapełniania plików dziennika transakcji od pierwszych plików wirtualnych (najczęściej otwarte transakcje, ale przyczyn może być więcej)

Sam kod wykorzystuje kilka widoków i poleceń DBCC:

  • sys.databases – widok systemowy zawierający listę baz danych i ich właściwości
  • sys.master_files – widok systemowy zawierający listę plików wszystkich baz danych
  • sys.database_recovery_status – widok systemowy zawierający informacje na temat procesów recovery baz danych
  • DBCC LOGINFO – nieudokumentowane polecenie DBCC pokazujące układ wirtualnych plików dziennika transakcji dla bieżącej bazy danych (tu użyte do zliczenia wirtualnych plików)
  • DBCC SQLPERF(LOGSPACE) – polecenie DBCC zwracające listę baz danych wraz z wielkością dzienników transakcji oraz stopnia wypełnienia tych dzienników

Mam nadzieję, że powyższy kod się przyda. Działa na SQL Server 2005 i nowszych. Przy odrobinie wysiłku można kod przerobić, by działał także na wersji 2000.

I na deser kilka odnośników do postów na blogach, które przeczytałem przed przystąpieniem do pisania kodu:

Szczególnie ciekawy jest bug wymieniony na koniec ostatniego z wymienionych postów – polecam do poczytania :-)

[PL] SQL Server 2008 – kopia zapasowa zadań

VN:F [1.7.9_1023]
Rating: 0.0/5 (0 votes cast)

Czy kiedykolwiek zdarzyło Ci się, że trzeba było przenosić zadania (ang. jobs) usługi SQL Server Agent? Dość oczywistym sposobem jest wykorzystanie mechanizmu skryptowania dostępnego w SQL Server Management Studio. Po kolei:

  1. Łączymy się z instancją SQL Server 2008 za pomocą okna Object Explorer (wciśnij F8, by to okno zobaczyć).
  2. Przechodzimy w drzewie do węzła SQL Server Agent – Jobs.
  3. Otwieramy okno Object Explorer Details (wciśnij F7, by to okno zobaczyć).
  4. Zaznaczamy wszystkie zadania w oknie Object Explorer Details, klikamy prawym przyciskiem myszy i wybieramy Script Job as – CREATE To i wybieramy odpowiednią dla nas opcję (do pliku, do nowego okna skryptu, do schowka).

image

Wszystko dobrze, ale co, gdy taką kopię zapasową zadań musimy wykonywać cyklicznie? Chcemy mieć do tego zadania automat. Pierwsza myśl – użyć T-SQL. Trzeba się jednak nieźle naklepać kodu, by napisać takie narzędzie. Druga myśl – użyć tego, czego używa Management Studio – .NET i bibliotek SMO. Dobry pomysł, ale nadal sporo kodu do napisania. Czy na pewno? Otóż nie, bo z odsieczą przychodzi nam… PowerShell!

A zatem – otwieramy na początek konsolę sqlps.exe (klikamy prawym przyciskiem myszy na nazwie instancji w oknie Object Explorer i wybieramy Start PowerShell).

image 

Na powyższym zrzucie 0809-001 to nazwa mojej maszyny, a DEFAULT oznacza oczywiście instancję domyślną. Gdy nie wiemy, dokąd dalej nawigować w oknie konsoli PowerShell, wystarczy uruchomić komendę dir (jest to tutaj alias na odpowiedni commandlet). Teraz nawigujemy (cd jest kolejnym przydatnym aliasem – działa zupełnie jak w konsolach DOS i umożliwia nawigację po “folderach” SQL Servera) do zbioru zadań usługi SQL Server Agent. U mnie to wyglądało tak:

  • cd JobServer
  • cd Jobs
  • dir

I mamy listę zadań. W konsoli PowerShell wiele klasy SMO są dostępne bez pokrętnego dodawania bibliotek. Praktycznie każdy obiekt dysponuje metodą Script() i ten fakt właśnie można wykorzystać. Gdy w konsoli PowerShell pojawił się prompt:

PS SQLSERVER:\SQL\0809-001\DEFAULT\JobServer\Jobs>

wpisałem coś takiego:

dir | %{$_.Script()}

I dostałem kod T-SQL zawierający definicje zadań. Kod niestety niedoskonały, ale w zasadzie brakowało w nim jedynie odseparowania każdej definicji końcem wsadu (słowem GO).

Czyli skryptować zadania w PowerShell już umiem. Teraz zapis do pliku. Nic prostszego. Lekka modyfikacja dopiero co uruchomionego skryptu:

dir | %{$_.Script()} >> C:\jobs.sql

I mam zapisane definicje zadań w pliku C:\jobs.sql. Oczywiście – warunkiem jest to, by wskazać folder, do którego SQL Server (a w zasadzie konto Windows przydzielone do tej usługi) ma prawo zapisu.

I teraz ostateczny szlif. Jak spowodować, żeby – po pierwsze – taki skrypt PowerShell był uruchamiany cyklicznie i – po drugie – by za każdym razem wynikowy kod T-SQL lądował w nowym pliku (np. oznaczonym datą i godziną)?

Skoro cyklicznie, to pewnie wypada użyć zadania usługi SQL Server Agent. Tworzymy więc nowe zadanie:

  1. W oknie Object Explorer przechodzimy do węzła SQL Server Agent – Jobs.
  2. Klikamy prawym przyciskiem myszy na węźle Jobs i wybieramy New Job….
  3. Wpisujemy nazwę zadania (pole Name) i po lewej stronie okna New Job wybieramy opcję Steps, a następnie klikamy na przycisku New…
  4. Wpisujemy nazwę nowego kroku zadania (pole Name), a następnie z rozwijanej listy Type wybieramy PowerShell.
  5. W polu Command wpisujemy skrypt PowerShell. W moim przypadku wyglądał on tak:

$data = Get-Date –Format yyyyMMddHHmm

dir SQLSERVER:\SQL\0809-001\DEFAULT\JobServer\Jobs | %{$_.Script() + “GO”} >> C:\backup_jobs_$data.sql

Pierwsza linijka skryptu ustawia jako obiekt $data bieżącą datę i czas zapisaną w odpowiednim formacie. Druga linijka dla każdego zadania uruchamia metodę Script(), dokleja do treści zadania zamknięcie wsadu (GO), a całość zeskryptowanego kodu T-SQL zapisuje do pliku o zmiennej nazwie, np. C:\backup_jobs_201004282220.sql.

image Teraz pozostaje już tylko wybrać harmonogram dla zadania i przetestować (oczywiście, usługa SQL Server Agent musi być uruchomiona). U mnie działa. Wykorzystałem ten mechanizm także u klienta, który miał potrzebę przenoszenia zadań między serwerami zawierającymi bazy sprzężone mechanizmem log shippingu. PowerShell zaoszczędził mi w tym przypadku wiele pracy.

Przy okazji – polecam sesje Łukasza Grali poświęcone wykorzystaniu PowerShell do administracji systemem SQL Server 2008. Sesje te są organizowane przez portal VirtualStudy.pl (gratulacje dla prowadzących portal z okazji rocznicy istnienia portalu – tak trzymać!).