sql >> Databáze >  >> RDS >> Database

Je předpona sp_ stále ne-ne?

Ve světě SQL Serveru existují dva typy lidí:ti, kteří mají rádi všechny své objekty s předponou, a ti, kteří ne. První skupina se dále dělí na dvě kategorie:ti, kteří předponu uloženým procedurám mají sp_ a ti, kteří si vyberou jiné předpony (například usp_ nebo proc_ ). Dlouhodobým doporučením bylo vyhnout se sp_ prefix, a to jak z důvodu výkonu, tak z důvodu zabránění nejednoznačnosti nebo kolizím, pokud náhodou zvolíte název, který již existuje v systémovém katalogu. Kolize jsou jistě stále problémem, ale za předpokladu, že jste si ověřili název svého objektu, je to stále problém s výkonem?

Verze TL;DR:ANO.

Předpona sp_ je stále ne-ne. Ale v tomto příspěvku vysvětlím proč, jak vás SQL Server 2012 může vést k domněnce, že tato varovná rada již neplatí, a některé další potenciální vedlejší účinky výběru této konvence pojmenování.

Jaký je problém s sp_?

sp_ prefix neznamená to, co si myslíte, že znamená:většina lidí si myslí sp znamená „uložená procedura“, i když ve skutečnosti znamená „speciální“. Uložené procedury (stejně jako tabulky a pohledy) uložené v hlavním serveru s sp_ prefix jsou přístupné z libovolné databáze bez řádného odkazu (za předpokladu, že lokální verze neexistuje). Pokud je procedura označena jako systémový objekt (pomocí sp_MS_marksystemobject (nedokumentovaný a nepodporovaný systémový postup, který nastavuje is_ms_shipped až 1), pak se procedura v master provede v kontextu volající databáze. Podívejme se na jednoduchý příklad:

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

Výsledky:

(0 row(s) affected)

sp_test    foo

(1 row(s) affected)

Problém s výkonem pochází ze skutečnosti, že hlavní server může být zkontrolován na ekvivalentní uloženou proceduru v závislosti na tom, zda existuje místní verze procedury a zda je ve skutečnosti ekvivalentní objekt v hlavním serveru. To může vést k dodatečné režii metadat a také k dalšímu SP:CacheMiss událost. Otázkou je, zda je tato režie hmatatelná.

Podívejme se tedy na velmi jednoduchý postup v testovací databázi:

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

A ekvivalentní postupy v 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:skutečnost nebo fikce?

Pokud spustíme rychlý test z naší testovací databáze, uvidíme, že provedení těchto uložených procedur ve skutečnosti nikdy nevyvolá verze z hlavní, bez ohledu na to, zda proceduru správně kvalifikujeme pro databázi nebo schéma (běžná mylná představa) nebo zda označíme hlavní verze jako systémový objekt:

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

Výsledky:

sp_prefix    sp_prefix
sp_prefix    sp_prefix
sp_prefix    sp_prefix

Spusťte také Quick Trace® pomocí SQL Sentry ke sledování, zda existují nějaké SP:CacheMiss události:

Vidíme CacheMiss události pro dávku ad hoc, která volá uloženou proceduru (protože SQL Server se obecně nebude obtěžovat ukládáním dávky, která sestává primárně z volání procedur), ale ne pro samotnou uloženou proceduru. Jak s sp_something, tak bez něj procedura existující v masteru (a když existuje, jak s, tak bez toho, aby byla označena jako systémový objekt), volání sp_something v databázi uživatelů nikdy "omylem" nevolejte proceduru v masteru a nikdy negenerujte žádnou CacheMiss události pro postup.

To bylo na SQL Server 2012. Zopakoval jsem stejné testy výše na SQL Server 2008 R2 a našel jsem mírně odlišné výsledky:

Takže na SQL Server 2008 R2 vidíme další CacheMiss událost, která nenastane v SQL Server 2012. K tomu dochází ve všech scénářích (žádný ekvivalentní hlavní objekt, objekt v hlavním serveru označený jako systémový objekt a objekt v hlavním serveru, který není označen jako systémový objekt). Okamžitě jsem byl zvědavý, zda tato dodatečná událost bude mít nějaký znatelný dopad na výkon.

Problém s výkonem:skutečnost nebo fikce?

Udělal jsem další postup bez sp_ prefix pro porovnání hrubého výkonu, CacheMiss stranou:

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

Takže jediný rozdíl mezi sp_something a proc_something . Poté jsem vytvořil procedury wrapper, abych je každou provedl 1000krát, pomocí EXEC sp_prefix.dbo.<procname> , EXEC dbo.<procname> a EXEC <procname> syntaxe s ekvivalentními uloženými procedurami žijícími v masteru a označenými jako systémový objekt, žijícími v masteru, ale neoznačené jako systémový objekt a vůbec nebydící v masteru.

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

Výsledky měření doby běhu každé procedury wrapperu pomocí SQL Sentry Plan Explorer ukazují, že pomocí sp_ prefix má významný dopad na průměrnou dobu trvání téměř ve všech případech (a rozhodně v průměru):