La razón por la que el size_in_bytes
campo del sys.dm_exec_cached_plans
DMV, al menos en términos de "Planes compilados", es mayor que el CachedPlanSize
atributo del QueryPlan
nodo en el plan XML es porque un Plan compilado no es lo mismo que un Plan de consulta. Un plan compilado se compone de múltiples objetos de memoria, cuyo tamaño combinado equivale al size_in_bytes
campo. Por lo tanto, la descripción de " Número de bytes consumidos por el objeto de caché " que encontró en la documentación es precisa; es solo que es fácil malinterpretar lo que se entiende por "objeto de caché" dado el nombre del DMV y que el término "plan" tiene múltiples significados.
Un plan compilado es un contenedor que contiene varias piezas de información relacionadas con el lote de consultas (es decir, no solo una sola declaración), una (o más) de esas piezas son los planes de consulta. Los planes compilados tienen un objeto de memoria de nivel superior de MEMOBJ_COMPILE_ADHOC, que es la fila sys.dm_os_memory_objects
que está vinculada a través del memory_object_address
campo en ambos DMV. Este objeto de memoria contiene la tabla de símbolos, colección de parámetros, enlaces a objetos relacionados, caché de acceso, caché de metadatos TDS y posiblemente algunos otros elementos. Los planes compilados se comparten entre sesiones / usuarios que están ejecutando el mismo lote con la misma configuración de sesión. Sin embargo, algunos objetos relacionados no se comparten entre Sesiones / Usuarios.
Los planes compilados también tienen uno o más objetos dependientes que se pueden encontrar pasando el plan_handle
(in sys.dm_exec_cached_plans
) al sys.dm_exec_cached_plan_dependent_objects
DMF. Hay dos tipos de objetos dependientes: Plan ejecutable (Objeto de memoria = MEMOBJ_EXECUTE ) y Cursor (Objeto de memoria = MEMOBJ_CURSOREXEC ). Habrá 0 o más objetos de cursor, uno por cada cursor. También habrá uno o más objetos del Plan ejecutable, uno por cada Usuario que ejecute ese mismo lote , por lo tanto, los Planes ejecutables no soncompartido entre los usuarios. Los planes ejecutables contienen parámetros de tiempo de ejecución e información de variables locales, estado de tiempo de ejecución como la instrucción actualmente en ejecución, identificadores de objeto para objetos creados en tiempo de ejecución (supongo que esto se refiere a variables de tabla, tablas temporales, procedimientos almacenados temporales, etc.) y posiblemente otros artículos.
Cada declaración dentro de un lote de múltiples declaraciones está contenida dentro de una Declaración Compilada (Objeto de Memoria = MEMOBJ_STATEMENT ). El tamaño de cada instrucción compilada (es decir, pages_in_bytes
dividido por 1024) debe coincidir con los CachedPlanSize="xx"
valores de los <QueryPlan>
nodos en el plan XML. Las declaraciones compiladas a menudo tendrán uno (¿posiblemente más?) Planes de consulta de tiempo de ejecución asociados (Objeto de memoria = MEMOBJ_XSTMT ). Finalmente, para cada Plan de consulta de tiempo de ejecución que sea una consulta, debe haber un Contexto de ejecución de consulta asociado (Objeto de memoria = MEMOBJ_QUERYEXECCNTXTFORSE ).
Con respecto a las declaraciones compiladas, los lotes de una sola declaración no tienen una declaración compilada separada (es decir, MEMOBJ_STATEMENT ) u objetos separados del plan de consulta en tiempo de ejecución (es decir, MEMOBJ_XSTMT ). El valor de cada uno de esos objetos se almacenará en el objeto principal del plan compilado (es decir, MEMOBJ_COMPILE_ADHOC ), y en ese caso, el pages_in_bytes
valor de ese objeto principal dividido por 1024 debe coincidir con el CachedPlanSize
tamaño en el <QueryPlan>
nodo del plan XML. Esos valores no serán iguales, sin embargo, en lotes de múltiples declaraciones.
El size_in_bytes
valor se puede obtener sumando las entradas en el sys.dm_os_memory_objects
DMV (los elementos indicados anteriormente en negrita), todos relacionados por dm_os_memory_objects.page_allocator_address
ese Plan Compilado. El truco para obtener el valor correcto es primero obtener el memory_object_address
de sys.dm_exec_cached_plans
un Plan Compilado en particular, luego usarlo para obtener la fila MEMOBJ_COMPILE_ADHOC correspondiente en sys.dm_os_memory_objects
función de su memory_object_address
campo. Luego, tome el page_allocator_address
valor de sys.dm_os_memory_objects
esa fila y úselo para tomar todas las filas sys.dm_os_memory_objects
que tengan el mismo page_allocator_address
valor. (Tenga en cuenta que esta técnica no funciona para los otros tipos de objetos en caché: árbol de análisis , proceso extendido , proceso compilado CLR y función compilada CLR.)
Usando el memory_object_address
valor obtenido de sys.dm_exec_cached_plans
, puede ver todos los componentes del Plan Compilado a través de la siguiente consulta:
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 consulta a continuación enumera todos los planes compilados sys.dm_exec_cached_plans
junto con el plan de consulta y las declaraciones para cada lote. La consulta directamente arriba se incorpora a la consulta a continuación a través de XML como el MemoryObjects
campo:
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;
Tenga en cuenta que:
- el
TotalPlanBytes
campo es solo una nueva declaración del sys.dm_exec_cached_plans.size_in_bytes
campo,
- el
AllocatedBytes
campo es la SUMA de los objetos de memoria relacionados que normalmente coinciden TotalPlanBytes
(es decir size_in_bytes
)
- el
AllocatedBytes
campo ocasionalmente será mayor que TotalPlanBytes
(es decir size_in_bytes
) debido al aumento del consumo de memoria durante la ejecución. Esto parece suceder principalmente debido a la compilación (que debería ser evidente con el usecounts
campo que se muestra 1
)
- el
BaseSingleStatementPlanKB
campo debe coincidir con el CachedPlanSize
atributo del QueryPlan
nodo en el XML, pero solo cuando se utiliza un único lote de consulta.
- para lotes con múltiples consultas, debe haber filas marcadas como
MEMOBJ_STATEMENT
en sys.dm_os_memory_objects
, una para cada consulta. El pages_in_bytes
campo para estas filas debe coincidir con los <QueryPlan>
nodos individuales del plan XML.
Recursos: