La raison pour laquelle le size_in_bytes
champ du sys.dm_exec_cached_plans
DMV, au moins en termes de "plans compilés", est plus grand que l' CachedPlanSize
attribut du QueryPlan
nœud dans le plan XML, car un plan compilé n'est pas la même chose qu'un plan de requête. Un plan compilé est composé de plusieurs objets mémoire, dont la taille combinée équivaut au size_in_bytes
champ. Ainsi, la description de " Nombre d'octets consommés par l'objet cache » que vous avez trouvée dans la documentation est exacte; c'est juste qu'il est facile de mal interpréter ce que l'on entend par "objet cache" étant donné le nom du DMV et que le terme "plan" a plusieurs significations.
Un plan compilé est un conteneur qui contient divers éléments d'information liés au lot de requêtes (c'est-à-dire pas seulement une seule instruction), un (ou plusieurs) de ces éléments étant le ou les plans de requête. Les plans compilés ont un objet mémoire de niveau supérieur de MEMOBJ_COMPILE_ADHOC qui est la ligne sys.dm_os_memory_objects
qui est liée via le memory_object_address
champ dans les deux DMV. Cet objet mémoire contient la table des symboles, la collection de paramètres, les liens vers les objets associés, le cache d'accesseur, le cache de métadonnées TDS et éventuellement d'autres éléments. Les plans compilés sont partagés entre les sessions / utilisateurs qui exécutent le même lot avec les mêmes paramètres de session. Cependant, certains objets associés ne sont pas partagés entre les sessions / utilisateurs.
Les plans compilés ont également un ou plusieurs objets dépendants qui peuvent être trouvés en passant le plan_handle
(in sys.dm_exec_cached_plans
) dans le sys.dm_exec_cached_plan_dependent_objects
DMF. Il existe deux types d'objets dépendants: le plan exécutable (objet mémoire = MEMOBJ_EXECUTE ) et le curseur (objet mémoire = MEMOBJ_CURSOREXEC ). Il y aura 0 ou plusieurs objets Cursor, un pour chaque curseur. Il y aura également un ou plusieurs objets de plan exécutable, un pour chaque utilisateur exécutant ce même lot , donc les plans exécutables ne sont paspartagé entre les utilisateurs. Les plans exécutables contiennent des paramètres d'exécution et des informations sur les variables locales, l'état d'exécution tel que l'instruction en cours d'exécution, les ID d'objet pour les objets créés au moment de l'exécution (je suppose que cela fait référence aux variables de table, aux tables temporaires, aux procédures stockées temporaires, etc.) , et éventuellement d'autres éléments.
Chaque instruction d' un lot à instructions multiples est contenue dans une instruction compilée (objet mémoire = MEMOBJ_STATEMENT ). La taille de chaque instruction compilée (c.-à-d. pages_in_bytes
) Divisée par 1024 doit correspondre aux CachedPlanSize="xx"
valeurs des <QueryPlan>
nœuds dans le plan XML. Les instructions compilées ont souvent un (éventuellement plus?) Plans d'exécution de requête associés (objet mémoire = MEMOBJ_XSTMT ). Enfin, pour chaque plan de requête d'exécution qui est une requête, il doit y avoir un contexte d'exécution de requête associé (objet mémoire = MEMOBJ_QUERYEXECCNTXTFORSE ).
En ce qui concerne les instructions compilées, les lots d'instructions uniques n'ont pas d' objets d' instructions compilées distinctes (par exemple MEMOBJ_STATEMENT ) ni de plan de requête d'exécution distinct (par exemple MEMOBJ_XSTMT ). La valeur de chacun de ces objets sera stockée dans l'objet principal du plan compilé (c'est-à-dire MEMOBJ_COMPILE_ADHOC ), et dans ce cas, la pages_in_bytes
valeur de cet objet principal divisée par 1024 devrait correspondre à la CachedPlanSize
taille du <QueryPlan>
nœud du plan XML. Cependant, ces valeurs ne seront pas égales dans les lots à instructions multiples.
La size_in_bytes
valeur peut être dérivée en additionnant les entrées dans le sys.dm_os_memory_objects
DMV (les éléments indiqués ci-dessus en gras), tous liés par dm_os_memory_objects.page_allocator_address
pour ce plan compilé. L'astuce pour obtenir la valeur correcte consiste à obtenir d'abord le à memory_object_address
partir sys.dm_exec_cached_plans
d'un plan compilé particulier, puis à l'utiliser pour obtenir la ligne MEMOBJ_COMPILE_ADHOC correspondante en sys.dm_os_memory_objects
fonction de son memory_object_address
champ. Ensuite, récupérez la page_allocator_address
valeur de sys.dm_os_memory_objects
pour cette ligne et utilisez-la pour extraire toutes les lignes sys.dm_os_memory_objects
qui ont la même page_allocator_address
valeur. (Veuillez noter que cette technique ne fonctionne pas pour les autres types d'objets mis en cache: arbre d'analyse , proc étendu , CLR compilé Proc et CLR compilé Func.)
À l'aide de la memory_object_address
valeur obtenue à partir de sys.dm_exec_cached_plans
, vous pouvez voir tous les composants du plan compilé via la requête suivante:
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;
La requête ci-dessous répertorie tous les plans compilés sys.dm_exec_cached_plans
ainsi que le plan de requête et les instructions pour chaque lot. La requête directement ci-dessus est incorporée dans la requête ci-dessous via XML comme MemoryObjects
champ:
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;
Veuillez noter que:
- le
TotalPlanBytes
champ est juste une ré-déclaration du sys.dm_exec_cached_plans.size_in_bytes
champ,
- le
AllocatedBytes
champ est la somme des objets de mémoire associés qui correspondent généralement TotalPlanBytes
(c.-à-d. size_in_bytes
)
- le
AllocatedBytes
champ sera parfois supérieur à TotalPlanBytes
(ie size_in_bytes
) en raison de l'augmentation de la consommation de mémoire pendant l'exécution. Cela semble se produire principalement en raison de la recompilation (ce qui devrait être évident avec le usecounts
champ affiché 1
)
- le
BaseSingleStatementPlanKB
champ doit correspondre à l' CachedPlanSize
attribut du QueryPlan
nœud dans le XML, mais uniquement lors de l'utilisation d'un seul lot de requêtes.
- pour les lots comportant plusieurs requêtes, il doit y avoir des lignes marquées comme
MEMOBJ_STATEMENT
dans sys.dm_os_memory_objects
, une pour chaque requête. Le pages_in_bytes
champ de ces lignes doit correspondre aux <QueryPlan>
nœuds individuels du plan XML.
Ressources: