Database
 sql >> база данни >  >> RDS >> Database

Префиксът sp_ все още ли е не-не?

В света на 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 Trace® използвайки SQL Sentry, за да наблюдавате дали има SP: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_ префиксът има значително влияние върху средната продължителност в почти всички случаи (и със сигурност средно):