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 TraceSP: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):