Продължавайки моята поредица от статии за ключалки, този път ще обсъдя ключалката DBCC_OBJECT_METADATA и ще покажа как то може да бъде основно пречка за проверките на последователност преди SQL Server 2016 при определени обстоятелства. Проблемът засяга DBCC CHECKDB, DBCC CHECKTABLE и DBCC CHECKFILEGROUP, но за яснота просто ще се позова на DBCC CHECKDB за останалата част от тази публикация.
Може да се чудите защо пиша за проблем, който засяга по-старите версии, но все още има огромен брой SQL Server 2014 и по-стари екземпляри, така че това е валидна тема за моята поредица.
Силно ви препоръчвам да прочетете първоначалната публикация от поредицата преди тази, за да имате всички основни основни познания за ключалките.
Какво е ключалката DBCC_OBJECT_METADATA?
За да обясня това ключалка, трябва да обясня малко как работи DBCC CHECKDB.
Сред огромния брой проверки за последователност, които DBCC CHECKDB извършва, е проверка на коректността на неклъстерираните индекси. По-конкретно, DBCC CHECKDB гарантира:
- За всеки неклъстериран индексен запис във всеки неклъстериран индекс има точно един „съвпадащ“ запис на данни в основната таблица (или купчина, или клъстериран индекс)
- За всеки запис на данни в таблица има точно един „съвпадащ“ неклъстериран индексен запис във всеки неклъстериран индекс, който е дефиниран за таблицата, като се вземат предвид филтрираните индекси
Без да навлиза твърде много в подробностите за това как се прави това, за всеки запис от данни в таблица, DBCC CHECKDB конструира всеки неклъстериран индексен запис, който трябва да съществува за всеки неклъстериран индекс и се уверява, че конструираният неклъстериран индексен запис съвпада точно с действителния неклъстериран индексен запис. Ако неклъстерираният индекс има изчислена колона в него (или като част от неклъстерирания индексен ключ, или като Включена колона), DBCC CHECKDB трябва да изчисли стойността на изчислената колона, която да използва при конструирането на индексните записи.
Както и проверките за коректност на неклъстерирания индекс, ако има постоянен изчислена колона в дефиницията на таблица, след което за всеки запис от данни в таблицата DBCC CHECKDB трябва да провери дали постоянната стойност е правилна, независимо дали тази колона е част от неклъстериран индекс или не.
И така, как разбира изчислените стойности на колоните?
Процесорът на заявки предоставя механизъм за изчисляване на стойности на изчислени колони, наречен "оценител на изрази". DBCC CHECKDB извиква тази функция, предоставяйки подходяща информация за метаданни и записа с данни, а оценителят на изрази използва съхранената дефиниция на изчислената колона в метаданните и стойностите от записа на данни и връща стойността на изчислената колона, която DBCC CHECKDB да използва . Вътрешната работа на оценителя на изрази е извън контрола на DBCC кода, но за да може да се използва оценителят на изрази, първо трябва да се получи ключалка; ключалката DBCC_OBJECT_METADATA.
Как резето се превръща в тесно място?
Тук е проблемът:има само един приемлив режим, в който ключалката DBCC_OBJECT_METADATA може да бъде получена преди използване на оценителя на изрази, и това е режим EX (изключителен). И както ще разберете от четенето на въвеждащата публикация в поредицата, само една нишка в даден момент може да задържи ключа в режим EX.
Събиране на цялата тази информация заедно:когато базата данни е запазила изчислени колони или неклъстерирани индекси, които имат изчислени колони в тях, трябва да се използва инструментът за оценка на изрази. Ако изданието на SQL Server е Enterprise, DBCC CHECKDB може да използва паралелизъм и така има множество нишки, извършващи различните проверки. И веднага щом имате множество нишки, които се опитват да придобият ключалка в режим EX, това ключалка се превръща в тесно място. Колко голямо затруднение ще стане зависи от това колко трябва да се използва оценителят на изрази, така че колкото повече постоянни изчислени колони или неклъстерирани индекси, използващи изчислени колони, има и колкото по-голям е броят на редовете на таблицата в тези таблици, толкова по-голямо е тесното място, ключът на DBCC _OBJECT_METADATA става.
Но не забравяйте, че това затруднение се случва само за версии на SQL Server по-стари от SQL Server 2016. В SQL Server 2016 Microsoft реши да „поправи“ затруднението, като изключи проверките на неклъстерирани индекси, използвайки изчислени колони по подразбиране и ги прави само когато WITH Използва се опцията EXTENDED_LOGICAL_CHECKS.
Показване на тесното място
Можете лесно да възпроизведете затруднението за себе си, като стартирате DBCC CHECKDB в база данни, която има или постоянни изчислени колони, или неклъстерни индекси с изчислени колони, а предоставената от Microsoft база данни AdventureWorks е чудесен пример. Можете да изтеглите резервни копия на AdventureWorks за вашата версия на SQL Server от тук. Проведох някои тестове с помощта на база данни AdventureWorks2014 на екземпляр на SQL Server 2014 (на 32-ядрен Dell R720) и разширих базата данни до няколкостотин GB, използвайки скриптовете на Джонатан.
Когато стартирах DBCC CHECKDB, със сървър MAXDOP, зададен на 0, отне повече от 5 часа за изпълнение. Типът на изчакване LATCH_EX представлява около 30% от изчакванията, като всяко изчакване е само по-малко от 1 милисекунда, а 99% от изчакванията LATCH_EX са за ключалката DBCC_OBJECT_METADATA.
Потърсих неклъстерирани индекси, съдържащи изчислени колони, използвайки следния код:
SELECT [s].[name] AS [Схема], [o].[name] AS [Object], [i].[name] AS [Index], [c].[name] AS [Колона] ], [ic].* ОТ sys.columns [c] JOIN sys.index_columns [ic] ON [ic].[object_id] =[c].[object_id] AND [ic].[column_id] =[c]. [column_id] JOIN sys.indexes [i] ON [i].[object_id] =[ic].[object_id] AND [i].[index_id] =[ic].[index_id] JOIN sys.objects [o] ON ON [i].[object_id] =[o].[object_id] JOIN sys.schemas [s] ON [o].[schema_id] =[s].[schema_id] WHERE [c].[is_computed] =1;предварително>Този код намери шест неклъстерирани индекса в базата данни на AdventureWorks2014. Деактивирах всичките шест индекса (използвайки ALTER INDEX ... DISABLE) и стартирах отново DBCC CHECKDB и завърши за около 18 минути. Така че DBCC_OBJECT_METADATA тесното място на ключалката беше основен фактор, който кара DBCC CHECKDB да работи повече от 16 пъти по-бавно!
Резюме
За съжаление, деактивирането на неклъстерирани индекси с помощта на изчислени колони (и след това повторното им активиране с помощта на ALTER INDEX ... REBUILD) е *единственият* начин за премахване на тесното място на ключалката DBCC_OBJECT_METADATA във версии преди SQL Server 2016, като същевременно запазва цялата друга функционалност на DBCC CHECKDB. Деактивирането на неклъстерирани индекси вероятно не е нещо, което ще искате да правите в производствена среда, освен ако нямате прозорец за поддръжка с нулева активност. Това означава, че вероятно ще деактивирате тези неклъстерирани индекси, за да премахнете пречката, ако вашите проверки за последователност се прехвърлят на друг сървър чрез метода backup-copy-restore-CHECKDB.
Друг начин да го направите е да използвате опцията WITH PHYSICAL_ONLY, когато изпълнявате DBCC CHECKDB, но тогава пропускате всички задълбочени логически проверки, така че не съм голям фен да препоръчвам това като решение.