O motivo pelo qual o size_in_bytes
campo da sys.dm_exec_cached_plans
DMV, pelo menos em termos de "Planos Compilados", é maior que o CachedPlanSize
atributo do QueryPlan
nó no plano XML, porque um Plano Compilado não é a mesma coisa que um Plano de Consulta. Um plano compilado é composto por vários objetos de memória, cujo tamanho combinado equivale ao size_in_bytes
campo. Portanto, a descrição de " Número de bytes consumidos pelo objeto de cache " encontrada na documentação é precisa; é fácil interpretar mal o que se entende por "objeto de cache", devido ao nome da DMV, e que o termo "plano" tem vários significados.
Um plano compilado é um contêiner que contém várias informações relacionadas ao lote de consulta (ou seja, não apenas uma única instrução), sendo que uma (ou mais) dessas partes é o (s) plano (s) de consulta. Os planos compilados possuem um objeto de memória de nível superior de MEMOBJ_COMPILE_ADHOC, que é a linha na sys.dm_os_memory_objects
qual está vinculada por meio do memory_object_address
campo nas duas DMVs. Este objeto de memória contém a tabela de símbolos, a coleção de parâmetros, os links para objetos relacionados, o cache do acessador, o cache de metadados do TDS e, possivelmente, alguns outros itens. Os planos compilados são compartilhados entre as sessões / usuários que estão executando o mesmo lote com as mesmas configurações de sessão. No entanto, alguns objetos relacionados não são compartilhados entre sessões / usuários.
Os planos compilados também têm um ou mais objetos dependentes que podem ser encontrados passando o plan_handle
(in sys.dm_exec_cached_plans
) para o sys.dm_exec_cached_plan_dependent_objects
DMF. Existem dois tipos de objetos dependentes: Plano Executável (Objeto de Memória = MEMOBJ_EXECUTE ) e Cursor (Objeto de Memória = MEMOBJ_CURSOREXEC ). Haverá 0 ou mais objetos Cursor, um por cada cursor. Também haverá um ou mais objetos de plano executável, um por cada usuário executando o mesmo lote ; portanto, os planos executáveis não sãocompartilhado entre usuários. Os Planos Executáveis contêm informações sobre o parâmetro em tempo de execução e a variável local, estado em tempo de execução, como a instrução atualmente em execução, IDs de objetos para objetos criados em tempo de execução (presumo que isso se refira a Variáveis de Tabela, Tabelas Temporárias, Procedimentos Armazenados Temporários, etc.) e possivelmente outros itens.
Cada instrução em um lote com várias instruções está contida em uma Instrução Compilada (Objeto de Memória = MEMOBJ_STATEMENT ). O tamanho de cada Instrução Compilada (ie pages_in_bytes
) dividido por 1024 deve corresponder aos CachedPlanSize="xx"
valores dos <QueryPlan>
nós no plano XML. Instruções compiladas geralmente terão um (possivelmente mais?) Planos de consulta em tempo de execução associados (Objeto de memória = MEMOBJ_XSTMT ). Por fim, para cada Plano de Consulta em Tempo de Execução que é uma consulta, deve haver um Contexto de Execução de Consulta associado (Objeto de Memória = MEMOBJ_QUERYEXECCNTXTFORSE ).
Com relação às Instruções Compiladas, os lotes de instrução única não possuem objetos de Instruções Compiladas separadas (por exemplo, MEMOBJ_STATEMENT ) ou de Plano de Consulta em Tempo de Execução (por exemplo, MEMOBJ_XSTMT ). O valor de cada um desses objetos será armazenado no objeto principal do plano compilado (ou seja, MEMOBJ_COMPILE_ADHOC ) e, nesse caso, o pages_in_bytes
valor desse objeto principal dividido por 1024 deve corresponder ao CachedPlanSize
tamanho no <QueryPlan>
nó do plano XML. Esses valores não serão iguais, no entanto, em lotes com várias instruções.
O size_in_bytes
valor pode ser obtido somando as entradas no camposys.dm_os_memory_objects
DMV (os itens mencionados acima em negrito), todos relacionados por dm_os_memory_objects.page_allocator_address
esse plano compilado. O truque para obter o valor correto é primeiro obter a memory_object_address
partir sys.dm_exec_cached_plans
de um determinado plano compilado e depois usá-lo para obter a linha MEMOBJ_COMPILE_ADHOC correspondente com sys.dm_os_memory_objects
base em seu memory_object_address
campo. Em seguida, pegue o page_allocator_address
valor sys.dm_os_memory_objects
dessa linha e use-o para pegar todas as linhas sys.dm_os_memory_objects
que tenham o mesmo page_allocator_address
valor. (Observe que essa técnica não funciona para os outros tipos de objeto em cache: árvore de análise , processo estendido , processo compilado CLR e função compilada CLR.)
Usando o memory_object_address
valor obtido de sys.dm_exec_cached_plans
, você pode ver todos os componentes do Plano Compilado através da seguinte 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;
A consulta abaixo lista todos os planos compilados, sys.dm_exec_cached_plans
juntamente com o plano de consulta e as instruções para cada lote. A consulta diretamente acima é incorporada à consulta abaixo via XML como o 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;
Observe que:
- o
TotalPlanBytes
campo é apenas uma re-declaração do sys.dm_exec_cached_plans.size_in_bytes
campo,
- o
AllocatedBytes
campo é a SOMA dos objetos de memória relacionados que normalmente correspondem TotalPlanBytes
(ou seja size_in_bytes
)
- o
AllocatedBytes
campo ocasionalmente será maior que TotalPlanBytes
(ou seja size_in_bytes
) devido ao aumento do consumo de memória durante a execução. Isso parece acontecer principalmente devido à recompilação (o que deve ser evidente com o usecounts
campo exibido 1
)
- a
BaseSingleStatementPlanKB
campo deve corresponder ao CachedPlanSize
atributo do QueryPlan
nó no XML, mas apenas ao usar um único lote de consulta.
- para lotes com várias consultas, deve haver linhas marcadas como
MEMOBJ_STATEMENT
em sys.dm_os_memory_objects
, uma para cada consulta. O pages_in_bytes
campo para essas linhas deve corresponder aos <QueryPlan>
nós individuais do plano XML.
Recursos: