Planifique el tamaño del caché y la memoria reservada

18

Al ejecutar una consulta que incluye el Plan de ejecución real, el operador raíz ( SELECT) me dice que el Tamaño del plan en caché es de 32 KB.

Una consulta que se une sys.dm_exec_cached_plansy sys.dm_os_memory_objects, al observar el plan en cuestión, dice que los valores para pages_in_bytesy max_pages_in_bytesson 32768 (32 KB), que coincide con el tamaño del plan en caché.

Lo que no entiendo es cuál es el valor en sys.dm_exec_cached_plans.size_in_bytes49152 (48 KB). He leído BOL en todas estas columnas, y especialmente lo size_in_bytesque dice:

" Número de bytes consumidos por el objeto de caché " .

No puedo poner esa última parte del rompecabezas en su lugar, para entender lo que realmente significa.

Sé que todos los operadores (sin hablar de la concesión de memoria adicional utilizada para géneros y hashes) requieren una cierta cantidad de memoria fija, para almacenar el estado, hacer cálculos, etc., que se almacena con el plan optimizado en la memoria caché, pero ¿dónde?

Entonces, mis preguntas son:

  • ¿Qué significa size_in_bytesrealmente?
  • ¿Por qué es un valor más alto que el "Tamaño del plan en caché"?
  • ¿Dónde está reservada la cantidad fija de memoria para todos los operadores / iteradores, es con el "Tamaño del plan en caché" (32Kb en mi ejemplo) o en cualquier otro lugar?

Sé que son diferentes DMV con diferentes funciones, pero están relacionados. Los planes compilados (en caché) se sys.dm_exec_cached_plansunen sys.dm_os_memory_objectsen la memory_object_addresscolumna. La razón por la que publico las preguntas aquí, es porque estoy pidiendo ayuda sobre esto, entendiendo cómo interpretar los DMV y sus columnas.

Si size_in_byteses el tamaño del plan en caché, ¿por qué SQL Server dice otro valor en el plan de ejecución real?

Nueva consulta, nuevos números:

  • Plan real
    • Tamaño del plan en caché 16 KB
    • CompileMemory 96KB
  • DMV:
    • sys.dm_exec_cached_plans.size_in_bytes 24 KB
    • sys.dm_os_memory_objects.pages_in_bytes, .max_pages_in_bytes 16 KB.

Además, tenga en cuenta que esta consulta no requiere ninguna concesión de memoria adicional para clases y operaciones hash.

Microsoft SQL Server 2012 - 11.0.5343.0 (X64)
GordonLiddy
fuente

Respuestas:

12

La razón por la que el size_in_bytescampo del sys.dm_exec_cached_plansDMV, al menos en términos de "Planes compilados", es mayor que el CachedPlanSizeatributo del QueryPlannodo 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_bytescampo. 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_objectsque está vinculada a través del memory_object_addresscampo 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_objectsDMF. 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_bytesdividido 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_bytesvalor de ese objeto principal dividido por 1024 debe coincidir con el CachedPlanSizetamañ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_bytesvalor se puede obtener sumando las entradas en el sys.dm_os_memory_objectsDMV (los elementos indicados anteriormente en negrita), todos relacionados por dm_os_memory_objects.page_allocator_addressese Plan Compilado. El truco para obtener el valor correcto es primero obtener el memory_object_addressde sys.dm_exec_cached_plansun Plan Compilado en particular, luego usarlo para obtener la fila MEMOBJ_COMPILE_ADHOC correspondiente en sys.dm_os_memory_objectsfunción de su memory_object_addresscampo. Luego, tome el page_allocator_addressvalor de sys.dm_os_memory_objectsesa fila y úselo para tomar todas las filas sys.dm_os_memory_objectsque tengan el mismo page_allocator_addressvalor. (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_addressvalor 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_plansjunto 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 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;

Tenga en cuenta que:

  • el TotalPlanBytescampo es solo una nueva declaración del sys.dm_exec_cached_plans.size_in_bytescampo,
  • el AllocatedBytescampo es la SUMA de los objetos de memoria relacionados que normalmente coinciden TotalPlanBytes(es decir size_in_bytes)
  • el AllocatedBytescampo 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 usecountscampo que se muestra 1)
  • el BaseSingleStatementPlanKBcampo debe coincidir con el CachedPlanSizeatributo del QueryPlannodo 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_STATEMENTen sys.dm_os_memory_objects, una para cada consulta. El pages_in_bytescampo para estas filas debe coincidir con los <QueryPlan>nodos individuales del plan XML.

Recursos:

Solomon Rutzky
fuente