sql >> Databasteknik >  >> RDS >> Database

Är sp_-prefixet fortfarande ett nej-nej?

I SQL Server-världen finns det två typer av människor:de som gillar att alla deras objekt har prefix och de som inte gör det. Den förstnämnda gruppen är vidare uppdelad i två kategorier:de som prefixar lagrade procedurer med sp_ , och de som väljer andra prefix (som usp_ eller proc_ ). En långvarig rekommendation har varit att undvika sp_ prefix, både av prestandaskäl och för att undvika oklarheter eller kollisioner om du råkar välja ett namn som redan finns i systemkatalogen. Kollisioner är förvisso fortfarande ett problem, men förutsatt att du har kontrollerat ditt objektnamn, är det fortfarande ett prestandaproblem?

TL;DR-version:JA.

Prefixet sp_ är fortfarande ett nej-nej. Men i det här inlägget kommer jag att förklara varför, hur SQL Server 2012 kan få dig att tro att detta varningsråd inte längre gäller, och några andra potentiella bieffekter av att välja denna namnkonvention.

Vad är problemet med sp_?

sp_ prefix betyder inte vad du tror att det gör:de flesta tror sp står för "lagrad procedur" när det i själva verket betyder "speciell". Lagrade procedurer (liksom tabeller och vyer) lagrade i master med en sp_ prefix är tillgängliga från vilken databas som helst utan en korrekt referens (förutsatt att en lokal version inte existerar). Om proceduren är markerad som ett systemobjekt (med sp_MS_marksystemobject (en odokumenterad och ostödd systemprocedur som ställer in is_ms_shipped till 1), så kommer proceduren i master att köras i sammanhanget med den anropande databasen. Låt oss titta på ett enkelt exempel:

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

Resultat:

(0 row(s) affected)

sp_test    foo

(1 row(s) affected)

Prestandaproblemet kommer från det faktum att master kan kontrolleras för en likvärdig lagrad procedur, beroende på om det finns en lokal version av proceduren och om det faktiskt finns ett motsvarande objekt i master. Detta kan leda till extra metadataoverhead såväl som ytterligare SP:CacheMiss händelse. Frågan är om denna omkostnad är påtaglig.

Så låt oss överväga en mycket enkel procedur i en testdatabas:

CREATE DATABASE sp_prefix;
GO
USE sp_prefix;
GO
CREATE PROCEDURE dbo.sp_something
AS
BEGIN
  SELECT 'sp_prefix', DB_NAME();
END
GO

Och motsvarande procedurer i 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:Fakta eller fiktion?

Om vi ​​kör ett snabbtest från vår testdatabas ser vi att exekvering av dessa lagrade procedurer aldrig kommer att anropa versionerna från master, oavsett om vi korrekt databas- eller schemakvalificerar proceduren (en vanlig missuppfattning) eller om vi markerar masterversion som ett systemobjekt:

USE sp_prefix;
GO
EXEC sp_prefix.dbo.sp_something;
GO
EXEC dbo.sp_something;
GO
EXEC sp_something;

Resultat:

sp_prefix    sp_prefix
sp_prefix    sp_prefix
sp_prefix    sp_prefix

Låt oss också köra en Quick Trace® använder SQL Sentry för att observera om det finns någon SP:CacheMiss händelser:

Vi ser CacheMiss händelser för ad hoc-batchen som anropar den lagrade proceduren (eftersom SQL Server i allmänhet inte bryr sig om att cachelagra en batch som huvudsakligen består av proceduranrop), men inte för själva den lagrade proceduren. Både med och utan sp_something procedur som finns i master (och när den finns, både med och utan att den är markerad som ett systemobjekt), anropar sp_something i användardatabasen aldrig "av misstag" anropa proceduren i master, och aldrig generera någon CacheMiss händelser för proceduren.

Det här var på SQL Server 2012. Jag upprepade samma tester ovan på SQL Server 2008 R2 och hittade något annorlunda resultat:

Så på SQL Server 2008 R2 ser vi ytterligare en CacheMiss händelse som inte inträffar i SQL Server 2012. Detta inträffar i alla scenarier (ingen motsvarande objektmaster, ett objekt i master markerat som ett systemobjekt och ett objekt i master som inte är markerat som ett systemobjekt). Jag blev genast nyfiken på om denna extra händelse skulle ha någon märkbar inverkan på prestandan.

Prestandaproblem:fakta eller fiktion?

Jag gjorde en ytterligare procedur utan sp_ prefix för att jämföra råprestanda, CacheMiss åt sidan:

USE sp_prefix;
GO
CREATE PROCEDURE dbo.proc_something
AS
BEGIN
  SELECT 'sp_prefix', DB_NAME();
END
GO

Så den enda skillnaden mellan sp_something och proc_something . Jag skapade sedan wrapper-procedurer för att exekvera dem 1000 gånger vardera, med EXEC sp_prefix.dbo.<procname> , EXEC dbo.<procname> och EXEC <procname> syntax, med likvärdiga lagrade procedurer som lever i master och markerade som ett systemobjekt, lever i master men inte markerade som ett systemobjekt, och lever inte alls i 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

När man mäter körtiden för varje omslagsprocedur med SQL Sentry Plan Explorer, visar resultaten att man använder sp_ prefix har en betydande inverkan på den genomsnittliga varaktigheten i nästan alla fall (och säkerligen i genomsnitt):