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