Posts tagged SQL Server
[PL] Denali CTP3 – Nowe lepsze DBCC IND?
Jul 26th
Niedawno ukazała się nowa publiczna wersja Community Preview 3 (CTP3) systemu SQL Server vNext (codename Denali). Okoliczności ukazania się tej wersji były dość ciekawe, ponieważ wersja ukazała się dość niespodziewanie szybciej, niż ktokolwiek mógł podejrzewać. Jednocześnie firma Microsoft zarzuciła użytkowników bogactwem nowych wersji SQL Server 2008 R2 (Service Pack1) i 2008 (kilka Cumulative Update’ów). Jest więc co eksplorować, choć zalecam ostrożność, ponieważ z zaufanych źródeł wiem, że zwłaszcza Service Pack 1 do R2 jest źródłem licznych problemów (włącznie z tym, że dość trudno go poprawnie zainstalować).
Dopiero kilka dni temu znalazłem czas, by sprokurować sobie maszynę wirtualną do Denali CTP3. Maszyna działa pod kontrolą Windows Server 2008 SP1. Do tego trzeba było doinstalować parę rzeczy, ale na szczęście instalator wszystko dość jasno opisuje i nie ma problemów ze znalezieniem w sieci brakujących dodatków do systemu operacyjnego.
Jedną z pierwszych czynności po zainstalowaniu każdej nowej wersji SQL Servera jest w moim przypadku sprawdzenie, czy nie pojawiły się nowe widoki i funkcje dynamiczne. Uruchamiam zatem zapytanie:
SELECT 'sys.' + name FROM sys.system_objects WHERE [schema_id] = 4 AND name LIKE 'dm[_]%';
Przejrzenie listy w przypadku Denali CTP3 zaowocowało znalezieniem kilku ciekawych obiektów, ale najciekawszym z mojego punktu widzenia wydaje się być sys.dm_db_database_page_allocations. Jest to funkcja dynamiczna, która przyjmuje sporo parametrów: identyfikator bazy, identyfikator tabeli, identyfikator indeksu, numer partycji oraz rodzaj widoku. Lista parametrów przypomina widok sys.dm_db_index_physical_stats. Jednak nowa funkcja zwraca informacje prawie takie same, jakie dotąd można było uzyskać za pomocą polecenia DBCC IND. Możemy zatem obejrzeć, które strony wchodzą w skład konkretnego indeksu czy sprawdzić, z ilu poziomów stron dany indeks jest złożony. Przykładowe odwołanie do tej funkcji:
SELECT * FROM sys.dm_db_database_page_allocations( DB_ID(), NULL, NULL, NULL, 'DETAILED' );
Przykładowy wynik (lista stron zaalokowanych dla całej bazy, widok o największej liczbie detali):
Nie muszę tłumaczyć, o ile lepiej byłoby mieć w systemie widok dynamiczny, na dodatek w pełni wspierany i udokumentowany. Do szczęścia jeszcze poproszę jakiś oficjalny zamiennik dla DBCC PAGE :-)
[PL] Zmiany w PLSSUG
Jun 20th
W ostatnią sobotę, 18 czerwca, odbyła się we Wrocławiu trzecia już w historii konferencja SQLDay. Organizatorem, jak zwykle, była Polska Grupa Użytkowników SQL Server (PLSSUG). Niestety, sprawy zawodowe tym razem nie pozwoliły mi wziąć udziału w konferencji, nad czym boleję. Tak czy owak, tradycji musiało stać się za dość i w trakcie konferencji został ogłoszony wybór nowego prezesa PLSSUG :-)
Nowym prezesem został… (TADA!) Tobiasz Koprowski! Tobiasz jest znany w środowisku jako tytan pracy. To na jego barkach co roku spoczywała organizacja SQLDay. To on jest najbardziej aktywnym członkiem grupy w szeregach międzynarodowych organizacji wspierających grupy offline (tu mowa o GITCA). Generalnie, człowiek orkiestra. Stąd, pierwsza myśl, jaka przyszła mi do głowy, gdy nadeszła pora wytypowania mojego “następcy”, brzmiała, że to właśnie jemu należałoby powierzyć następną kadencję na najwyższym “stołku” w PLSSUG. Tobiaszu, jestem pewien, że dasz sobie radę i będziesz doskonałym “wodzem” grupy.
Co udało się zrobić grupie przez ostatnie kilka miesięcy (od grudniowej konferencji SQLDay 2010)? Przede wszystkim przeszliśmy na nową witrynę. Może nie jest jeszcze ona doskonała, ale jesteśmy na dobrej drodze, a przynajmniej samodzielnie ją utrzymujemy i informacje na niej są w miarę aktualne. Po drugie, oczywiście, udało się zrealizować projekt konferencji latem, a nie, jak to było dotychczas, porą jesienną lub zimową. Przynajmniej dojechać było łatwiej ;-) Poza tym, co najważniejsze, grupa cały czas żyje. Spotykamy się w kilku miastach w Polsce i nie zapowiada się, byśmy mieli przestać dyskutować o SQL Serverze.
W tym miejscu zwracam się do tych czytelników mojego bloga, którzy są członkami PLSSUG. Jeśli macie możliwość udzielenia jakiegokolwiek wsparcia Tobiaszowi i liderom grupy, nie wahajcie się i piszcie maila na adres leaders@plssug.org.pl. Żaden nadesłany mail nie zostanie bez odpowiedzi. A że my wszyscy jesteśmy zapracowani i doba zawsze ma za mało godzin, każda pomocna dłoń jest na wagę złota. Zachęcam do czynnego udziału w życiu grupy. Przynosi to często wymierne korzyści nie tylko samym aktywistom, ale i szerokiej rzeszy członków grupy i uczestników naszych spotkań i konferencji.
Kończąc ten wpis wyrażam nadzieję, że na następną edycję SQLDay uda mi się dotrzeć i – być może – przedstawić jakąś ciekawą prezentację. Do zobaczenia!
[PL] SQL Server – Przerwany łańcuch backupów różnicowych
Jun 3rd
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 – Jak optymalizator robi ze mnie głupca
May 31st
Dawno, dawno temu dowiedziałem się z bloga Tibora Karasziego (SQL Server MVP), że optymalizator może wykorzystać obiekty constraint, takie jak CHECK czy FOREIGN KEY (klucz obcy) do optymalizacji określonych klas zapytań.
Jakiś czas temu próbowałem pokazać koledze z firmy, jak to działa i przy okazji dowiedziałem się ciekawej rzeczy, na którą dotąd nie natrafiłem. A w zasadzie, można powiedzieć, normalnie aż zgłupiałem :-)
Na początek weźmy tabelę:
USE tempdb; GO IF OBJECT_ID('dbo.Employee') IS NOT NULL DROP TABLE dbo.Employee; CREATE TABLE dbo.Employee ( EmployeeId int IDENTITY(1,1) NOT NULL PRIMARY KEY, FirstName varchar(50) NOT NULL, LastName varchar(50) NOT NULL, Income money NOT NULL DEFAULT (0) ); GO ALTER TABLE dbo.Employee ADD CONSTRAINT CK_Employee_Income CHECK (Income >= 0); GO INSERT INTO dbo.Employee (FirstName, LastName, Income) SELECT NEWID(), NEWID(), RAND(CHECKSUM(NEWID())) * v1.number + v2.number FROM master.dbo.spt_values AS v1, master.dbo.spt_values AS v2 WHERE v1.type = 'P' AND v2.type = 'P' AND v1.number BETWEEN 1 AND 1000 AND v2.number BETWEEN 1 AND 1000; GO
Tabela dość typowa. I zawiera milion rekordów. To, co ważne, to fakt, że założyłem w niej warunek ograniczający możliwe wartości kolumny Income (muszą być nieujemne). Świeżo założony w powyższy sposób warunek CHECK jest “trusted” (patrz wpis na blogu Tibora). Weźmy teraz zapytanie:
SELECT * FROM dbo.Employee WHERE Income < 0; GO
Plany wykonania na mojej maszynie i przy konfiguracji serwera z MAXDOP = 0 wygląda u mnie tak:
Hę? Spodziewałem się, że zobaczę operator Constant Scan i… tyle! A tymczasem jakiś skan, z jakimś parallelismem… Horror!
Pobawiłem się trochę tym zapytaniem. Pierwsza myśl – wyeliminować parallelism:
SELECT * FROM dbo.Employee WHERE Income < 0 OPTION (MAXDOP 1); GO
Plan wykonania:
Noooo, to lubimy :-) Żadnego zbędnego zaglądania do tabeli, po prostu od razu na podstawie zaufanego warunku CHECK generowana jest odpowiedź, że nie ma rekordów spełniających warunek z zapytania.
A teraz deser – przyznaję, że sam tego nie wymyśliłem (zbyt fikuśne :-)), choć testowałem różne śmieszne warianty zapytań (m.in. dodawałem grupowania po wszystkich kolumnach tabeli – pomagało i wymuszało Constant Scan). Maciek Pilecki, z którym ten problem przedyskutowałem i który, podobnie jak ja, ma wrażenie, że coś tu nie gra, wypróbował taki kwiatek:
SELECT * FROM dbo.Employee WHERE Income < 0 OPTION (MAXDOP 0); GO
Efekt? Sami sprawdźcie, ale gwarantuję pełne zaskoczenie :-) Oczywiście, Constant Scan! I o so chosi?
Wygląda na to, że optymalizator pozostawiony samemu sobie i stojący przed “trudnym” wyborem (nie wpisując hinta powodujemy, że optymalizator ma do dyspozycji wszystkie możliwe ścieżki optymalizacji zapytania) robi głupotę. A może to nowy bug? Ale plan dla zapytania z MAXDOP = 0 powoduje już moją konsternację. Trzeba będzie z kimś z grupy produktowej o tym porozmawiać, bo bardzo nie lubię tłumaczyć się przed kolegami, którym demonstruję “niby dobrze znane” triki optymalizacyjne, a tymczasem optymalizator krzyżuje moje plany i wychodzę na głupca :-)
Wszelkie przemyślenia własne na temat powyższego mile widziane. Moje przemyślenie – nigdy nie bądź niczego pewnym na 100% bez sprawdzenia organoleptycznie :-)
Dodam, że przełączenie MADXOP = 1 na poziomie serwera nie zmienia sytuacji – dalej mamy skan całej tabeli, jeśli nie dodamy hinta lub nie skomplikujemy zapytania! O co tutaj chodzi??? :-)
I na sam koniec – kupa śmiechu. Jaki jest bodaj najprostszy możliwy hint wymuszający Constant Scan w omawianym przypadku? Opadają mi ręce, gdy pomyślę o mojej SQL-owej niewiedzy…
SELECT * FROM dbo.Employee WHERE Income < 0 AND 1=1; GO
[EDIT] Jak słusznie zauważył Patrik Třeštik komentując ten wpis, problem jest najpewniej spowodowany przez parametryzację zapytania (i zarazem zjawisko parameter sniffing). Jakimś cudem zero w warunku WHERE przestaje być stałą i optymalizator zaczyna traktować je jak wartość nieznaną, a więc jednocześnie nie może porównać jej do wartości zaszytej w ograniczeniu CHECK. Tylko powstaje pytanie – po co ta parametryzacja? :-) [/EDIT]
[PL] SQL Server – {Workbench} Funkcje i ich “gotchas”
May 4th
Od jakiegoś czasu chodził za mną pomysł, żeby publikować na blogu obszerne fragmenty kodu, które wraz z komentarzami w nich zawartymi opisywałyby się same. Pomysł zaczerpnąłem z serii, którą dawno temu śledziłem na portalu Simple Talk (przykład). Dzisiaj pierwszy workbench na moim blogu – poświęcony pułapkom w systemowych funkcjach skalarnych (kod trzymam od dawna, więc pomyślałem, że czas go opublikować). Prawie na pewno nie będzie to ostatni workbench, jaki opublikuję. A gdyby komuś przyszło do głowy, co by można było dopisać do listy poniższych “gotchas”, śmiało dodawajcie w komentarzach albo ślijcie na maila (najlepiej od razu w takiej postaci, jak poniżej). Creditsy gwarantowane :-)
/* Workbench: Funkcje i ich "gotchas" Cel: Pokazanie pułapek ukrytych w systemowych funkcjach skalarnych w SQL Server. Założenie: Znajomość przedstawionych funkcji i umiejętność znalezienia pomocy do nich w BOL Uwagi: kod zawiera tylko "gotchas", nie podaję gotowych obejść problemów */ ------------------------------------------------------------ -- Funkcja : DATEDIFF -- Gotcha : Niedziela zawsze pierwszym dniem tygodnia -- Rozwiązanie : Własna funkcja ------------------------------------------------------------ -- Niedziela pierwszym dniem tygodnia SET DATEFIRST 7 -- Wynik: oczekiwane 0 SELECT DATEDIFF(week, '20110501', '20110502') -- Poniedziałek pierwszym dniem tygodnia SET DATEFIRST 1 -- Wynik: nieoczekiwane 0 SELECT DATEDIFF(week, '20110501', '20110502') ------------------------------------------------------------ -- Funkcja : ISNULL -- Gotcha : Typ danych narzucany przez pierwszy parametr -- Rozwiązanie : Jawna konwersja lub użycie COALESCE zamiast ISNULL ------------------------------------------------------------ -- Wynik: Ala m - bo typ wynikowy to char(5) SELECT ISNULL(CONVERT(char(5), NULL), 'Ala ma kota') ------------------------------------------------------------ -- Funkcja : ISNUMERIC -- Gotcha : Nie zawsze mówi prawdę -- Rozwiązanie : Własna funkcja (CLR?) ------------------------------------------------------------ -- Wynik: 0 i 0, czyli '' nie jest liczbą, -- ale daje się rzutować na typ liczbowy SELECT ISNUMERIC(''), CONVERT(int, '') ------------------------------------------------------------ -- Funkcja : LEN -- Gotcha : Przed zliczaniem znaków ucina końcowe spacje -- Rozwiązanie : Własna funkcja ------------------------------------------------------------ -- Wynik: 3 (zamiast 5) SELECT LEN('abc ') ------------------------------------------------------------ -- Funkcja : QUOTENAME -- Gotcha : Zwraca NULL dla parametrów dłuższych niż 128 znaków -- Rozwiązanie : Własna funkcja ------------------------------------------------------------ -- Wynik: NULL SELECT QUOTENAME(REPLICATE('a', 129)) ------------------------------------------------------------ -- Funkcja : REPLICATE -- Gotcha : Typ danych prawie narzucany przez pierwszy parametr -- Rozwiązanie : Jawna konwersja pierwszego parametru ------------------------------------------------------------ -- Wynik: 7998, bo kolejna trójka nie mieści się w 8000, -- a typ varchar(max)/nvarchar(max) musi być wskazany jawnie SELECT LEN(REPLICATE(CONVERT(char(3), 'abc'), 3000)) ------------------------------------------------------------ -- Funkcja : SERVERPROPERTY -- Gotcha1 : Dla "nieznanej" wartości parametru zwraca NULL -- Gotcha2 : Zwraca wartość typu sql_variant (jak inne funkcje *PROPERTY) -- Rozwiązania : Uważać na parametr + jawna konwersja wyniku ------------------------------------------------------------ -- Wynik: NULL i nazwa_maszyny SELECT SERVERPROPERTY('MachinName'), SERVERPROPERTY('MachineName') -- Wynik: Błąd -- Msg 402, Level 16, State 1, Line 1 -- The data types varchar and sql_variant are incompatible in the add operator. SELECT 'Nazwa maszyny: ' + SERVERPROPERTY('MachineName') ------------------------------------------------------------ -- Funkcja : STR -- Gotcha : Czasem zwraca "gwiazdki" :-) -- Rozwiązania : Podawać dwa parametry lub nie używać STR ------------------------------------------------------------ -- Wynik: ********** i 12345678901 -- Pierwsze wyrażenie zwraca wartość typu char(10), -- a gwiazdki to wynik zwracany przez STR przy przekroczeniu -- długości określonej przez drugi opcjonalny parametr (domyślnie 10) SELECT STR(12345678901), STR(12345678901, 11)
[PL] SQL Server – Czy potrzebujemy master.dbo.sysprocesses?
Apr 29th
Na portalu connect,microsoft,com jednym ze zgłoszonych błędów jest ten zgłoszony przez Tony’ego Rogersona pt. Deprecation of sysprocesses – DMV’s doesn’t fully replace all columns. Od momentu, gdy w dokumentacji SQL Servera w opisie widoku sysprocesses (tak, to jest widok, ale piszą o nim, jakby to była tabela, jak za czasów SQL Servera 2000) pojawił się zapisek:
This feature will be removed in a future version of Microsoft SQL Server. Avoid using this feature in new development work, and plan to modify applications that currently use this feature.
rozgorzały dyskusje na temat słuszności posunięcia Microsoftu zmierzającego w kierunku wycięcia sysprocesses z przyszłych wersji SQL Servera. Głównym zarzutem zawsze było to, że widoki DMV, które Microsoft rekomenduje zamiast sysprocesses (konkretnie są to: sys.dm_exec_connections, sys.dm_exec_sessions, sys.dm_exec_requests) nie umożliwiają zwrócenia tych samych informacji, które można uzyskać odpytując sysprocesses. W szczególności nie można z ich pomocą dowiedzieć się, jakie sesje są podłączone do konkretnej bazy danych (np. w celu zamknięcia określonych połączeń poleceniem KILL), ponieważ nawet, jeśli w którymś z nich (sys.dm_exec_requests) pojawia się kolumna zawierająca identyfikator bazy danych, to niestety sensowne identyfikatory pojawiają się w niej tylko, gdy użytkownik używa obiektu proceduralnego, a nie pojawiają się w przypadku używania zapytań ad-hoc. Smaczku sprawie dodaje fakt, że Microsoft sam nadal wykorzystuje widok sysprocesses w zapytaniach zadawanych przez narzędzia najnowszych nawet wersji SQL Servera (np. Activity Monitor w SSMS 2008 R2 używa tego widoku do uzyskiwania listy sesji).
Paul White (aka SQLKiwi), świeżo upieczony SQL Server MVP i posiadacz arcyciekawego bloga (polecam!), podał obejście problemu z uzyskaniem przy użyciu DMV listy sesji podłączonych do konkretnej bazy danych:
SELECT request_session_id FROM sys.dm_tran_locks WHERE resource_type = N'DATABASE' AND request_mode = N'S' AND request_status = N'GRANT' AND request_owner_type = N'SHARED_TRANSACTION_WORKSPACE' AND resource_database_id = DB_ID(N'AdventureWorks');
Czy powyższe zapytanie zawsze zadziała poprawnie? Dla baz użytkownika, wg wszelkich przesłanek, tak. Testowałem różne warianty, poziomy izolacji i zapytanie zaproponowane przez Paula daje dobry wynik. Oczywiście, ciśnie się na usta pytanie, czemu nie można tego wyłuskać za pomocą trzech wspomnianych DMVs z rodziny sys.dm_exec_*. To pytanie do Microsoftu, na które chyba nikt w Redmond nie potrafi sensownie odpowiedzieć :-)
Analogiczne zapytanie z użyciem sysprocesses zwracające listę sesji podłączonych do bazy danych:
SELECT spid FROM master.dbo.sysprocesses WITH (NOLOCK) -- asekuracyjny NOLOCK WHERE spid > 50 -- choć ponoć spid > 50 nie zawsze oznacza sesję użytkownika AND dbid = DB_ID(N'AdventureWorks');
A teraz zagadka na długi weekend. Dla jakich baz zapytanie Paula nie powie prawdy (nie pokaże pełnej listy podłączonych sesji, bo nie wykryje blokad typu SHARED_TRANSACTION_WORKSPACE)? Odpowiedzi możecie wpisywać w komentarzach do tego wpisu. Podpowiem, że akurat w przypadku tych baz raczej nikomu nie przyjdzie do głowy wycinać podłączonych sesji.
Miłego długiego majowego weekendu życzę :-)
[PL] SQL Server – Moja nierówna walka z Resource Governorem
Apr 17th
W zeszłym tygodniu podjąłem próbę zaprzęgnięcia Resource Governora (dalej zwanego RG) w służbie moim potrzebom administracyjnym. Konkretnie, chodziło o to, by dla dobrze określonego zbioru kroków z zadań (jobów) SQL Server Agenta narzucać odpowiedni MAXDOP.
Zacząłem od zbadania, czy w ogóle jest o co walczyć – to znaczy, czy jestem w stanie namierzyć właściwy krok zadania Agenta po wartości zwracanej przez funkcję APP_NAME (nazwa aplikacji). Okazało się, że tak, choć droga jest ciut straszna. W momencie, gdy wykonywany jest job, każdy krok to nawiązanie od nowa połączenia z SQL Serverem (logout + login). Za każdym krokiem zmienia się nazwa aplikacji, zawsze na coś podobnego do:
SQLAgent – TSQL JobStep (Job 0×4935DE6F94B48045822A472E80BAF8F0 : Step 1)
Z powyższego dało się wycągnąć nazwę joba (wyboldowane binaria to zrzutowany na varbinary identyfikator joba) oraz nazwę kroku (gdy znamy identyfikator joba wyciągamy z tabeli msdb.dbo.sysjobsteps krok o odpowiednim numerze wziętym z nazwy aplikacji – wyboldowany numer na końcu nazwy aplikacji).
Napisałem na szybko funkcję klasyfikującą (kopiując ją z jakiegoś artykułu i zmieniając tylko warunek – na stałe wpisałem zwracaną nazwę workload grupy, do której moja testowa instancja miała przypisywać wszystkie połączenia), skonfigurowałem pulę i workload grupę (MAXDOP=4) i przetestowałem całe rozwiązanie. Działało pięknie i rozkoszowałem się planami wykonania z poziomem zrównoleglenia dokładnie równemu 4 :-)
Teraz pozostało już tylko dopisać w funkcji klasyfikującej logikę – odwołania do msdb w celu wydobycia odpowiednich danych, które pozwoliłyby stwierdzić, czy połączenie jest związane z właściwym krokiem joba, czy też nie.
I tu RG mnie powalił… Zupełnie nie skojarzyłem, że każda funkcja klasyfikująca RG musi mieć w definicji opcję WITH SCHEMABINDING… A co za tym idzie? Nie można odwołać się do żadnego obiektu używając większej ilości członów jego nazwy niż dwa! Nie da się odpytać tabeli z innej bazy danych niż master! Zonk… Próbowałem na różne sposoby, ale nie dało się oszukać SCHEMABINDINGu (widok w bazie master?- źle, bo musi być z opcją SCHEMABINDING, więc też nie odwołam się do msdb; OPENROWSET? – źle, bo nie można użyć w funkcji…). Co mi zostało? Drut. Drut w postaci cyklicznego kopiowania do bazy master danych z tabeli msdb.dbo.sysjobsteps. To rozwiązanie nie przypadło mi jednak do gustu :-)
Wszystko rozumiem. Rozumiem, że RG nie pozwala ograniczać bufora danych, że operuje per połączenie, że niektóre limity są honorowane wtedy, gdy inna grupa potrzebuje zasobów. Świetnie. Ale po co u licha w funkcji klasyfikującej ten nieszczęsny SCHEMABINDING, który z całkiem obiecująco wyglądającego mechanizmu zrobił coś, co w przypadku, gdy chcemy stworzyć coś ciut bardziej wyrafinowanego niż badanie, jaki login jest zalogowany na połączeniu, wymaga od użytkownika przysłowiowego "łapania się lewą ręką za prawe ucho"? Wie ktoś? :-)
I już zupełnie na marginesie – także w zeszłym tygodniu próbowałem zaprzęgać Policy-Based Management (PBM) do wykrywania jednego z typowych błędów deweloperskich w procedurach. I co? I guzik. Okazało się, że w PBM wzorzec procedury składowanej nie ma takiej właściwości, która pozwala zbadać zawartość definicji procedury… (i jakoś nie bardzo mnie pociesza, że w wyrażeniach w PBM mogę użyć dostępnej funkcji ExecuteSql i wydłubać zapytaniem z odpowiedniego widoku definicję obiektu…) Opadło mi wszystko, włącznie z chęcią używania PBM. To już sobie napiszę rękoma DDL trigger i przynajmniej będę dokładnie wiedział, co w kodzie piszczy.
Suma sumarum, za takie "ficzery" w SQL Serverze ja dziękuję. Nijak mi nie pomagają, a jedynie zjadają czas spędzony na bezowocnym R&D. Czas, który mógłbym spożytkować na pisanie swojego kodu, który robiłby to samo, tylko pewnie lepiej ;-)
[PL] SQL Server dla DBA – Zdań kilka o SQL Trace
Apr 12th
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:
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:
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:
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:
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] Materiały z sesji o XML z 53. spotkania PLSSUG Warszawa
Apr 11th
W czwartek, 7 kwietnia, Paweł Skolimowski poprowadził prezentację pt. “Niechaj Cię nie onieśmiela gąszcz misterny XML-a” w ramach 53. spotkania Polskiej Grupy Użytkowników SQL Server (PLSSUG).
Prezentacja wypadła, moim zdaniem, znakomicie. Paweł przygotował profesjonalne slajdy (zmieniające się jak w kalejdoskopie kolorowanie składni w PPT – szacunek!) oraz obrazowe przykłady kodu T-SQL. Jak na pierwszą prezentację przed, co by nie mówić, sporym gronem publiczności (na sali było około 50-60 osób), dla mnie bomba.
A ponieważ na nowej witrynie PLSSUG, póki co, nie ma modułu do przechowywania i udostępniania materiałów z prezentacji (pracujemy nad tym!), uzgodniłem z Pawłem, że materiały z jego sesji zamieszczę w ramach mojego bloga, by zainteresowane osoby mogły pobrać kod i slajdy.
Krótki komentarz Pawła do materiałów:
Prezentacja jest tak skonstruowana, że w okolicach lewego dolnego rogu slajdu pojawia się nazwa skryptu, który jest przykładem omawianego na obecnym slajdzie (oraz kilku wcześniejszych) materiału. W większości skryptów używane są pliki XML, dlatego wymagana jest zmiana używanej ścieżki (f:\#Transfer\…) na inną. Musi ona prowadzić do folderu, do którego ma dostęp serwer MSSQL.
Polecam!
Pobierz materiały z prezentacji “Niechaj Cię nie onieśmiela gąszcz misterny XML-a” (ZIP, 1 MB)
[PL] SQL Server dla DBA – sys.dm_os_performance_counters
Apr 4th
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)
- 537003264 – PERF_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:
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%';
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):
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:
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:
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:
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).



Nazywam się Paweł Potasiński i pracuję w polskim oddziale Microsoft w dziale Small and Midmarket Solutions & Partners (SMS&P) jako Partner Technology Advisor.




