Създаването на таблица е относително ресурсоемка и отнемаща време операция. Сървърът трябва да локализира и разпредели място за съхранение за новите структури от данни и индекси и да направи съответните записи в множество системни таблици с метаданни. Цялата тази работа трябва да се извършва по начини, които винаги ще работят правилно при висок паралелизъм и които отговарят на всички ACID гаранции, очаквани от релационна база данни.
В SQL Server това означава вземане на правилните видове ключалки и ключалки, в правилната последователност, като същевременно се гарантира, че подробните записи в дневника на транзакциите са безопасно ангажирани в постоянно съхранение преди всякакви физически промени в базата данни. Тези записи в журнала гарантират, че системата може да върне базата данни в последователно състояние в случай на връщане на транзакция или срив на системата.
Изпускането на маса е също толкова скъпа операция. За щастие повечето бази данни не създават или пускат таблици с голяма честота. Очевидното изключение от това е системната база данни tempdb . Тази единна база данни съдържа физическото съхранение, структурите за разпределение, системните метаданни и записите в журнала на транзакциите за всички временни таблици и променливи на таблицата в целия екземпляр на SQL Server.
В природата на временните таблици и променливите на таблици е да се създават и отпадат много по-често от други типове обекти на база данни. Когато тази естествено висока честота на създаване и унищожаване се комбинира с концентриращия ефект на всички временни таблици и таблични променливи, свързани с една база данни, едва ли е изненадващо, че може да възникне спор в структурите за разпределение и метаданни на tempdb база данни.
Временно кеширане на обекти
За да намалите въздействието върху tempdb структури, SQL Server може да кешира временни обекти за повторна употреба. Вместо да изпуска временен обект, SQL Server запазва системните метаданни и съкращава данните от таблицата. Ако таблицата е 8MB или по-малка, съкращаването се извършва синхронно; в противен случай се използва отложен спад. И в двата случая съкращаването намалява изискването за съхранение до една (празна) страница с данни, а информацията за разпределение до една IAM страница.
Кеширането избягва почти всички разходи за разпределение и метаданни за създаване на временния обект следващия път. Като страничен ефект от правенето на по-малко промени в tempdb база данни, отколкото пълен цикъл на изпускане и пресъздаване, временното кеширане на обекти също намалява необходимото количество регистриране на транзакциите.
Постигане на кеширане
Променливите на таблицата и локалните временни таблици могат да бъдат кеширани. За да отговаряте на изискванията за кеширане, локална временна таблица или променлива на таблица трябва да бъде създаден в модул:
- Запазена процедура (включително временна съхранена процедура)
- Задействане
- Функция с таблична стойност с множество изрази
- Скаларна дефинирана от потребителя функция
Връщаната стойност на функция с таблична стойност с множество изрази е таблична променлива, която сама по себе си може да бъде кеширана. Параметрите със стойност на таблицата (които също са таблични променливи) могат да се кешират, когато параметърът се изпраща от клиентско приложение, например в .NET код с помощта на SqlDbType.Structured
. Когато изразът е параметризиран, структурите с параметри с таблично значение могат да се кешират само на SQL Server 2012 или по-нова версия.
Следното не може да се кешира:
- Глобални временни таблици
- Обекти, създадени с помощта на ad-hoc SQL
- Обекти, създадени с помощта на динамичен SQL (напр. с помощта на
EXECUTE
). илиsys.sp_executesql
)
За да бъде кеширан, временен обект допълнително не трябва :
- Именуват ограничения (ограниченията без изрични имена са напълно добре)
- Изпълнете „DDL“ след създаването на обект
- Бъдете в модул, дефиниран с помощта на
WITH RECOMPILE
опция - Бъдете извикани с помощта на
WITH RECOMPILE
опция наEXECUTE
изявление
За да се справите изрично с някои често срещани погрешни схващания:
TRUNCATE TABLE
не предотвратяване на кеширанеDROP TABLE
не предотвратяване на кеширанеUPDATE STATISTICS
не предотвратяване на кеширане- Автоматичното създаване на статистически данни не предотвратяване на кеширане
- Ръчно
CREATE STATISTICS
ще предотвратяване на кеширане
Всички временни обекти в един модул се оценяват за пригодност за кеширане поотделно. Модул, който съдържа един или повече временни обекти, които не могат да бъдат кеширани, все пак може да отговаря на изискванията за кеширане на други временни обекти в рамките на същия модул.
Често срещан модел, който деактивира кеширането за временни таблици, е създаването на индекси след първоначалния израз за създаване на таблица. В повечето случаи това може да се заобиколи с помощта на първичен ключ и уникални ограничения. В SQL Server 2014 и по-нови версии имаме опцията да добавяме неуникални неклъстерирани индекси директно в израза за създаване на таблица с помощта на INDEX
клауза.
Наблюдение и поддръжка
Можем да видим колко временни обекта са кеширани в момента с помощта на кеш броячите DMV:
SELECT DOMCC.[type], DOMCC.pages_kb, DOMCC.pages_in_use_kb, DOMCC.entries_count, DOMCC.entries_in_use_countFROM sys.dm_os_memory_cache_counters КАТО DOMCC WHERE DOMCC.[name] =N'Temporary Various Tables';Примерен резултат е:
Записът в кеша се счита за използван докато се изпълнява която и да е част от съдържащия модул. Едновременното изпълнение на един и същ модул ще доведе до създаване на множество кеширани временни обекта. Множество планове за изпълнение за един и същ модул (може би поради различна сесия
SET
опции) също ще доведе до множество записи в кеша за един и същ модул.Записите в кеша могат да бъдат остарели с течение на времето в отговор на конкуриращи се нужди от памет. Кешираните временни обекти също могат да бъдат премахнати (асинхронно, от фонова системна нишка), когато планът за изпълнение на родителския модул бъде премахнат от кеша на плана.
Въпреки че не се поддържа (или по някакъв начин се препоръчва) за производствени системи, временното хранилище на кеша на обекти може да бъде ръчно изчистено напълно за целите на тестване с:
DBCC FREESYSTEMCACHE('Временни таблици и променливи в таблицата') WITH MARK_IN_USE_FOR_REMOVAL;WAITFOR DELAY '00:00:05';Петсекундното закъснение дава време за изпълнение на задачата за почистване на фона. Имайте предвид, че тази команда е всъщност опасна . Трябва да го използвате (на свой собствен риск) само в тестов екземпляр, до който имате изключителен достъп. След като приключите с тестването, рестартирайте екземпляра на SQL Server.
Подробности за внедряване на кеширане
Табличните променливи се реализират от „реална“ потребителска таблица в tempdb база данни (въпреки че не е таблица, която можем да потърсим директно). Името на асоциираната таблица е "#", последвано от осемцифрено шестнадесетично представяне на идентификатора на обекта. Следната заявка показва връзката:
- Таблица променливаDECLARE @Z AS таблица (z цяло число NULL); -- Съответстващ запис на sys.tablesSELECT T.[име], ObjIDFromName =CONVERT(цяло число, CONVERT(двоично(4), RIGHT(T.[име], 8), 2)), T.[идентификатор_на_обект], T.[ type_desc], T.create_date, T.modify_dateFROM tempdb.sys.tables AS T WHERE T.[name] LIKE N'#[0-9A-F][0-9A-F][0-9A-F][0][0] -9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]';Примерен резултат е показан по-долу. Забележете как идентификаторът на обекта, изчислен от името на обекта, съвпада с действителния идентификатор на обекта:
Изпълнението на този скрипт като ad-hoc SQL ще доведе до различен tempdb ID на обект (и име на обект) при всяко изпълнение (без кеширане). Поставянето на същия скрипт вътре в модул (например съхранена процедура) ще позволи променливата на таблицата да бъде кеширана (докато не се използва динамичен SQL), така че идентификационният номер и името на обекта ще бъдат еднакви при всяко изпълнение.
Когато променливата на таблицата не е кеширана, основната таблица се създава и отпада всеки път. Когато временното кеширане на обекти е активирано, таблицата се съкращава в края на модула, вместо да бъде изпусната. Няма промени към системните метаданни, когато променливата на таблицата е кеширана. Въздействието върху структурите за разпределение и регистрирането на транзакции е ограничено до изтриване на редовете в таблицата и премахване на излишните данни и страници за разпределение, когато модулът приключи.
Временни таблици
Когато се използва временна таблица вместо променлива на таблица, основният механизъм е по същество същият, само с няколко допълнителни стъпки за преименуване:Когато временна таблица не е кеширана , то се вижда в tempdb с познатото име, предоставено от потребителя, последвано от куп долни черти и шестнадесетично представяне на идентификатора на обекта като краен суфикс. Локалната временна таблица остава, докато не бъде изрично отхвърлена или докато обхватът, в който е създадена, приключи. За ad-hoc SQL това означава, когато сесията се прекъсва от сървъра.
Закеширана временна таблица , когато модулът се стартира за първи път, временната таблица се създава точно както за некеширания случай. В края на модула, вместо да бъде отпаднала автоматично (като обхватът, в който е създаден, завършва), временната таблица се съкращава и след това именува до шестнадесетичното представяне на идентификатора на обекта (точно както се вижда за променливата на таблицата). Следващият път, когато модулът се стартира, кешираната таблица се преименува от шестнадесетичен формат в предоставеното от потребителя име (плюс долни черти плюс идентификатор на шестнадесетичен обект).
Допълнителните операции по преименуване в началото и в края на модула включват малък брой системни промени в метаданните . Следователно кешираните временни таблици все още могат да изпитат поне някои спорове за метаданни при много високи нива на повторна употреба. Независимо от това, въздействието на метаданните от кеширана временна таблица е много по-ниско, отколкото за некеширания случай (създаване и отпадане на таблицата всеки път).
Повече подробности и примери за това как работи кеширането на временни обекти можете да намерите в предишната ми статия.
Статистика за кеширани временни таблици
Както споменахме по-рано, статистиката може да бъде автоматично създадено върху временни таблици, без да се губят предимствата на временното кеширане на обекти (като напомняне, ръчното създаване на статистически данни ще деактивиране на кеширането).
Важно предупреждение е, че встатистиката свързани с кеширана временна таблица не се нулират когато обектът е кеширан в края на модула или когато кешираният обект се извлича от кеша в началото на модула. В резултат на това статистиката за кеширана временна таблица може да бъде оставена от несвързано предварително изпълнение. С други думи, статистиката може да има абсолютно никакво отношение към текущото съдържание на временната таблица.
Това очевидно е нежелателно, като се има предвид, че основната причина да се предпочете локална временна таблица пред таблична променлива е наличието на точни статистически данни за разпространението. Като смекчаване, статистиката ще се актуализира автоматично, когато (ако) натрупаният брой промени в основния кеширан обект достигне вътрешния праг за прекомпилиране. Това е трудно да се оцени предварително, тъй като детайлите са сложни и донякъде противоинтуитивни.
Най-изчерпателното решение, като същевременно запазва предимствата на временното кеширане на обекти, е да:
- Ръчно
UPDATE STATISTICS
на временната маса в модула; и - Добавете
OPTION (RECOMPILE)
намек за изрази, които препращат към временната таблица
Естествено има разходи, свързани с това, но това най-често е приемливо. Всъщност, избирайки да използва локална временна таблица на първо място, авторът на модула имплицитно казва, че изборът на план вероятно ще бъде чувствителен към съдържанието на временната таблица, така че прекомпилирането може да има смисъл. Ръчното актуализиране на статистическите данни гарантира, че статистическите данни, използвани по време на прекомпилирането, отразяват текущото съдържание на таблицата (както със сигурност бихме очаквали).
За повече подробности как точно работи това, моля, вижте предишната ми статия по темата.
Резюме и препоръки
Временното кеширане на обекти в рамките на модул може значително да намали натиска върху споделените структури за разпределение и метаданни в tempdb база данни. Най-голямото намаление ще настъпи при използване на променливи в таблицата, тъй като кеширането и повторното използване на тези временни обекти изобщо не включва промяна на метаданни (без операции по преименуване). Все още може да се наблюдават спорове относно структурите за разпределение, ако една страница с кеширани данни е недостатъчна, за да задържи всички данни на променливата на таблицата по време на изпълнение.
Въздействието върху качеството на плана поради липсата на кардинална информация за променливите в таблицата може да бъде смекчено чрез използване на OPTION(RECOMPILE)
или флаг за проследяване 2453 (достъпен от SQL Server 2012 нататък). Имайте предвид, че тези смекчавания дават на оптимизатора само информация за общия брой редове в таблицата.
За обобщение, таблица променливи се използват най-добре, когато данните са малки (в идеалния случай се вписват в една страница с данни за максимални ползи от конкуренцията) и когато изборът на план не зависи от стойностите, присъстващи в променливата в таблицата.
Ако информация за разпределението на данни (плътност и хистограми) е важно за избора на план, използвайте локална временна таблица вместо. Уверете се, че отговаряте на условията за временно кеширане на таблицата, което най-често означава да не създавате индекси или статистически данни след първоначалния израз за създаване на таблица. Това става по-удобно от SQL Server 2014 нататък поради въвеждането на INDEX
клауза на CREATE TABLE
изявление.
Изрично UPDATE STATISTICS
след като данните се заредят във временната таблица и OPTION (RECOMPILE)
намеци за изрази, които препращат към таблицата, може да са необходими, за да се произведат всички очаквани ползи от кеширани временни таблици в рамките на модул.
Важно е да използвате временни обекти само когато те носят ясна полза, най-често по отношение на качеството на плана. Прекомерното, неефективно или ненужно използване на временни обекти може да доведе до tempdb спор, дори когато е постигнато временно кеширане на обекти.
Оптималното временно кеширане на обекти може да не е достатъчно за намаляване на tempdb спор до приемливи нива във всички случаи, дори когато временните обекти се използват само когато са напълно обосновани. Използването на променливи в таблици в паметта или на нетрайни таблици в паметта може да осигури целеви решения в такива случаи, въпреки че винаги има компромиси, които трябва да се направят, и понастоящем нито едно решение не представлява най-добрият вариант във всички случаи.