В света на SQL Server има два типа хора:тези, които обичат всичките им обекти да са с префикс, и тези, които не го правят. Първата група е допълнително разделена на две категории:тези, които поставят префикс на съхранените процедури с sp_
и тези, които избират други префикси (като usp_
или proc_
). Дългогодишната препоръка е да се избягва sp_
префикс, както от съображения за производителност, така и за избягване на неясноти или сблъсъци, ако случайно изберете име, което вече съществува в системния каталог. Сблъсъците със сигурност все още са проблем, но ако приемем, че сте проверили името на обекта си, все още ли е проблем с производителността?
TL;DR версия:ДА.
Префиксът sp_ все още е не-не. Но в тази публикация ще обясня защо, как SQL Server 2012 може да ви накара да повярвате, че този предупредителен съвет вече не важи, както и някои други потенциални странични ефекти от избора на тази конвенция за именуване.
Какъв е проблемът със sp_?
sp_
префиксът не означава това, което мислите, че прави:повечето хора мислят, че sp
означава "съхранена процедура", когато всъщност означава "специална". Съхранените процедури (както и таблици и изгледи), съхранявани в master с sp_
префикса са достъпни от всяка база данни без подходяща препратка (ако приемем, че локална версия не съществува). Ако процедурата е маркирана като системен обект (с помощта на sp_MS_marksystemobject
(недокументирана и неподдържана системна процедура, която задава is_ms_shipped
до 1), тогава процедурата в master ще се изпълни в контекста на извикващата база данни. Нека разгледаме прост пример:
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
Резултати:
(0 row(s) affected) sp_test foo (1 row(s) affected)
Проблемът с производителността идва от факта, че master може да бъде проверен за еквивалентна съхранена процедура, в зависимост от това дали има локална версия на процедурата и дали всъщност има еквивалентен обект в master. Това може да доведе до допълнителни разходи за метаданни, както и до допълнителен SP:CacheMiss
събитие. Въпросът е дали тези режийни разходи са осезаеми.
Така че нека разгледаме една много проста процедура в тестова база данни:
CREATE DATABASE sp_prefix; GO USE sp_prefix; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
И еквивалентни процедури в master:
USE master; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'master', DB_NAME(); END GO EXEC sp_MS_marksystemobject N'sp_something';
CacheMiss:Факт или измислица?
Ако изпълним бърз тест от нашата тестова база данни, виждаме, че изпълнението на тези съхранени процедури никога няма да извика версиите от master, независимо дали правилно квалифицираме процедурата за база данни или схема (често срещано погрешно схващане) или ако маркираме главната версия като системен обект:
USE sp_prefix; GO EXEC sp_prefix.dbo.sp_something; GO EXEC dbo.sp_something; GO EXEC sp_something;
Резултати:
sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix
Нека също стартираме Quick TraceSP:CacheMiss
събития:
Виждаме CacheMiss
събития за ad hoc пакет, който извиква съхранената процедура (тъй като SQL Server обикновено няма да си прави труда да кешира пакет, който се състои предимно от извиквания на процедури), но не и за самата съхранена процедура. Както със, така и без sp_something
процедура, съществуваща в master (и когато съществува, както със, така и без да е маркиран като системен обект), извикванията към sp_something
в потребителската база данни никога не извиквайте "случайно" процедурата в master и никога не генерирайте CacheMiss
събития за процедурата.
Това беше на SQL Server 2012. Повторих същите тестове по-горе на SQL Server 2008 R2 и открих малко по-различни резултати:
Така че на SQL Server 2008 R2 виждаме допълнителен CacheMiss
събитие, което не се случва в SQL Server 2012. Това се случва във всички сценарии (няма еквивалентен главен обект, обект в главен обект, маркиран като системен обект, и обект в главен, който не е маркиран като системен обект). Веднага ми стана любопитно дали това допълнително събитие ще има забележимо въздействие върху представянето.
Проблем с производителността:факт или измислица?
Направих допълнителна процедура без sp_
префикс за сравнение на необработената производителност, CacheMiss
настрана:
USE sp_prefix; GO CREATE PROCEDURE dbo.proc_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Така че единствената разлика между sp_something
и proc_something
. След това създадох обвиващи процедури, за да ги изпълня 1000 пъти всяка, използвайки EXEC sp_prefix.dbo.<procname>
, EXEC dbo.<procname>
и EXEC <procname>
синтаксис, с еквивалентни съхранени процедури, живеещи в master и маркирани като системен обект, живеещи в master, но не маркирани като системен обект и изобщо не живеещи в master.
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
Измервайки продължителността на изпълнение на всяка процедура за обвиване с SQL Sentry Plan Explorer, резултатите показват, че с помощта на sp_
префиксът има значително влияние върху средната продължителност в почти всички случаи (и със сигурност средно):