La raison pour laquelle le size_in_byteschamp du sys.dm_exec_cached_plansDMV, au moins en termes de "plans compilés", est plus grand que l' CachedPlanSizeattribut du QueryPlannœ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_byteschamp. 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_objectsqui est liée via le memory_object_addresschamp 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_objectsDMF. 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_bytesvaleur de cet objet principal divisée par 1024 devrait correspondre à la CachedPlanSizetaille du <QueryPlan>nœud du plan XML. Cependant, ces valeurs ne seront pas égales dans les lots à instructions multiples.
La size_in_bytesvaleur peut être dérivée en additionnant les entrées dans le sys.dm_os_memory_objectsDMV (les éléments indiqués ci-dessus en gras), tous liés par dm_os_memory_objects.page_allocator_addresspour ce plan compilé. L'astuce pour obtenir la valeur correcte consiste à obtenir d'abord le à memory_object_addresspartir sys.dm_exec_cached_plansd'un plan compilé particulier, puis à l'utiliser pour obtenir la ligne MEMOBJ_COMPILE_ADHOC correspondante en sys.dm_os_memory_objectsfonction de son memory_object_addresschamp. Ensuite, récupérez la page_allocator_addressvaleur de sys.dm_os_memory_objectspour cette ligne et utilisez-la pour extraire toutes les lignes sys.dm_os_memory_objectsqui ont la même page_allocator_addressvaleur. (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_addressvaleur 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_plansainsi 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 MemoryObjectschamp:
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
TotalPlanByteschamp est juste une ré-déclaration du sys.dm_exec_cached_plans.size_in_byteschamp,
- le
AllocatedByteschamp est la somme des objets de mémoire associés qui correspondent généralement TotalPlanBytes(c.-à-d. size_in_bytes)
- le
AllocatedByteschamp 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 usecountschamp affiché 1)
- le
BaseSingleStatementPlanKBchamp doit correspondre à l' CachedPlanSizeattribut du QueryPlannœ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_STATEMENTdans sys.dm_os_memory_objects, une pour chaque requête. Le pages_in_byteschamp de ces lignes doit correspondre aux <QueryPlan>nœuds individuels du plan XML.
Ressources: