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

Проста параметризация и тривиални планове — част 1

Това е първата част от поредица за проста параметризация и тривиални планове . Тези две функции за компилация са тясно свързани и имат сходни цели. И двете са насочени към производителност и ефективност за работни натоварвания, които често подават прости изявления.

Въпреки „простите“ и „тривиалните“ имена, и двете имат фини поведения и детайли за изпълнение, които могат да затруднят разбирането им как работят. Тази поредица не се спира твърде дълго върху основите, а се концентрира върху по-малко известни аспекти, които вероятно ще заблудят дори и най-опитните професионалисти в базата данни.

В тази първа част, след кратко въведение, разглеждам ефектите от простата параметризация в кеша на плана.

Проста параметризация

Почти винаги е по-добре да изрично параметризирате изявления, вместо да разчита на сървъра да го направи. Да бъдеш изричен ви дава пълен контрол върху всички аспекти на процеса на параметризиране, включително къде се използват параметрите, точните използвани типове данни и кога плановете се използват повторно.

Повечето клиенти и драйвери предоставят специфични начини за използване на изрична параметризация. Има и опции като sp_executesql , съхранени процедури и функции.

Няма да навлизам в свързаните с тях проблеми с подслушването на параметри или SQL инжектирането, защото, макар и важни, те не са в центъра на тази серия. Все пак трябва да пишете код, като и двете са близо до ума си.

За наследени приложения или друг код на трети страни, който не може лесно да се промени, изричното параметризиране може да не винаги е възможно. Може да успеете да преодолеете някои препятствия, като използвате ръководства за шаблонен план. Във всеки случай това би било необичайно работно натоварване, което не съдържа поне някои параметризирани изрази от страна на сървъра.

Планове на Shell

Когато SQL Server 2005 въведе Принудителна параметризация , съществуващата автоматична параметризация функцията беше преименувана на Проста параметризация . Въпреки промяната в терминологията, проста параметризация работи по същия начин като автоматично параметризиране винаги правеше:SQL Server се опитва да замени константни литерални стойности в ad hoc изрази с маркери на параметри. Целта е да се намалят компилациите чрез увеличаване на повторното използване на кеширания план.

Нека разгледаме пример, използвайки базата данни Stack Overflow 2010 на SQL Server 2019 CU 14. Съвместимостта на базата данни е настроена на 150, а прагът на разходите за паралелизъм е настроен на 50, за да се избегне паралелизъм за момента:

EXECUTE sys.sp_configure @configname ='показване на разширени опции', @configvalue =1;RECONFIGURE;GOEXECUTE sys.sp_configure @configname ='праг на разходите за паралелизъм', @configvalue =G50; 

Примерен код:

-- Изчистете кеша на плановете за тази база данни ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =2521;GOSELECT U.DisplayNameFROM WHERE Users AS U8 AS U8;GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =3144;GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =3151;GO

Тези твърдения съдържат предикати, които се различават само по своите постоянни буквални стойности. SQL Server успешно прилага проста параметризация , което води до параметризиран план. Единичният параметризиран план се използва четири пъти, както можем да видим, като потърсим кеша на плана:

ИЗБЕРЕТЕ CP.usecounts, CP.cacheobjtype, CP.objtype, CP.size_in_bytes, ST.[текст], QP.query_planFROM sys.dm_exec_cached_plans КАТО CPOUTER ПРИЛОЖИ sys.dm_exec_sql_text (CP.plan_exec_sql_text (CP.plan_exec_sql_text) (CP.plan_execquedry_APPLY_SPL._execmdry) CP.plan_handle) КАТО QPWHERE ST.[text] НЕ КАТО '%dm_exec_cached_plans%' И ST.[text] КАТО '%DisplayName%Users%'ORDER BY CP.usecounts ASC;

Резултатите показват Adhoc запис в кеша на плана за всеки оригинален израз и един Подготвен план:

Четири adhoc плана и един подготвен план

A Подготвен операторът е подобен на съхранена процедура, с параметри, изведени от буквални стойности, намерени в Adhoc изявление. Споменавам това като полезен умствен модел, когато мисля за процеса на параметризиране от страна на сървъра.

Забележете, че SQL Server кешира и двете оригиналния текст и параметризираната форма. Когато простата параметризация е успешна, планът, свързан с оригиналния текст, е Adhoc и не съдържа пълен план за изпълнение. Вместо това, кешираният план е обвивка с много малко освен указател към Prepared параметризиран план.

XML представянето на плановете на shell съдържат текст като:

Планиране на елементи 

Shell плановете са по-малки от пълен компилиран план – 16KB вместо 40KB в примера. Това все още може да добави значително количество памет, ако имате много изрази, използващи проста параметризация или много различни стойности на параметрите. Повечето екземпляри на SQL Server не са толкова затрупани с памет, че да могат да си позволят да я прахосват по този начин. Shell плановете се считат за много еднократни от SQL Server, но намирането и премахването им консумира ресурси и може да се превърне в спорна точка.

Можем да намалим общата консумация на памет за шел планове, като активираме опцията за оптимизиране за ad hoc работни натоварвания.

EXECUTE sys.sp_configure @configname ='показване на разширени опции', @configvalue =1;RECONFIGURE;GOEXECUTE sys.sp_configure @configname ='оптимизиране за ad hoc работни натоварвания', @configvalue =1; 

Това кешира мъничко мъниче при първия път, когато се срещне ad hoc изявление вместо обвивка. Копчето служи като отметка, така че сървърът да си спомни, че е виждал точния текст на изявлението преди. След като срещнете същия текст втори път, компилацията и кеширането продължават, сякаш оптимизирани за ad hoc работни натоварвания не бяха активирани.

Повторно стартиране на примера с оптимизиране за ad hoc работни натоварвания разрешено показва ефекта върху кеша на плана.

Компилирани елементи на план

Никакъв план не се кешира за ad-hoc изявленията, а само мъниче. Няма ParameterizedPlanHandle показалец към Подготвен план, въпреки че пълен параметризиран план е кеширано.

Изпълнението на тестовите партиди за втори път (без изчистване на кеша на плана) дава същия резултат като при оптимизиране за ad hoc работни натоварвания не беше активиран — четири Adhoc shell планове, сочещи към Подготвени план.

Преди да продължите, нулирайте оптимизирането за ad hoc работни натоварвания настройка на нула:

EXECUTE sys.sp_configure @configname ='оптимизиране за ad hoc работни натоварвания', @configvalue =0;RECONFIGURE;

Ограничения за размера на кеша на плана

Независимо дали се използват планови обвивки или планове, все още има недостатъци на всички тези Adhoc записи в кеша. Вече споменах общото използване на паметта, но всеки кеш на плана също има максимален брой на вписванията. Дори когато общото използване на паметта не е проблем, самото количество може да бъде.

Ограниченията могат да бъдат повишени с документиран флаг за проследяване 174 (брой вписвания) и флаг за проследяване 8032 (общ размер). В зависимост от натоварването и други изисквания за памет, това може да не е най-доброто решение. В крайна сметка това просто означава кеширане на повече Adhoc с ниска стойност планове, отнемащи паметта от други нужди.

Кеширане само на подготвени планове

Ако работното натоварване рядко издава ad hoc партиди с точно един и същ текст на изявлението, кеширане на обвивки на план или планови заглавия е загуба на ресурси. Той изразходва памет и може да предизвика спор, когато SQL плановете кеш магазин (CACHESTORE_SQLCP ) трябва да се свие, за да се побере в конфигурираните граници.

Идеалното би било да се параметризират входящите ad-hoc партиди, но само кеширайте параметризираната версия. Правенето на това има цена, тъй като бъдещите ad-hoc изрази трябва да бъдат параметризирани, преди да могат да бъдат съпоставени с параметризирания кеширан план. От друга страна, това щеше да се случи така или иначе, тъй като вече посочихме точно текстовите съвпадения са рядкост за целевото работно натоварване.

За натоварвания, които се възползват от простата параметризация, но не и от кеширането на Adhoc записи, има няколко опции.

Недокументиран флаг за проследяване

