Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Вътрешни елементи на SQL Server:Планово кеширане, т. I – Планове за повторно използване

SQL Server съществува повече от 30 години и аз работя със SQL Server почти толкова дълго. Виждал съм много промени през годините (и десетилетия!) и версии на този невероятен продукт. В тези публикации ще споделя с вас как гледам на някои от функциите или аспектите на SQL Server, понякога заедно с малко историческа перспектива.

Разгледайте последните блогове на Kalen за проблемните оператори тук.

Създаването на планове за диагностика на SQL сървър може да бъде скъпо, тъй като оптимизаторът на заявки трябва да може да намери добър план за всяка подадена правна заявка. Оптимизаторът оценява множество ред на присъединяване, множество индекси. и различни видове алгоритми за присъединяване и групиране, в зависимост от вашата заявка и включените таблици. Ако същата заявка се изпълни повторно, SQL Server може да спести много ресурси чрез повторно използване на съществуващ план. Но не винаги е възможно да използвате повторно съществуващ план и не винаги е добре да го направите. В следващите две статии ще разгледаме кога планът се използва повторно и кога се компилира.

Първо, ще разгледаме различните варианти на планове и изгледа на метаданни, който използвам най-често, за да разгледам какво има в кеша на моя план. Написах собствен изглед, който предоставя информацията, която намирам за най-полезна най-често. SQL Server кешира шест различни типа планове за заявки, но само два обикновено се използват за настройка на кеша на плана. Това са COMPILED PLAN и COMPILED PLAN STUB. Моят изглед филтрира всички, освен тези два типа кеш обекти. СЪСТАВЕНИТЕ ПЛАНОВЕ се предлагат в три разновидности:AD HOC, PREPARED и PROC. Ще коментирам и трите вида.

Дори ако просто разглеждаме КОМПИЛИРАНИ ПЛАНОВЕ, все още има много планове в кеша, които обикновено трябва да се игнорират, тъй като се генерират от самия SQL Server. Те включват планове за търсене на файлов поток или индекси за търсене в пълен текст или вътрешни заявки, работещи с OLTP в паметта. И така, моят изглед добавя филтри, за да опитам да отбия повечето от плановете, които не ме интересуват. Можете да изтеглите скрипт за изграждане на този изглед, наречен sp_cacheobjects , оттук

Дори с всички филтри, които използва моят изглед, все още има някои от собствените вътрешни заявки на SQL Server в кеша; Обикновено изчиствам кеша на плана често, когато правя тестове в тази област. Най-простият начин да изчистите ВСИЧКИ планове от кеша е с командата:DBCC FREEPROCCACHE.

Компилирани планове Adhoc

Най-простият тип план е Adhoc. Това се използва за основни заявки, които не се вписват в друга категория. Ако сте изтеглили моя скрипт и сте създали моя изглед sp_cacheobjects, можете да изпълните следното. Всяка версия на базата данни AdventureWorks трябва да работи. Този скрипт прави копие на таблица и изгражда няколко уникални индекса върху нея. Той също така масажира сумата SubTotal, за да премахне всички десетични цифри.

USE AdventureWorks2016;
GO
DROP TABLE IF EXISTS newsales;
GO
-- Make a copy of the Sales.SalesOrderHeader table
SELECT * INTO dbo.newsales
FROM Sales.SalesOrderHeader;
GO
UPDATE dbo.newsales
SET SubTotal = cast(cast(SubTotal as int) as money);
GO
CREATE UNIQUE index newsales_ident
    ON newsales(SalesOrderID);
GO
CREATE INDEX IX_Sales_SubTotal ON newsales(SubTotal);
GO
-- Adhoc query plan reuse
DBCC FREEPROCCACHE;
GO
-- adhoc query
SELECT * FROM dbo.newsales
WHERE SubTotal = 4;
GO
SELECT * FROM sp_cacheobjects;
GO

