Powodem, dla którego size_in_bytespole sys.dm_exec_cached_plansDMV, przynajmniej w kategoriach „planów skompilowanych”, jest większe niż CachedPlanSizeatrybut QueryPlanwęzła w planie XML, jest to, że plan skompilowany nie jest tym samym, co plan zapytań. Skompilowany plan składa się z wielu obiektów pamięci, których łączny rozmiar odpowiada size_in_bytespolu. Tak więc opis „ liczby bajtów zajętych przez obiekt pamięci podręcznej ” znaleziony w dokumentacji jest dokładny; po prostu łatwo jest źle zinterpretować znaczenie „obiektu pamięci podręcznej”, biorąc pod uwagę nazwę DMV, i że termin „plan” ma wiele znaczeń.
Plan skompilowany jest kontenerem, który przechowuje różne informacje związane z partią zapytań (tj. Nie tylko pojedynczą instrukcję), przy czym jeden (lub więcej) z tych fragmentów jest planem (planami) zapytania. Skompilowane plany zawierają obiekt pamięci najwyższego poziomu MEMOBJ_COMPILE_ADHOC, który jest wierszem sys.dm_os_memory_objectspołączonym przez memory_object_addresspole w obu DMV. Ten obiekt pamięci zawiera tablicę symboli, kolekcję parametrów, łącza do powiązanych obiektów, pamięć podręczną akcesorium, pamięć podręczną metadanych TDS i ewentualnie inne elementy. Skompilowane plany są udostępniane między sesjami / użytkownikami, którzy wykonują tę samą partię z tymi samymi ustawieniami sesji. Jednak niektóre powiązane obiekty nie są udostępniane między sesjami / użytkownikami.
Plany skompilowane zawierają również jeden lub więcej zależnych obiektów, które można znaleźć, przekazując plan_handle(in sys.dm_exec_cached_plans) do sys.dm_exec_cached_plan_dependent_objectsDMF. Istnieją dwa typy obiektów zależnych: Plan wykonywalny (Obiekt pamięci = MEMOBJ_EXECUTE ) i Kursor (Obiekt pamięci = MEMOBJ_CURSOREXEC ). Będzie 0 lub więcej obiektów Kursorów, po jednym na każdy kursor. Będzie także jeden lub więcej obiektów Planu wykonywalnego, po jednym dla każdego użytkownika wykonującego tę samą partię , dlatego Plany wykonywalne nie sąwspółdzielone między użytkownikami. Plany wykonywalne zawierają parametry wykonawcze i informacje o zmiennych lokalnych, stan wykonawczy, taki jak aktualnie wykonywana instrukcja, identyfikatory obiektów dla obiektów utworzonych w czasie wykonywania (zakładam, że odnosi się to do zmiennych tabel, tabel tymczasowych, tymczasowych procedur przechowywanych itp.) i ewentualnie inne przedmioty.
Każda instrukcja w partii zawierającej wiele instrukcji jest zawarta w instrukcji skompilowanej (Obiekt pamięci = MEMOBJ_STATEMENT ). Rozmiar każdej instrukcji skompilowanej (tj. pages_in_bytes) Podzielonej przez 1024 powinien być zgodny z CachedPlanSize="xx"wartościami <QueryPlan>węzłów w planie XML. Skompilowane instrukcje często mają jeden (prawdopodobnie więcej?) Powiązany plan zapytań środowiska wykonawczego (Obiekt pamięci = MEMOBJ_XSTMT ). Wreszcie dla każdego planu zapytań środowiska wykonawczego, który jest zapytaniem, powinien istnieć powiązany kontekst wykonywania zapytania (Obiekt pamięci = MEMOBJ_QUERYEXECCNTXTFORSE ).
W odniesieniu do instrukcji skompilowanych, partie pojedynczych instrukcji nie mają osobnych instrukcji skompilowanych (tj. MEMOBJ_STATEMENT ) ani oddzielnych obiektów planu zapytań środowiska wykonawczego (tj. MEMOBJ_XSTMT ). Wartość każdego z tych obiektów będzie przechowywana w głównym obiekcie Planu Kompilowanego (tj. MEMOBJ_COMPILE_ADHOC ), w takim przypadku pages_in_byteswartość tego głównego obiektu podzielona przez 1024 powinna być zgodna z CachedPlanSizerozmiarem w <QueryPlan>węźle planu XML. Wartości te nie będą jednak równe w partiach zawierających wiele instrukcji.
size_in_bytesWartość można uzyskać poprzez zsumowanie wpisy w sys.dm_os_memory_objectsDMV (pozycje wspomniano powyżej pogrubione), przez wszystkie związane dm_os_memory_objects.page_allocator_addressz tym skompilowany planu. Sposób na uzyskanie poprawnej wartości polega na tym, aby najpierw uzyskać memory_object_addressod sys.dm_exec_cached_plansokreślonego Planu Skompilowanego, a następnie użyć go, aby uzyskać odpowiedni wiersz MEMOBJ_COMPILE_ADHOCsys.dm_os_memory_objects na podstawie jego memory_object_addresspola. Następnie pobierz page_allocator_addresswartość z sys.dm_os_memory_objectstego wiersza i użyj go, aby pobrać wszystkie wiersze sys.dm_os_memory_objectso tej samej page_allocator_addresswartości. (Należy pamiętać, że ta technika nie działa w przypadku innych typów obiektów buforowanych: parsowania drzewa , rozszerzonego przetwarzania , skompilowanego przetwarzania CLR i obsługiwanego działania CLR.)
Wykorzystując memory_object_addresswartość uzyskaną zsys.dm_exec_cached_plans , możesz zobaczyć wszystkie składniki Planu Kompilowanego za pomocą następującego zapytania:
DECLARE @CompiledPlanAddress VARBINARY(8) = 0x00000001DC4A4060;
SELECT obj.memory_object_address, obj.pages_in_bytes, obj.type
FROM sys.dm_os_memory_objects obj
WHERE obj.page_allocator_address = (
SELECT planobj.page_allocator_address
FROM sys.dm_os_memory_objects planobj
WHERE planobj.memory_object_address = @CompiledPlanAddress
)
ORDER BY obj.[type], obj.pages_in_bytes;
Poniższe zapytanie zawiera listę wszystkich skompilowanych planów sys.dm_exec_cached_planswraz z planem zapytań i instrukcjami dla każdej partii. Zapytanie bezpośrednio powyżej jest włączone do zapytania poniżej poprzez XML jako MemoryObjectspole:
SELECT cplan.bucketid,
cplan.pool_id,
cplan.refcounts,
cplan.usecounts,
cplan.size_in_bytes,
cplan.memory_object_address,
cplan.cacheobjtype,
cplan.objtype,
cplan.plan_handle,
'---' AS [---],
qrypln.[query_plan],
sqltxt.[text],
'---' AS [---],
planobj.pages_in_bytes,
planobj.pages_in_bytes / 1024 AS [BaseSingleStatementPlanKB],
'===' AS [===],
cplan.size_in_bytes AS [TotalPlanBytes],
bytes.AllocatedBytes,
(SELECT CONVERT(VARCHAR(30), obj.memory_object_address, 1)
AS [memory_object_address], obj.pages_in_bytes, obj.[type]
--,obj.page_size_in_bytes
FROM sys.dm_os_memory_objects obj
WHERE obj.page_allocator_address = planobj.page_allocator_address
FOR XML RAW(N'object'), ROOT(N'memory_objects'), TYPE) AS [MemoryObjects]
FROM sys.dm_exec_cached_plans cplan
OUTER APPLY sys.dm_exec_sql_text(cplan.[plan_handle]) sqltxt
OUTER APPLY sys.dm_exec_query_plan(cplan.[plan_handle]) qrypln
INNER JOIN sys.dm_os_memory_objects planobj
ON planobj.memory_object_address = cplan.memory_object_address
OUTER APPLY (SELECT SUM(domo.[pages_in_bytes]) AS [AllocatedBytes]
FROM sys.dm_os_memory_objects domo
WHERE domo.page_allocator_address = planobj.page_allocator_address) bytes
WHERE cplan.parent_plan_handle IS NULL
AND cplan.cacheobjtype IN (N'Compiled Plan', N'Compiled Plan Stub')
--AND cplan.plan_handle = 0x06000D0031CD572910529CE001000000xxxxxxxx
ORDER BY cplan.objtype, cplan.plan_handle;
Proszę to zanotować:
TotalPlanBytespole jest tylko ponowne oświadczenie o sys.dm_exec_cached_plans.size_in_byteszakresie,
AllocatedBytespole jest sumą powiązanych obiektów pamięci, które zwykle jest dopasowany TotalPlanBytes(tj size_in_bytes)
AllocatedBytespole będzie od czasu do czasu być większa niż TotalPlanBytes(tjsize_in_bytes ) w wyniku zużycia pamięci rośnie w realizacji. Wydaje się, że dzieje się tak głównie z powodu ponownej kompilacji (co powinno być widoczne w usecountspolu pokazującym 1)
BaseSingleStatementPlanKBpola powinny pasować do CachedPlanSizeatrybutu QueryPlanwęzła w XML, ale tylko przy użyciu pojedynczej partii zapytania.
- dla partii z wieloma zapytaniami powinny być zaznaczone wiersze jako
MEMOBJ_STATEMENTin sys.dm_os_memory_objects, po jednym dla każdego zapytania. pages_in_bytesPole dla tych wierszach powinny odpowiadać poszczególne<QueryPlan> węzły planu XML.
Zasoby: