Когато бях в Чикаго преди няколко седмици за едно от нашите събития за потапяне, един от присъстващите имаше въпрос със статистически данни. Няма да навлизам във всички подробности около проблема, но присъстващият спомена, че статистиката е била актуализирана с помощта на sp_updatestats
. Това е метод за актуализиране на статистически данни, който никога не съм препоръчвал; Винаги съм препоръчвал комбинация от възстановяване на индекси и UPDATE STATISTICS
за да поддържате статистиката актуална. Ако не сте запознати с sp_updatestats
, това е команда, която се изпълнява за цялата база данни за актуализиране на статистиката. Но както Кимбърли посочи на присъстващия, sp_updatestats
ще актуализира статистика, стига да има променен един ред. уау. Веднага отворих Books Online и за sp_updatestats
ще видите това:
Сега, признавам, направих предположение за това какво означава "...изисква актуализиране въз основа на информацията за rowmodctr в изгледа на каталога sys.sysindexes...". Предполагах, че решението за актуализиране ще следва същата логика, която следва опцията Auto Update Statistics, която е:
- Размерът на таблицата е нараснал от 0 до>0 реда (тест 1).
- Броят на редовете в таблицата, когато са били събрани статистическите данни, е бил 500 или по-малко, а colmodctr на водещата колона на статистическия обект се е променил с повече от 500 оттогава (тест 2).
- Таблицата имаше повече от 500 реда, когато статистическите данни са били събрани, а colmodctr на водещата колона на статистическия обект се е променил с повече от 500 + 20% от броя на редовете в таблицата, когато са били събрани статистическите данни ( тест 3).
Тази логика не се следва за sp_updatestats
. Всъщност логиката е толкова невероятно проста, че е страшна:ако един ред е променен, статистиката се актуализира. Един ред. ЕДИН РЕД. какво ме притеснява? Притеснявам се за излишните разходи за актуализиране на статистически данни за куп статистически данни, които наистина не трябва да се актуализират. Нека разгледаме по-отблизо sp_updatestats
.
Ще започнем с ново копие на базата данни AdventureWorks2012, което можете да изтеглите от Codeplex. Първо ще актуализирам редове в три различни таблици:
USE [AdventureWorks2012]; GO SET NOCOUNT ON; GO UPDATE [Production].[Product] SET [Name] = 'Bike Chain' WHERE [ProductID] = 952; UPDATE [Person].[Person] SET [LastName] = 'Cameron' WHERE [LastName] = 'Diaz'; GO INSERT INTO Sales.SalesReason (Name, ReasonType, ModifiedDate) VALUES('Stats', 'Test', GETDATE()); GO 10000
Променихме един ред в Production.Product
, 211 реда в Person.Person
и добавихме 10 000 реда към Sales.SalesReason
. Ако sp_updatestats
процедурата следва същата логика за актуализации като опцията Auto Update Statistics, след което само Sales.SalesReason
ще се актуализира, защото имаше 10 реда за начало (докато 211 реда бяха актуализирани в Person.Person
представляват около един процент от таблицата). Ако обаче се поразровим в sp_updatestats
, можем да видим, че използваната логика е различна. Обърнете внимание, че извличам изявленията само от sp_updatestats
които се използват, за да се определи каква статистика се актуализира.
Курсорът преминава през всички потребителски дефинирани таблици и вътрешни таблици в базата данни:
declare ms_crs_tnames cursor local fast_forward read_only for select name, object_id, schema_id, type from sys.objects o where o.type = 'U' or o.type = 'IT' open ms_crs_tnames fetch next from ms_crs_tnames into @table_name, @table_id, @sch_id, @table_type
Друг курсор преглежда статистическите данни за всяка таблица и изключва купища и хипотетични индекси и статистики. Имайте предвид, че sys.sysindexes
се използва в sp_helpstats
. Sysindexes
е системна таблица на SQL Server 2000 и е планирано да бъде премахната в бъдеща версия на SQL Server. Това е интересно, тъй като другият метод за определяне на актуализирани редове е sys.dm_db_stats_properties
DMF, който е наличен само в SQL 2008 R2 SP2 и SQL 2012 SP1.
set @index_names = cursor local fast_forward read_only for select name, indid, rowmodctr from sys.sysindexes where id = @table_id and indid > 0 and indexproperty(id, name, 'ishypothetical') = 0 order by indid
След малко подготовка и допълнителна логика стигаме до IF
изявление, което разкрива, че sp_updatestats
филтрира статистически данни, които не са актуализирали нито един ред... потвърждавайки, че дори само един ред да е променен, статистиката ще бъде актуализирана. Има и проверка за @is_ver_current
, което се определя от вградена вътрешна функция.
if ((@ind_rowmodctr <> 0) or ((@is_ver_current is not null) and (@is_ver_current = 0)))
Още няколко проверки, свързани с извадката и нивото на съвместимост, а след това UPDATE
оператор се изпълнява за статистиката. Преди всъщност да изпълним sp_updatestats, можем да потърсим sys.sysindexes
за да видите какви статистики ще се актуализират:
SELECT [o].[name], [si].[indid], [si].[name], [si].[rowmodctr], [si].[rowcnt], [o].[type] FROM [sys].[objects] [o] JOIN [sys].[sysindexes] [si] ON [o].[object_id] = [si].[id] WHERE ([o].[type] = 'U' OR [o].[type] = 'IT') AND [si].[indid] > 0 AND [si].[rowmodctr] <> 0 ORDER BY [o].[type] DESC, [o].[name];
В допълнение към трите таблици, които променихме, има още една статистика за потребителска таблица (dbo.DatabaseLog
) и три вътрешни статистики, които ще бъдат актуализирани:
Статистика, която ще бъде актуализирана
Ако стартираме sp_updatestats
за базата данни AdventureWorks, изходът изброява всяка таблица и актуализираната(ите) статистика(и). Изходът по-долу е променен, за да показва само актуализирани статистически данни:
Актуализиране на [sys].[fulltext_avdl_1589580701]
[clust] бе актуализиран...
1 индекс(и)/статистика(и) бяха актуализирани, 0 не изискваше актуализиране.
…
Актуализиране на [dbo].[DatabaseLog]
[PK_DatabaseLog_DatabaseLogID] бе актуализиран...
1 индекс(и)/статистика(и) бяха актуализирани, 0 не изискваше актуализиране.
…
Актуализиране на [sys].[fulltext_avdl_1077578877]
[clust] бе актуализиран...
1 индекс(и)/статистика(и) бяха актуализирани, 0 не изискваше актуализиране.
…
[Person].[Person]
[PK_Person_BusinessEntityID], актуализация не е необходима...
[IX_Person_LastName_FirstName_MiddleName] е актуализирана…
[AK_Person_rowguid], не е необходима актуализация...
1 индекс(и)/статистически(и) са актуализирани, 2 не изискват актуализиране.
…
Актуализиране на [Sales].[SalesReason]
[PK_SalesReason_SalesReasonID] бе актуализиран…
1 индекс(и)/статистика(и) бяха актуализирани, 0 не изискваше актуализиране.
…
[Продукция].[Продукт]
[PK_Product_ProductID], актуализация не е необходима…
[AK_Product_ProductNumber], актуализация не е необходима…
[AK_Product_Name] е актуализирана…
[ AK_Product_rowguid], актуализация не е необходима…
[_WA_Sys_00000013_75A278F5], актуализация не е необходима…
[_WA_Sys_00000014_75A278F5], актуализация не е необходима…
0_WA_Sys_07, не е необходима актуализация<0_005/07>[_WA_Sys_0000000C_75A278F5], актуализация не е необходима...
1 индекс(и)/статистика(и) бяха актуализирани, 7 не изискваха актуализация.
…
Статистиката за всички таблици е актуализирана.
Последният ред на изхода е малко подвеждащ – статистическите данни за всички таблици не са актуализирани, актуализирани са само статистиките, които са имали един ред или повече модифицирани. И отново, недостатъкът на това е, че може би са използвани ресурси, които не е необходимо. Ако една статистика има променен само един ред, трябва ли да се актуализира? Не. Ако има актуализирани 10 000 реда, трябва ли да се актуализира? Е, това зависи. Ако таблицата има само 5000 реда, тогава абсолютно; ако таблицата има 1 милион реда, тогава не, тъй като само един процент от таблицата е променен.
Изводът тук е, че ако използвате sp_updatestats
за да актуализирате статистиката си, най-вероятно губите ресурси, включително CPU, I/O и tempdb. Освен това е необходимо време за актуализиране на всяка статистика и ако имате ограничен прозорец за поддръжка, вероятно имате други задачи за поддръжка, които могат да се изпълнят за това време, вместо ненужни актуализации. И накрая, вероятно не предоставяте никакви ползи за производителността, като актуализирате статистическите данни, когато толкова малко редове са променени. Промяната в разпределението вероятно е незначителна, ако само малък процент от редовете са променени, така че стойностите на хистограмата и плътността не се променят толкова много. Освен това не забравяйте, че актуализирането на статистическите данни прави невалидни планове за заявки, които използват тези статистически данни. Когато тези заявки се изпълнят, плановете се генерират отново и планът вероятно ще бъде точно същият, както беше преди, тъй като нямаше значителна промяна в хистограмата. Прекомпилирането на планове за заявка има цена – не винаги е лесно да се измери, но не бива да се пренебрегва.
По-добър метод за управление на статистически данни – тъй като наистина трябва да управлявате статистиката – е да приложите планирано задание, което се актуализира въз основа на процентите на редовете, които са били променени. Можете да използвате гореспоменатата заявка, която разпитва sys.sysindexes
, или можете да използвате заявката по-долу, която се възползва от новия DMF, добавен в SQL Server 2008 R2 SP2 и SQL Server 2012 SP1:
SELECT [sch].[name] + '.' + [so].[name] AS [TableName] , [ss].[name] AS [Statistic], [sp].[last_updated] AS [StatsLastUpdated] , [sp].[rows] AS [RowsInTable] , [sp].[rows_sampled] AS [RowsSampled] , [sp].[modification_counter] AS [RowModifications] FROM [sys].[stats] [ss] JOIN [sys].[objects] [so] ON [ss].[object_id] = [so].[object_id] JOIN [sys].[schemas] [sch] ON [so].[schema_id] = [sch].[schema_id] OUTER APPLY [sys].[dm_db_stats_properties]([so].[object_id], [ss].[stats_id]) sp WHERE [so].[type] = 'U' AND [sp].[modification_counter] > 0 ORDER BY [sp].[last_updated] DESC;
Осъзнайте, че различните таблици може да имат различни прагове и ще трябва да настроите заявката по-горе за вашите бази данни. За някои таблици може да е добре да се изчака, докато 15% или 20% от редовете бъдат променени. Но за други може да се наложи да актуализирате на 10% или дори 5%, в зависимост от действителните стойности и тяхното изкривяване. Няма сребърен куршум. Колкото и да обичаме абсолютите, те рядко съществуват в SQL Server и статистиката не е изключение. Все още искате да оставите статистическите данни за автоматично актуализиране активирани – това е безопасност, която ще се задейства, ако пропуснете нещо, точно като автоматичен растеж за вашите файлове с база данни. Но най-добре е да знаете данните си и да приложите методология, която ви позволява да актуализирате статистически данни въз основа на процента на променените редове.