Na serwerze z 32 GB działamy SQL Server 2014 SP2 z maksymalną pamięcią 25 GB mamy dwie tabele, tutaj znajdziesz uproszczoną strukturę obu tabel:
CREATE TABLE [dbo].[Settings](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceId] [int] NULL,
[typeID] [int] NULL,
[remark] [varchar](max) NULL,
CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Resources](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceUID] [int] NULL,
CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
z następującymi indeksami nieklastrowanymi:
CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
[resourceUID] ASC
)
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
[resourceId] ASC,
[typeID] ASC
)
Baza danych jest skonfigurowana na compatibility level
120.
Kiedy uruchamiam to zapytanie, pojawiają się wycieki tempdb
. Oto jak wykonuję zapytanie:
exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Jeśli nie wybierzesz [remark]
pola, nie nastąpi wyciek. Moja pierwsza reakcja była taka, że wycieki wystąpiły z powodu niskiej liczby szacowanych wierszy operatora pętli zagnieżdżonej.
Tak więc dodaję 5 kolumn datetime i 5 liczb całkowitych do tabeli ustawień i dodaję je do mojej instrukcji select. Kiedy wykonuję zapytanie, nie dochodzi do wycieków.
Dlaczego wycieki zdarzają się tylko po [remark]
wybraniu? Prawdopodobnie ma to coś wspólnego z faktem, że jest to varchar(max)
. Co mogę zrobić, aby uniknąć rozlania tempdb
?
Dodanie OPTION (RECOMPILE)
do zapytania nie ma znaczenia.
źródło
select r.id, LEFT(remark, 512)
(lub dowolną rozsądną długość podciągu).Odpowiedzi:
Będzie kilka możliwych obejść tego problemu.
Możesz ręcznie dostosować przyznanie pamięci, choć prawdopodobnie nie wybrałbym tej trasy.
Możesz także użyć CTE i TOP, aby przesunąć sortowanie niżej, zanim przejmiesz kolumnę o maksymalnej długości. Będzie to wyglądać jak poniżej.
Proof-of-concept dbfiddle tutaj . Przykładowe dane nadal będą mile widziane!
Jeśli chcesz przeczytać doskonałą analizę Paula White'a, przeczytaj tutaj.
źródło
Wyciek ma miejsce, gdy dodasz tę kolumnę, ponieważ nie dostaniesz wystarczającej wielkości pamięci na sortowanie danych o dużych ciągach.
Nie dostajesz dostatecznie dużego przydziału pamięci, ponieważ faktyczna liczba wierszy jest 10 razy większa niż szacowana liczba wierszy (1302 rzeczywistych vs 126 oszacowanych).
Dlaczego wycena jest wyłączona? Dlaczego SQL Server uważa, że w dbo jest tylko jeden wiersz. Ustawienia z liczbą
resourceid
38?Może to być problem ze statystykami, który można sprawdzić, uruchamiając
DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')
i wyświetlając liczby dla tego kroku histogramu. Ale plan wykonania wydaje się wskazywać, że statystyki są tak kompletne i aktualne, jak to tylko możliwe.Ponieważ statystyki nie pomagają, najlepszym rozwiązaniem jest prawdopodobnie przepisanie zapytania - które Forrest ujął w swojej odpowiedzi.
źródło
Wydaje mi się, że
where
klauzula w zapytaniu podaje problem i jest przyczyną niskich oszacowań, nawet jeśliOPTION(RECOMPILE)
jest używana.Stworzyłem trochę danych testowych, a na koniec wymyśliłem dwa rozwiązania, przechowując
ID
pole zresources
zmiennej (jeśli zawsze jest unikalna) lub tabeli tymczasowej, jeśli możemy mieć więcej niż jednąID
.Podstawowe zapisy testów
Wstaw wartości „Szukaj”, aby uzyskać ten sam przybliżony zestaw wyników, co OP (1300 rekordów)
Zmień statystyki kompatybilności i aktualizacji, aby dopasować OP
Oryginalne zapytanie
Moje szacunki są jeszcze gorsze , z jednym szacowanym wierszem, a zwracanych jest 1300. I jak stwierdził OP, nie ma znaczenia, czy dodam
OPTION(RECOMPILE)
Ważną rzeczą do zapamiętania jest to, że kiedy pozbyjemy się klauzuli where, szacunki są w 100% poprawne, co jest oczekiwane, ponieważ używamy wszystkich danych w obu tabelach.
Zmusiłem indeksy tylko po to, aby upewnić się, że używamy tych samych, co w poprzednim zapytaniu, aby to udowodnić
Zgodnie z oczekiwaniami dobre szacunki.
Co więc możemy zmienić, aby uzyskać lepsze szacunki, ale nadal szukać naszych wartości?
JEŻELI @UID jest unikalny, tak jak w przykładzie podanym przez OP, możemy umieścić singiel,
id
który został zwróconyresources
w zmiennej, a następnie wyszukać tę zmienną z OPCJĄ (RECOMPILE)Co daje 100% dokładne szacunki
Ale co, jeśli w zasobach znajduje się wiele zasobów UID?
dodaj trochę danych testowych
Można to rozwiązać za pomocą tabeli tymczasowej
Ponownie z dokładnymi szacunkami .
Dokonano tego z moim własnym zestawem danych, YMMV.
Napisane za pomocą sp_executesql
Ze zmienną
Ze stołem tymczasowym
Wciąż 100% poprawne szacunki w moim teście
źródło