Първата опция е да активирате флаг за недокументирано проследяване 253. Това предотвратява кеширането на Adhoc планове напълно. Това не просто ограничава броя на такива планове или не им позволява да „остават“ в кеша, както понякога се предлага.

Флагът за проследяване 253 може да бъде активиран на ниво сесия – ограничавайки ефектите му само до тази връзка – или по-широко като глобален или стартов флаг. Той също така функционира като намек за заявка, но използването им предотвратява простата параметризация, която би била контрапродуктивна тук. Има частичен списък на нещата, които пречат на простата параметризация в техническия документ на Microsoft, плановото кеширане и повторното компилиране в SQL Server 2012.

С флаг за проследяване 253 активен преди партидата да бъде компилирана , само Подготвен изявленията се кешират:

ПРОМЕНЯ КОНФИГУРАЦИЯ С ОБХВАТ НА БАЗА ДАННИ ИЗЧИСТВАНЕ НА ПРОЦЕДУРА_CACHE;GO-- Не кеширайте ad-hoc плановеDBCC TRACEON (253);GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =2521;GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =2521;GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =2521; WHERE U.Reputation =2827;GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =3144;GOSELECT U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =3151;GO-- Кеширане на ad-hoc планове отново (DBCC OFF) 253);GO

Заявката за кеш на плана потвърждава само Подготвен изявлението се кешира и се използва повторно.

Кешира се само подготвеното изявление

Некешируемата партида

Втората опция е да включите изявление, което маркира цялата партида като некешируема . Подходящите изрази често са свързани със сигурността или по друг начин чувствителни по някакъв начин.

Това може да звучи непрактично, но има няколко смекчавания. Първо, чувствителният израз не трябва да се изпълнява – той просто трябва да е наличен . Когато това условие е изпълнено, потребителят, изпълняващ пакета, дори не се нуждае от разрешение за изпълнение на чувствителния оператор. Забележете внимателно, ефектът е ограничен до партидата, съдържаща чувствителния израз.

Два подходящо чувствителни израза и примерна употреба са показани по-долу (с тестовите изрази вече в една партида):

ПРОММЕНЯТЕ КОНФИГУРАЦИЯТА С ОБХВАТ НА БАЗА ДАННИ ИЗЧИСТВАНЕ НА ПРОЦЕДУРА_CACHE;GO-- Предотвратете кеширането на всички изрази в този пакет.-- Не е необходимо да съществуват нито KEY, нито CERTIFICATE.-- Не са необходими специални разрешения.-- GOTO се използва, за да се гарантира, че операторите са не се изпълнява. GOTO Старт ОТВОРЕН СИМЕТРИЧЕН КЛЮЧ Банан ДЕКРИПТИРАНЕ ЧРЕЗ СЕРТИФИКАТ Banana;Старт:/* Друг начин за постигане на същия ефект без GOTOIF 1 =0ЗАПОЧВАНЕ СЪЗДАВАНЕ НА РОЛЯ НА ПРИЛОЖЕНИЕ Banana С ПАРОЛА ='';END;*/ SELECT U.DisplayName Потребители КАТО U WHERE U.Reputation =2521; ИЗБЕРЕТЕ U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =2827; ИЗБЕРЕТЕ U.DisplayNameFROM dbo.Users AS U WHERE U.Reputation =3144; ИЗБЕРЕТЕ U.DisplayNameFROM dbo.Users КАТО U КЪДЕТО U.Reputation =3151;GO

Подготвени планове, създадени чрез проста параметризация все още се кешират и се използват повторно, въпреки че родителската партида е маркирана като некешируема.

Кешира се само подготвеното изявление

Нито едно от двете решения не е идеално, но докато Microsoft не предостави документирано и поддържано решение за този проблем, те са най-добрите опции, за които знам.

Край на част 1

Има още много полета за разглеждане по тази тема. Част втора ще обхване типовете данни, присвоени при проста параметризация е нает.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Изненади и предположения при представянето:DATEADD

  2. Броячи на PerfMon на коляното:Продължителност на живота на страницата

  3. Свързване на Linux и UNIX към Azure SQL Data Warehouse

  4. Подход към настройката на индекса – част 2

  5. Как да комбинирате резултатите от две заявки в SQL