Nie wiem, czy jest to błąd, per se , ale to z pewnością interesujące zjawisko. Przebudowy partycji online są nowe w SQL Server 2014, więc mogą być pewne elementy wewnętrzne do posortowania z tym.
Oto moje najlepsze wytłumaczenie dla ciebie. Przyrostowe statystyki absolutnie wymagają, aby wszystkie partycje były próbkowane w tym samym tempie, aby podczas łączenia stron statystyk silnik miał pewność, że rozkład próbkowania jest porównywalny. REBUILD
koniecznie próbkuje dane ze 100% częstotliwością próbkowania. Nie ma gwarancji, że 100% częstotliwości próbkowania na partycji 9 zawsze będzie dokładną częstotliwością próbkowania dla pozostałych partycji. Z tego powodu wygląda na to, że silnik nie może scalić próbek i kończy się pustą kroplą statystyk. Jednak obiekt statystyk wciąż tam jest:
select
check_time = sysdatetime(),
schema_name = sh.name,
table_name = t.name,
stat_name = s.name,
index_name = i.name,
stats_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
s.stats_id,
s.has_filter,
s.is_incremental,
s.auto_created,
sp.last_updated,
sp.rows,
sp.rows_sampled,
sp.unfiltered_rows,
modification_counter
from sys.stats s
join sys.tables t
on s.object_id = t.object_id
join sys.schemas sh
on t.schema_id = sh.schema_id
left join sys.indexes i
on s.object_id = i.object_id
and s.name = i.name
outer apply sys.dm_db_stats_properties(s.object_id, s.stats_id) sp
where t.name = 'TransactionHistory' and sh.name = 'dbo'
Kroplę można wypełnić na wiele sposobów:
UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE;
lub
UPDATE STATISTICS dbo.TransactionHistory (IDX_ProductId) WITH RESAMPLE ON PARTITIONS (9);
lub możesz poczekać na aktualizację AutoStats przy pierwszej kompilacji planu zapytań przy użyciu tego obiektu:
-- look at my creative query
select *
from dbo.TransactionHistory
where TransactionDate = '20140101';
Powiedziawszy to wszystko, ten oświecający post Erin Stellato podkreśla to, co zostało uznane za poważny brak statystyk przyrostowych. Dane na poziomie partycji nie są wykorzystywane przez optymalizator do generowania planu zapytań, co zmniejsza przypuszczalną korzyść statystyki przyrostowej. Jaka jest zatem obecna korzyść ze statystyki przyrostowej? Twierdzę, że ich podstawową użytecznością jest możliwość bardziej spójnego próbkowania dużych tabel z wyższą częstotliwością niż w przypadku tradycyjnych statystyk.
Korzystając z Twojego przykładu, oto jak to wygląda:
set statistics time on;
update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;
--SQL Server Execution Times:
-- CPU time = 94 ms, elapsed time = 131 ms.
update statistics dbo.TransactionHistory(IDX_ProductId)
with resample on partitions(2);
--SQL Server Execution Times:
-- CPU time = 0 ms, elapsed time = 5 ms.
drop index IDX_ProductId On dbo.TransactionHistory;
CREATE NONCLUSTERED INDEX IDX_ProductId ON dbo.TransactionHistory (ProductId)
WITH (DATA_COMPRESSION = ROW)
ON [PRIMARY]
update statistics dbo.TransactionHistory(IDX_ProductId)
with fullscan;
--SQL Server Execution Times:
-- CPU time = 76 ms, elapsed time = 66 ms.
Aktualizacja statystyk fullscan dotycząca statystyki przyrostowej kosztuje 131 ms. Aktualizacja statystyki fullscan statystyki nieprzystosowanej do partycji kosztuje 66 ms. Nieprzystosowana statystyka jest wolniejsza, najprawdopodobniej z powodu ogólnych kosztów związanych z łączeniem poszczególnych stron statystyki z powrotem do głównego histogramu. Jednak za pomocą obiektu statystycznego wyrównanego do partycji możemy zaktualizować jedną partycję i scalić ją z powrotem w główny obiekt blob histogramu w 5 ms. W tym momencie administrator z przyrostową statystyką musi podjąć decyzję. Mogą skrócić całkowity czas utrzymywania statystyk, aktualizując tylko partycje, lub tradycyjnie musiałyby być aktualizowane, lub mogą eksperymentować z wyższymi częstotliwościami próbkowania, aby potencjalnie uzyskać więcej wierszy próbkowanych w tym samym okresie czasu, co poprzednie okresy konserwacji. Ten pierwszy pozwala odetchnąć w oknie konserwacji, ten drugi może przesunąć statystyki na bardzo dużym stole do miejsca, w którym zapytania uzyskują lepsze plany w oparciu o dokładniejsze statystyki. Nie stanowi to gwarancji, a przebieg może się różnić.
Czytelnik widzi, że 66 ms nie jest bolesnym czasem aktualizacji statystyk w tej tabeli, więc próbowałem skonfigurować test na zestawie danych wymiany stosów. W ostatnim zrzucie, który pobrałem, jest 6 418 608 postów (z wyłączeniem postów StackOverflow i wszystkich postów z 2012 r. - z mojej strony błąd danych).
Dane podzieliłem na partycje, [CreationDate]
ponieważ ... demo.
Oto niektóre czasy dla niektórych dość standardowych scenariuszy (100% - przebudowa indeksu, domyślnie - automatyczna aktualizacja statystyk lub UPDATE STATISTICS
bez określonej częstotliwości próbkowania:
- Twórz statystyki przyrostowe za pomocą Fullscan: czas procesora = 23500 ms, czas, który upłynął = 22521 ms.
- Twórz statystyki przyrostowe z Fullscan: czas procesora = 20406 ms, czas, który upłynął = 15413 ms.
- Zaktualizuj statystykę nieinkrementalną o domyślną częstotliwość próbkowania: czas procesora = 406 ms, czas, który upłynął = 408 ms.
- Zaktualizuj statystykę przyrostową z domyślną częstotliwością próbkowania: czas procesora = 453 ms, czas, który upłynął = 507 ms.
Powiedzmy, że jesteśmy bardziej wyrafinowani niż te domyślne scenariusze i zdecydowaliśmy, że 10% częstotliwość próbkowania jest minimalną częstotliwością, która powinna zapewnić nam potrzebne plany, przy jednoczesnym utrzymaniu rozsądnego czasu konserwacji.
- Zaktualizuj statystykę nieinkrementalną o próbkę 10 procent: czas procesora = 2344 ms, czas, który upłynął = 2441 ms.
- Zaktualizuj statystykę przyrostową o próbkę 10 procent: czas procesora = 2344 ms, czas, który upłynął = 2388 ms.
Jak dotąd nie ma wyraźnych korzyści z posiadania przyrostowej statystyki. Jeśli jednak wykorzystamy nieudokumentowane sys.dm_db_stats_properties_internal()
DMV (poniżej), możesz uzyskać wgląd w to, które partycje chcesz zaktualizować. Załóżmy, że wprowadziliśmy zmiany w danych na partycji 3 i chcemy mieć pewność, że statystyki będą aktualne dla zapytań przychodzących. Oto nasze opcje:
- Zaktualizuj domyślnie jako niekrementalne (także domyślne zachowanie aktualizacji Auto-Stats): 408 ms.
- Zaktualizuj wartość niekrementalną przy 10%: 2441 ms.
- Zaktualizuj statystyki przyrostowe, partycja 3 z ponownym próbkowaniem (10% - nasza zdefiniowana częstotliwość próbkowania): czas procesora = 63 ms, czas, który upłynął = 63 ms.
Oto, gdzie musimy podjąć decyzję. Czy przyjmujemy zwycięstwo 63 ms. aktualizacja statystyk oparta na partycjach, czy też zwiększamy częstotliwość próbkowania jeszcze wyżej? Powiedzmy, że jesteśmy gotowi przyjąć początkowe trafienie próbkowania na poziomie 50% na podstawie przyrostowej statystyki:
- Aktualizuj statystyki przyrostowe o 50%: upływ czasu = 16840 ms.
- Aktualizuj statystyki przyrostowe, partycja 3 z ponownym próbkowaniem (50% - nasz nowy czas aktualizacji): czas, który upłynął = 295 ms.
Jesteśmy w stanie próbkować o wiele więcej danych, być może konfigurując optymalizator, aby lepiej odgadywać nasze dane (nawet jeśli nie używa to jeszcze statystyk na poziomie partycji) i jesteśmy w stanie zrobić to szybciej, gdy już mamy statystyki przyrostowe.
Jeszcze jedna fajna rzecz do wymyślenia. Co z aktualizacjami statystyk synchronicznych? Czy 50% częstotliwość próbkowania jest zachowana nawet po uruchomieniu autostatów?
Usunąłem dane z partycji 3 i uruchomiłem zapytanie w CreationDate i sprawdziłem, a następnie sprawdziłem stawki za pomocą tego samego zapytania poniżej. Częstotliwość próbkowania 50% została zachowana.
Krótko mówiąc: statystyki przyrostowe mogą być użytecznym narzędziem przy odpowiedniej ilości przemyślenia i początkowej konfiguracji. Musisz jednak znać problem, który próbujesz rozwiązać, a następnie odpowiednio go rozwiązać. Jeśli otrzymujesz złych oszacowań liczności, to może być w stanie uzyskać lepsze plany strategiczne z częstotliwością próbkowania i niektórych zainwestowanego interwencji. Otrzymujesz jednak tylko niewielką część korzyści, ponieważ użyty histogram to pojedyncza, scalona strona statystyk, a nie informacje na poziomie partycji. Jeśli odczuwasz ból w oknie konserwacji, być może pomocne mogą być statystyki przyrostowe, ale prawdopodobnie będzie to wymagało skonfigurowania interwencji konserwacyjnej w trybie szybkiego dotyku. Bez względu,:
- Statystyki tworzone za pomocą indeksów, które nie są wyrównane do partycji z tabelą podstawową.
- Statystyki tworzone na wtórnych bazach danych AlwaysOn.
- Statystyki tworzone w bazach danych tylko do odczytu.
- Statystyki tworzone na filtrowanych indeksach.
- Statystyki tworzone na podstawie widoków.
- Statystyki tworzone na wewnętrznych tabelach.
- Statystyki tworzone za pomocą indeksów przestrzennych lub indeksów XML.
Mam nadzieję że to pomoże
select
sysdatetime(),
schema_name = sh.name,
table_name = t.name,
stat_name = s.name,
index_name = i.name,
leading_column = index_col(quotename(sh.name)+'.'+quotename(t.name),s.stats_id,1),
s.stats_id,
parition_number = isnull(sp.partition_number,1),
s.has_filter,
s.is_incremental,
s.auto_created,
sp.last_updated,
sp.rows,
sp.rows_sampled,
sp.unfiltered_rows,
modification_counter = coalesce(sp.modification_counter, n1.modification_counter)
from sys.stats s
join sys.tables t
on s.object_id = t.object_id
join sys.schemas sh
on t.schema_id = sh.schema_id
left join sys.indexes i
on s.object_id = i.object_id
and s.name = i.name
cross apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) sp
outer apply sys.dm_db_stats_properties_internal(s.object_id, s.stats_id) n1
where n1.node_id = 1
and (
(is_incremental = 0)
or
(is_incremental = 1 and sp.partition_number is not null)
)
and t.name = 'Posts'
and s.name like 'st_posts%'
order by s.stats_id,isnull(sp.partition_number,1)