sql >> Database teknologi >  >> RDS >> Database

Er sp_-præfikset stadig et nej-nej?

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 Trace® bruge SQL Sentry til at observere, om der er nogen SP: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):