В моя резултат виждате два Adhoc плана. Единият е за оператора SELECT от newssales таблица, а другият е за SELECT от моите sp_cacheobjects изглед. Тъй като планът е кеширан, ако ТОЧНО същата заявка се изпълни отново, същият план може да бъде използван повторно и ще видите usecounts увеличение на стойността. Има обаче уловка. За да може един Adhoc план да бъде използван повторно, SQL низът трябва да бъде абсолютно същият. Ако промените някакви знаци в SQL, заявката не се разпознава като същата и се генерира нов план. Ако дори добавя интервал, включвам коментара или нов прекъсване на ред, това не е същия низ. Ако сменя регистъра, това означава, че има различни стойности на ASCII код, следователно не е един и същ низ.

Можете да изпробвате това сами, като стартирате различни варианти на първото ми изявление SELECT от newssales маса. Ще видите различен ред в кеша за всеки от тях. След като изпълних няколко варианта – промяна на номера, който търсих, промяна на главния букв, добавяне на коментар и нов ред, виждам следното в кеша. SELECT от моя изглед се използва повторно, но всичко останало има usecounts стойност 1.

Едно допълнително изискване за повторно използване на план за заявка Adhoc е, че сесията, изпълняваща заявката, трябва да има същите опции SET в сила . Има друга колона в изхода, която можете да видите вдясно от текста на заявката, наречена SETOPTS. Това е битов низ с бит за всяка съответна опция SET. Ако промените една от опциите, например SET ANSI_NULLS OFF, битовият низ ще се промени и същият план с оригиналния битов низ не може да бъде използван повторно.

Подготвени компилирани планове

Вторият тип кеширан компилиран план е ПОДГОТОВЕН план. Ако вашата заявка отговаря на определен набор от изисквания. Всъщност може да се параметризира автоматично. Той се показва в метаданните като ПОДГОТОВЕН и SQL низът показва маркер за параметър. Ето един пример:

Планът ПОДГОТОВЕН показва маркера на параметъра като @1 и не включва действителната стойност. Забележете, че има ред за ADHOC заявка с действителна стойност 5555, но това всъщност е само „обвивка“ на истинската заявка. Той не кешира целия план, а само заявката и няколко идентифициращи подробности, за да помогне на процесора на заявки да намери параметризирания план в кеша. Обърнете внимание на размера (използвани страници ) е много по-малък от ПОДГОТОВЕНИЯ план.

Режимът на параметриране по подразбиране, наречен ПРОСТА параметризация, е изключително строг за това какви планове могат да бъдат параметризирани. Всъщност само най-простите заявки са параметризирани по подразбиране. Заявките, които съдържат JOIN, GROUP BY, OR и много други относително често срещани конструкции на заявка, предотвратяват параметризирането на заявка. Освен че няма нито една от тези конструкции, най-важното нещо за ПРОСТА параметризация е заявката да е БЕЗОПАСНА. Това означава, че има само един възможен план, независимо какви стойности се предават за който и да е параметр. (Разбира се, заявка без никакви параметри също може да бъде БЕЗОПАСНА.) Моята заявка търси точно съвпадение в колоната SalesOrderID , който има уникален индекс върху него. Така че съществуващият неклъстериран индекс може да се използва за намиране на всеки съвпадащ ред. Без значение каква стойност използвам, 55555 или нещо друго, никога няма да има повече от един ред, което означава, че планът все още ще бъде добър.

В моя пример за план за заявка Adhoc търсех съвпадащи стойности за SubTotal . Някои Междинни суми стойностите се появяват няколко пъти или изобщо не, така че един неклъстериран индекс би бил добър. Но други стойности могат да се появят много пъти, така че индексът НЕ би бил полезен. По този начин планът на заявката не е БЕЗОПАСЕН и заявката не може да бъде параметризирана. Ето защо видяхме Adhoc план за първия ми пример.

АКО имате заявки с JOIN или други забранени конструкции, можете да кажете на SQL Server да бъде по-агресивен при параметризиране, като промените опция за база данни:

ALTER DATABASE AdventureWorks2016 SET parameterization FORCED;
GO

Настройването на вашата база данни на ПРИНУДИТЕЛНА параметризация означава, че SQL Server ще параметризира много повече заявки, включително тези с JOIN, GROUP BY, OR и т.н. Но също така означава, че SQL Server може да параметризира заявка, която не е БЕЗОПАСНА. Може да измисли план, който е добър, когато се връщат само няколко реда, и след това да използва повторно плана, когато се връщат много редове. Това може да завърши с много неоптимална производителност.

Една последна опция за подготвен план е, когато изрично изготвите план. Това поведение обикновено се извиква чрез приложение с SQLPrepare и SQLEExecute API. Вие посочвате каква е заявката с маркиране на параметри, посочвате типовете данни и определяте конкретните стойности, които да използвате. След това същата заявка може да се изпълни отново с различни специфични стойности и ще се използва съществуващият план. Въпреки че използването на изрично подготвени планове може да бъде възможно за онези случаи, когато SQL Server не параметризира и вие желаете да го направи, това не пречи на SQL Server да използва план, който НЕ е добър за следващите параметри. Трябва да тествате заявките си с много различни входни стойности и да се уверите, че получавате очакваната производителност, ако и когато планът се използва повторно.

Метаданните (напр. моите sp_cacheobjects изглед) просто показва ПОДГОТОВЕН и за трите типа планове:ПРИНУДИТЕЛНА и ПРОСТА автопараметризация и ИЗРИЧНА параметризация.

Прок компилирани планове

Последният objtype стойността за компилирани планове е за съхранена процедура, която се показва като Proc. Когато е възможно, съхранените процедури са най-добрият избор за код за многократна употреба, поради лекотата им на управление от самия сървър, но това не означава, че винаги ще дават най-добра производителност. Точно както при използването на опцията за принудително параметриране (а също и изричното параметризиране), съхранените процедури използват „смъркане на параметри“. Това означава, че първата предадена стойност на параметъра определя плана. Ако последващите изпълнения се представят добре със същия план, тогава подслушването на параметри не е проблем и всъщност може да бъде от полза, защото ни спестява разходите за повторно компилиране и повторно оптимизиране. Въпреки това, ако последващите изпълнения с различни стойности не трябва да използват оригиналния план, тогава имаме проблем. Ще ви покажа пример за подслушване на параметри, което причинява проблем

Ще създам съхранена процедура въз основа на newssales таблица, която използвахме по-рано. Процедурата ще има една заявка, която филтрира въз основа на SalesOrderID колона, върху която изградихме неклъстериран индекс. Заявката ще се основава на неравенство, така че за някои стойности заявката може да върне само няколко реда и да използва индекса, а за други стойности заявката може да върне МНОГО редове. С други думи, заявката не е БЕЗОПАСНА.

USE AdventureWorks2016;
GO
DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num;
GO

Ще използвам опцията SET STATISTICS IO ON, за да видя колко работа се извършва, когато процедурата се изпълни. Първо, ще го изпълня с параметър, който връща само няколко реда:

SET STATISTICS IO ON
GO
EXEC get_sales_range 43700;
GO

Стойността на STATISTICS IO съобщава, че са били необходими 43 логически четения, за да се върнат 41 реда. Това е нормално за неклъстериран индекс. Сега изпълняваме процедурата отново с много по-голяма стойност.

EXEC get_sales_range 66666;
GO
SELECT * FROM sp_cacheobjects;
GO
This time, we see that SQL Server used a whole lot more reads:

Всъщност сканиране на таблица в новинарските продажби таблицата отнема само 843 четения, така че това е много по-лоша производителност от сканиране на таблица. sp_cacheobjects изгледът ни показва, че планът PROC е бил използван повторно за това второ изпълнение. Това е пример, когато подслушването на параметри НЕ е добро.

И така, какво можем да направим, когато подслушването на параметри е проблем? В следващата публикация ще ви кажа кога SQL Server излезе с нов план и не използва повторно стари. Ще разгледаме как можете да принудите (или насърчите) повторното компилиране, а също така ще видим кога SQL Server автоматично прекомпилира вашите заявки.

Spotlight Cloud може да революционизира вашия мониторинг на производителността и диагностика на SQL сървър. Започнете с безплатната си пробна версия, като използвате връзката по-долу:


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да намерите оптималния уникален идентификатор в таблица в SQL Server:sp_special_columns

  2. ExecuteNonQuery:Свойството на връзката не е инициализирано.

  3. SET срещу SELECT при присвояване на променливи?

  4. T-sql - определя дали стойността е цяло число

  5. SQL Server 2016:Въздействие върху производителността на винаги криптирани