W świecie SQL Server istnieją dwa typy ludzi:ci, którzy lubią, gdy wszystkie ich obiekty mają prefiksy, i ci, którzy tego nie lubią. Pierwsza grupa jest dalej podzielona na dwie kategorie:ci, którzy poprzedzają procedury składowane przedrostkiem sp_
i tych, którzy wybierają inne prefiksy (takie jak usp_
lub proc_
). Od dawna zaleca się unikanie sp_
prefiks, zarówno ze względu na wydajność, jak i w celu uniknięcia niejednoznaczności lub kolizji w przypadku wybrania nazwy, która już istnieje w katalogu systemowym. Kolizje z pewnością nadal stanowią problem, ale zakładając, że zweryfikowałeś nazwę swojego obiektu, czy nadal jest to problem z wydajnością?
Wersja TL;DR:TAK.
Prefiks sp_ jest nadal nie-nie. Ale w tym poście wyjaśnię, dlaczego, jak SQL Server 2012 może prowadzić do przekonania, że ta ostrzegawcza rada już nie ma zastosowania, oraz kilka innych potencjalnych skutków ubocznych wyboru tej konwencji nazewnictwa.
Na czym polega problem z sp_?
sp_
prefiks nie oznacza tego, co myślisz, że robi:większość ludzi myśli sp
oznacza „procedura składowana”, podczas gdy w rzeczywistości oznacza „specjalny”. Procedury składowane (a także tabele i widoki) przechowywane we wzorcu z sp_
prefiksy są dostępne z dowolnej bazy danych bez odpowiedniego odniesienia (zakładając, że nie istnieje wersja lokalna). Jeśli procedura jest oznaczona jako obiekt systemowy (za pomocą sp_MS_marksystemobject
(nieudokumentowana i nieobsługiwana procedura systemowa, która ustawia is_ms_shipped
do 1), to procedura w master zostanie wykonana w kontekście wywołującej bazy danych. Spójrzmy na prosty przykład:
CREATE DATABASE sp_test; GO USE sp_test; GO CREATE TABLE dbo.foo(id INT); GO USE master; GO CREATE PROCEDURE dbo.sp_checktable AS SELECT DB_NAME(), name FROM sys.tables WHERE name = N'foo'; GO USE sp_test; GO EXEC dbo.sp_checktable; -- runs but returns 0 results GO EXEC master..sp_MS_marksystemobject N'dbo.sp_checktable'; GO EXEC dbo.sp_checktable; -- runs and returns results GO
Wyniki:
(0 row(s) affected) sp_test foo (1 row(s) affected)
Problem z wydajnością wynika z faktu, że master może być sprawdzany pod kątem równoważnej procedury składowanej, w zależności od tego, czy istnieje lokalna wersja procedury i czy faktycznie istnieje równoważny obiekt w master. Może to prowadzić do dodatkowych kosztów metadanych, a także dodatkowego SP:CacheMiss
wydarzenie. Pytanie brzmi, czy ten koszt jest namacalny.
Rozważmy więc bardzo prostą procedurę w testowej bazie danych:
CREATE DATABASE sp_prefix; GO USE sp_prefix; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
I równoważne procedury w masterze:
USE master; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'master', DB_NAME(); END GO EXEC sp_MS_marksystemobject N'sp_something';
CacheMiss:fakt czy fikcja?
Jeśli uruchomimy szybki test z naszej testowej bazy danych, zobaczymy, że wykonanie tych procedur składowanych nigdy nie wywoła w rzeczywistości wersji z wzorca, niezależnie od tego, czy prawidłowo zakwalifikujemy procedurę w oparciu o bazę danych lub schemat (powszechne błędne przekonanie), czy też zaznaczymy wersja główna jako obiekt systemowy:
USE sp_prefix; GO EXEC sp_prefix.dbo.sp_something; GO EXEC dbo.sp_something; GO EXEC sp_something;
Wyniki:
sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix
Uruchommy także Quick TraceSP:CacheMiss
wydarzenia:
Widzimy CacheMiss
zdarzenia dla partii ad hoc, która wywołuje procedurę składowaną (ponieważ program SQL Server zazwyczaj nie przejmuje się buforowaniem partii, która składa się głównie z wywołań procedur), ale nie dla samej procedury składowanej. Zarówno z, jak i bez sp_something
procedura istniejąca w master (i gdy istnieje, zarówno z zaznaczeniem, jak i bez oznaczenia jako obiekt systemowy), wywołania sp_something
w bazie danych użytkownika nigdy "przypadkowo" nie wywołuj procedury w master i nigdy nie generuj żadnego CacheMiss
wydarzenia dla procedury.
To było na SQL Server 2012. Powtórzyłem te same testy na SQL Server 2008 R2 i znalazłem nieco inne wyniki:
Tak więc w SQL Server 2008 R2 widzimy dodatkowy CacheMiss
zdarzenie, które nie występuje w programie SQL Server 2012. Dzieje się tak we wszystkich scenariuszach (brak równoważnego wzorca obiektu, obiekt w wzorcu oznaczony jako obiekt systemowy i obiekt w wzorcu nieoznaczony jako obiekt systemowy). Od razu byłem ciekaw, czy to dodatkowe wydarzenie będzie miało jakiś zauważalny wpływ na wydajność.
Problem z wydajnością:fakt czy fikcja?
Dokonałem dodatkowej procedury bez sp_
prefiks do porównania surowej wydajności, CacheMiss
na bok:
USE sp_prefix; GO CREATE PROCEDURE dbo.proc_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Więc jedyna różnica między sp_something
i proc_something
. Następnie utworzyłem procedury opakowujące, aby wykonać je 1000 razy każda, używając EXEC sp_prefix.dbo.<procname>
, EXEC dbo.<procname>
i EXEC <procname>
składnia, z równoważnymi procedurami składowanymi znajdującymi się w systemie nadrzędnym i oznaczonymi jako obiekt systemowy, znajdującymi się w systemie nadrzędnym, ale nie oznaczonymi jako obiekt systemowy i w ogóle nie działającymi w systemie nadrzędnym.
USE sp_prefix; GO CREATE PROCEDURE dbo.wrap_sp_3part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC sp_prefix.dbo.sp_something; SET @i += 1; END END GO CREATE PROCEDURE dbo.wrap_sp_2part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC dbo.sp_something; SET @i += 1; END END GO CREATE PROCEDURE dbo.wrap_sp_1part AS BEGIN DECLARE @i INT = 1; WHILE @i <= 1000 BEGIN EXEC sp_something; SET @i += 1; END END GO -- repeat for proc_something
Mierząc czas trwania każdej procedury opakowującej za pomocą SQL Sentry Plan Explorer, wyniki pokazują, że przy użyciu sp_
prefiks ma znaczący wpływ na średni czas trwania w prawie wszystkich przypadkach (i na pewno średnio):