Planejar o tamanho do cache e a memória reservada

18

Ao executar uma consulta, incluindo o Plano de Execução Real, o operador raiz ( SELECT) informa que o Tamanho do Plano em Cache é de 32 KB.

Uma consulta que se junta sys.dm_exec_cached_planse sys.dm_os_memory_objects, observando o plano em questão, diz que os valores para pages_in_bytese max_pages_in_bytessão 32768 (32 KB), que correspondem ao tamanho do plano em cache.

O que eu não entendo é qual é o valor sys.dm_exec_cached_plans.size_in_bytes, que é 49152 (48 KB). Eu li BOL em todas essas colunas, e especialmente o size_in_bytesque diz:

" Número de bytes consumidos pelo objeto de cache. "

Não consigo colocar o último pedaço do quebra-cabeça para entender o que realmente significa.

Eu sei que todos os operadores (sem falar na concessão de memória adicional usada para classificações e hashes) requerem uma certa quantidade de memória fixa, para armazenar estado, fazer cálculos etc., que é armazenado com o plano otimizado no cache, mas onde?

Então, minhas perguntas são:

  • O que size_in_bytesrealmente significa
  • Por que é um valor mais alto que o "tamanho do plano em cache"?
  • Onde está reservada a quantidade fixa de memória para todos os operadores / iteradores, está com o "tamanho do plano em cache" (32 KB no meu exemplo) ou em qualquer outro lugar?

Eu sei que eles são DMVs diferentes com funções diferentes, mas estão relacionados. Os planos compilados (armazenados em cache) em sys.dm_exec_cached_plansjunções sys.dm_os_memory_objectsna memory_object_addresscoluna. A razão de eu postar as perguntas aqui é que estou pedindo ajuda, entendendo como interpretar as DMVs e suas colunas.

Se size_in_bytesé o tamanho do plano em cache, por que o SQL Server diz outro valor no plano de execução real?

Nova consulta, novos números:

  • Plano real
    • Tamanho do plano em cache 16KB
    • CompileMemory 96KB
  • DMVs:
    • sys.dm_exec_cached_plans.size_in_bytes 24KB
    • sys.dm_os_memory_objects.pages_in_bytes, .max_pages_in_bytes 16KB.

Além disso, observe que esta consulta não requer nenhuma concessão de memória adicional para operações de classificação e hash.

Microsoft SQL Server 2012 - 11.0.5343.0 (X64)
GordonLiddy
fonte

Respostas:

12

O motivo pelo qual o size_in_bytescampo da sys.dm_exec_cached_plansDMV, pelo menos em termos de "Planos Compilados", é maior que o CachedPlanSizeatributo do QueryPlannó 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_bytescampo. 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_objectsqual está vinculada por meio do memory_object_addresscampo 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_objectsDMF. 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_bytesvalor desse objeto principal dividido por 1024 deve corresponder ao CachedPlanSizetamanho 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_bytesvalor 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_addressesse plano compilado. O truque para obter o valor correto é primeiro obter a memory_object_addresspartir sys.dm_exec_cached_plansde um determinado plano compilado e depois usá-lo para obter a linha MEMOBJ_COMPILE_ADHOC correspondente com sys.dm_os_memory_objectsbase em seu memory_object_addresscampo. Em seguida, pegue o page_allocator_addressvalor sys.dm_os_memory_objectsdessa linha e use-o para pegar todas as linhas sys.dm_os_memory_objectsque tenham o mesmo page_allocator_addressvalor. (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_addressvalor 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_plansjuntamente com o plano de consulta e as instruções para cada lote. A consulta diretamente acima é incorporada à consulta abaixo via XML como o MemoryObjectscampo:

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 TotalPlanBytescampo é apenas uma re-declaração do sys.dm_exec_cached_plans.size_in_bytescampo,
  • o AllocatedBytescampo é a SOMA dos objetos de memória relacionados que normalmente correspondem TotalPlanBytes(ou seja size_in_bytes)
  • o AllocatedBytescampo 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 usecountscampo exibido 1)
  • a BaseSingleStatementPlanKB campo deve corresponder ao CachedPlanSizeatributo do QueryPlannó no XML, mas apenas ao usar um único lote de consulta.
  • para lotes com várias consultas, deve haver linhas marcadas como MEMOBJ_STATEMENTem sys.dm_os_memory_objects, uma para cada consulta. O pages_in_bytescampo para essas linhas deve corresponder aos <QueryPlan>nós individuais do plano XML.

Recursos:

Solomon Rutzky
fonte