I SQL Server-verdenen er der to typer mennesker:dem, der kan lide, at alle deres objekter får præfiks, og dem, der ikke gør. Den førstnævnte gruppe er yderligere opdelt i to kategorier:dem, der præfikser lagrede procedurer med sp_
, og dem, der vælger andre præfikser (såsom usp_
eller proc_
). En langvarig anbefaling har været at undgå sp_
præfiks, både af ydeevnemæssige årsager og for at undgå tvetydighed eller kollisioner, hvis du tilfældigvis vælger et navn, der allerede findes i systemkataloget. Kollisioner er bestemt stadig et problem, men forudsat at du har undersøgt dit objektnavn, er det så stadig et præstationsproblem?
TL;DR-version:JA.
Sp_-præfikset er stadig et nej-nej. Men i dette indlæg vil jeg forklare hvorfor, hvordan SQL Server 2012 kan få dig til at tro, at dette advarende råd ikke længere gælder, og nogle andre potentielle bivirkninger ved at vælge denne navnekonvention.
Hvad er problemet med sp_?
sp_
præfiks betyder ikke, hvad du tror, det gør:de fleste mennesker tænker sp
står for "lagret procedure", når det faktisk betyder "særlig". Lagrede procedurer (såvel som tabeller og visninger) gemt i master med en sp_
præfiks er tilgængelige fra enhver database uden en ordentlig reference (forudsat at en lokal version ikke eksisterer). Hvis proceduren er markeret som et systemobjekt (ved hjælp af sp_MS_marksystemobject
(en udokumenteret og ikke-understøttet systemprocedure, der indstiller is_ms_shipped
til 1), så vil proceduren i master udføres i sammenhæng med den kaldende database. Lad os se på et simpelt eksempel:
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
Resultater:
(0 row(s) affected) sp_test foo (1 row(s) affected)
Ydeevneproblemet kommer fra det faktum, at master muligvis kontrolleres for en tilsvarende lagret procedure, afhængigt af om der er en lokal version af proceduren, og om der faktisk er et tilsvarende objekt i master. Dette kan føre til ekstra metadata-overhead såvel som en ekstra SP:CacheMiss
begivenhed. Spørgsmålet er, om denne overhead er håndgribelig.
Så lad os overveje en meget simpel procedure i en testdatabase:
CREATE DATABASE sp_prefix; GO USE sp_prefix; GO CREATE PROCEDURE dbo.sp_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Og tilsvarende 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?
Hvis vi kører en hurtig test fra vores testdatabase, ser vi, at udførelse af disse lagrede procedurer aldrig faktisk vil påkalde versionerne fra master, uanset om vi korrekt database- eller skema-kvalificerer proceduren (en almindelig misforståelse), eller hvis vi markerer masterversion som et systemobjekt:
USE sp_prefix; GO EXEC sp_prefix.dbo.sp_something; GO EXEC dbo.sp_something; GO EXEC sp_something;
Resultater:
sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix sp_prefix
Lad os også køre en Quick TraceSP:CacheMiss
begivenheder:
Vi ser CacheMiss
hændelser for den ad hoc-batch, der kalder den lagrede procedure (da SQL Server generelt ikke gider cache en batch, der primært består af procedurekald), men ikke for selve den lagrede procedure. Både med og uden sp_something
procedure, der findes i master (og når den eksisterer, både med og uden at den er markeret som et systemobjekt), kalder kaldene til sp_something
i brugerdatabasen skal du aldrig "tilfældigt" kalde proceduren i master, og aldrig generere nogen CacheMiss
begivenheder for proceduren.
Dette var på SQL Server 2012. Jeg gentog de samme test ovenfor på SQL Server 2008 R2 og fandt lidt forskellige resultater:
Så på SQL Server 2008 R2 ser vi en ekstra CacheMiss
hændelse, der ikke forekommer i SQL Server 2012. Dette sker i alle scenarier (ingen tilsvarende objektmaster, et objekt i master markeret som et systemobjekt og et objekt i master der ikke er markeret som et systemobjekt). Umiddelbart var jeg nysgerrig efter, om denne ekstra begivenhed ville have nogen mærkbar indflydelse på ydeevnen.
Performanceproblem:Fakta eller fiktion?
Jeg lavede en yderligere procedure uden sp_
præfiks for at sammenligne rå ydeevne, CacheMiss
til side:
USE sp_prefix; GO CREATE PROCEDURE dbo.proc_something AS BEGIN SELECT 'sp_prefix', DB_NAME(); END GO
Så den eneste forskel mellem sp_something
og proc_something
. Jeg oprettede derefter indpakningsprocedurer for at udføre dem 1000 gange hver ved hjælp af EXEC sp_prefix.dbo.<procname>
, EXEC dbo.<procname>
og EXEC <procname>
syntaks, med tilsvarende lagrede procedurer, der lever i master og markeret som et systemobjekt, lever i master, men ikke markeret som et systemobjekt, og slet ikke lever 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åler runtime-varigheden af hver indpakningsprocedure med SQL Sentry Plan Explorer, viser resultaterne, at brug af sp_
præfiks har en betydelig indvirkning på den gennemsnitlige varighed i næsten alle tilfælde (og bestemt i gennemsnit):