Der Grund dafür, dass das size_in_bytes
Feld der sys.dm_exec_cached_plans
DMV, zumindest in Bezug auf "Kompilierte Pläne", größer ist als das CachedPlanSize
Attribut des QueryPlan
Knotens im XML-Plan, ist, dass ein kompilierter Plan nicht dasselbe ist wie ein Abfrageplan. Ein kompilierter Plan besteht aus mehreren Speicherobjekten, deren kombinierte Größe dem size_in_bytes
Feld entspricht. Daher ist die in der Dokumentation angegebene Beschreibung der " Anzahl der vom Cache-Objekt verbrauchten Bytes " korrekt. es ist nur so, dass es leicht zu Fehlinterpretationen kommt, was unter "Cache-Objekt" mit dem Namen der DMV zu verstehen ist und dass der Begriff "Plan" mehrere Bedeutungen hat.
Ein kompilierte Plan ist ein Container, die verschiedenen Informationen im Zusammenhang mit der Abfrage hält Batch (also nicht nur eine einzelne Anweisung), ein (oder mehr) diese Stücke sind , um den Abfrage - Plan (s). Kompilierte Pläne haben das oberste Speicherobjekt MEMOBJ_COMPILE_ADHOC. Dies ist die Zeile sys.dm_os_memory_objects
, die über das memory_object_address
Feld in beiden DMVs verknüpft ist. Dieses Speicherobjekt enthält die Symboltabelle, die Parameterauflistung, Links zu verwandten Objekten, den Accessor-Cache, den TDS-Metadaten-Cache und möglicherweise einige andere Elemente. Kompilierte Pläne werden für Sitzungen / Benutzer freigegeben, die denselben Stapel mit denselben Sitzungseinstellungen ausführen . Einige verwandte Objekte werden jedoch nicht von Sitzungen / Benutzern gemeinsam genutzt.
Kompilierte Pläne enthalten auch ein oder mehrere abhängige Objekte, die durch Übergeben des plan_handle
( Eingangs sys.dm_exec_cached_plans
) an den sys.dm_exec_cached_plan_dependent_objects
DMF gefunden werden können. Es gibt zwei Arten von abhängigen Objekten: Ausführbarer Plan (Speicherobjekt = MEMOBJ_EXECUTE ) und Cursor (Speicherobjekt = MEMOBJ_CURSOREXEC ). Es gibt 0 oder mehr Cursorobjekte, eines pro Cursor. Es gibt auch ein oder mehrere ausführbare Plan-Objekte, eines pro Benutzer, der denselben Stapel ausführt , daher sind ausführbare Pläne nicht vorhandenzwischen Benutzern geteilt. Ausführbare Pläne enthalten Laufzeitparameter und Informationen zu lokalen Variablen, Laufzeitstatus wie die aktuell ausgeführte Anweisung, Objekt-IDs für zur Laufzeit erstellte Objekte (ich nehme an, dies bezieht sich auf Tabellenvariablen, temporäre Tabellen, temporäre gespeicherte Prozeduren usw.). und möglicherweise andere Gegenstände.
Jede Anweisung in einem Stapel mit mehreren Anweisungen ist in einer kompilierten Anweisung enthalten (Memory Object = MEMOBJ_STATEMENT ). Die Größe jeder kompilierten Anweisung (dh pages_in_bytes
geteilt durch 1024) sollte mit den CachedPlanSize="xx"
Werten der <QueryPlan>
Knoten im XML-Plan übereinstimmen . Kompilierte Anweisungen haben häufig einen (möglicherweise mehrere?) Zugeordneten Laufzeit- Abfrageplan (Speicherobjekt = MEMOBJ_XSTMT ). Schließlich sollte für jeden Laufzeit-Abfrageplan, bei dem es sich um eine Abfrage handelt, ein zugehöriger Abfrageausführungskontext vorhanden sein (Speicherobjekt = MEMOBJ_QUERYEXECCNTXTFORSE ).
In Bezug auf kompilierte Anweisungen verfügen Stapel mit einzelnen Anweisungen nicht über separate kompilierte Anweisungen (dh MEMOBJ_STATEMENT ) oder separate Laufzeit- Abfrageplanobjekte (dh MEMOBJ_XSTMT ). Der Wert für jedes dieser Objekte wird im kompilierten Hauptplanobjekt (dh MEMOBJ_COMPILE_ADHOC ) gespeichert. In diesem Fall sollte der pages_in_bytes
Wert für dieses Hauptobjekt geteilt durch 1024 mit der CachedPlanSize
Größe im <QueryPlan>
Knoten des XML-Plans übereinstimmen . Diese Werte stimmen jedoch nicht mit Batches mit mehreren Anweisungen überein.
Der size_in_bytes
Wert kann abgeleitet werden, indem die Einträge in der sys.dm_os_memory_objects
DMV (die oben fettgedruckten Elemente) summiert werden , die sich alle dm_os_memory_objects.page_allocator_address
auf den kompilierten Plan beziehen . Der Trick , um den richtigen Wert zu erhalten ist, zunächst die bekommen memory_object_address
von sys.dm_exec_cached_plans
für einen bestimmten kompilierten Plan verwendet dann , dass die entsprechende bekommen MEMOBJ_COMPILE_ADHOC Reihe von sys.dm_os_memory_objects
basierend auf seinem memory_object_address
Feld. Nehmen Sie dann den page_allocator_address
Wert sys.dm_os_memory_objects
für diese Zeile und verwenden Sie ihn, um alle Zeilen sys.dm_os_memory_objects
mit demselben page_allocator_address
Wert zu erfassen . (Bitte beachten Sie, dass diese Technik für die anderen zwischengespeicherten Objekttypen nicht funktioniert: Parse Tree , Extended Proc , CLR Compiled Proc und CLR Compiled Func.)
Wenn Sie den memory_object_address
Wert von verwenden sys.dm_exec_cached_plans
, können Sie alle Komponenten des kompilierten Plans über die folgende Abfrage anzeigen:
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;
In der folgenden Abfrage werden alle kompilierten Pläne sys.dm_exec_cached_plans
zusammen mit dem Abfrageplan und den Anweisungen für jeden Stapel aufgelistet . Die Abfrage direkt darüber wird über XML als MemoryObjects
Feld in die folgende Abfrage eingefügt :
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;
Bitte beachte, dass:
- das
TotalPlanBytes
Feld ist nur eine Neuaussage des sys.dm_exec_cached_plans.size_in_bytes
Feldes,
- das
AllocatedBytes
Feld ist die SUMME der zugehörigen Speicherobjekte, die normalerweise übereinstimmen TotalPlanBytes
(dh size_in_bytes
)
- Das
AllocatedBytes
Feld ist gelegentlich größer als TotalPlanBytes
(dh size_in_bytes
), da der Speicherverbrauch während der Ausführung steigt. Dies scheint hauptsächlich auf eine Neukompilierung zurückzuführen zu sein (was anhand des angezeigten usecounts
Feldes ersichtlich sein sollte 1
).
- Das
BaseSingleStatementPlanKB
Feld sollte mit dem CachedPlanSize
Attribut des QueryPlan
Knotens in der XML übereinstimmen , jedoch nur bei Verwendung eines einzelnen Abfragestapels.
- Bei Stapeln mit mehreren Abfragen sollten Zeilen mit der Kennzeichnung "
MEMOBJ_STATEMENT
in" sys.dm_os_memory_objects
für jede Abfrage vorhanden sein. Das pages_in_bytes
Feld für diese Zeilen sollte mit den einzelnen <QueryPlan>
Knoten des XML-Plans übereinstimmen .
Ressourcen: