Encuentra el tamaño sin comprimir de todas las tablas en una base de datos

12

En Dynamics AX hay un mecanismo de almacenamiento en caché donde las tablas se pueden configurar para cargarse en la memoria y almacenarse en caché. Este caché está limitado a una cierta cantidad de KB para evitar problemas de memoria. Se llama a la configuración de la que estoy hablando entiretablecachey carga toda la tabla en la memoria tan pronto como se solicita un único registro.

Hasta hace poco, confiamos en algunos scripts para verificar el tamaño de las tablas que tienen esta configuración para ver si el tamaño de la tabla está por encima de este límite.

Ahora, sin embargo, la compresión entra en juego y cosas como sp_spaceused o sys.allocation_units parecen informar el espacio realmente utilizado por los datos comprimidos.

Obviamente, el servidor de aplicaciones está trabajando con datos sin comprimir, por lo que el tamaño de los datos en el disco en SQL Server es irrelevante. Necesito el tamaño real que tendrán los datos sin comprimir.

Sé de sp_estimate_data_compression_savings pero como su nombre lo dice, esto es solo una estimación.
Preferiría tener el tamaño lo más correcto posible.

La única forma en que podía pensar era en un SQL dinámico enrevesado que creaba tablas sin comprimir con la misma estructura que las tablas comprimidas, insertaba los datos comprimidos en esa tabla de sombra y luego verificaba el tamaño de esa tabla de sombra.
No hace falta decir que esto es un poco tedioso y lleva un tiempo ejecutarse en una base de datos de varios cientos de GB.

Powershell podría ser una opción, pero no me gustaría iterar sobre todas las tablas para realizar un select *control en ellas para verificar el tamaño en el script, ya que eso solo inundaría el caché y probablemente también tomaría mucho tiempo.

En resumen, necesito una forma de obtener el tamaño de cada tabla, ya que alguna vez estará sin comprimir y con la fragmentación fuera de la ecuación presentada a la aplicación, si eso es posible. Estoy abierto a diferentes enfoques, se prefiere T-SQL, pero no me opongo a Powershell u otros enfoques creativos.

Suponga que el búfer en la aplicación es del tamaño de los datos. Un bigint es siempre del tamaño de un bigint, y un tipo de datos de caracteres es de 2 bytes por carácter (unicode). Los datos BLOB también toman el tamaño de los datos, una enumeración es básicamente un int y los datos numéricos son numéricos (38,12), datetime es el tamaño de un datetime. Además, no hay NULLvalores, se almacenan como una cadena vacía 1900-01-01o cero.

No hay documentación sobre cómo se implementa esto, pero las suposiciones se basan en algunas pruebas y los scripts utilizados por PFE y el equipo de soporte (que aparentemente también ignoran la compresión, ya que el control está integrado en la aplicación y la aplicación no puede decirlo). si los datos subyacentes están comprimidos), que también verifican los tamaños de tabla. Este enlace, por ejemplo, dice:

Evite usar cachés de WholeTable para tablas grandes (en AX 2009 de más de 128 KB o 16 páginas, en AX 2012 sobre la configuración de la aplicación 'tamaño de caché de tabla completa' [valor predeterminado: 32 KB o 4 páginas]) - en su lugar, pase al caché de registros.

Tom V - prueba topanswers.xyz
fuente
3
Es hacky, pero quizás una copia restaurada con compresión deshabilitada sería la más precisa. Entonces también está probando restauraciones, lo que lo hace ver como un DBA TOP 1.
Erik Darling
Cree que esa sería tu mejor apuesta. Podría haber formas de intentar hacer los cálculos. Cuántas filas por tipos de datos columnares definidos y longitudes se multiplican y luego se agregan en los índices, etc. Es mucho más trabajo que programar la restauración y deshabilitar la compresión que @sp_BlitzErik sugiere anteriormente. ¿Y quién no querría ser un DBA TOP 1?
Mike Walsh
¿SUM (datalength ()) para todas las columnas obtiene un tamaño de datos sin comprimir?
Tapakah Ua
@sp_BlitzErik Esa podría ser una respuesta en lugar de un comentario.
Tom V: prueba topanswers.xyz el

Respuestas:

7

Necesito el tamaño real que tendrán los datos sin comprimir.
...
Preferiría tener el tamaño lo más correcto posible.

Si bien el deseo de esta información es ciertamente comprensible, obtener esta información, especialmente en el contexto de "lo más correcto posible" es más complicado de lo que todos esperan debido a suposiciones erróneas. Ya sea haciendo la idea de la tabla de sombra sin comprimir mencionada en la pregunta, o la sugerencia de @ sp_BlitzErik en un comentario sobre la restauración de la base de datos y la descompresión allí para verificar, no se debe suponer que el tamaño de la tabla sin comprimir == el tamaño de dichos datos en la memoria en el servidor de aplicaciones:

  1. ¿Se están almacenando en caché todas las filas de la tabla? ¿O solo dentro de un rango? La suposición aquí es que es todo, y eso podría ser correcto, pero pensé que al menos debería mencionarse que este podría no ser el caso (a menos que la documentación indique lo contrario, pero este es un punto menor de todos modos, simplemente no quería para no ser mencionado).

    La pregunta se actualizó al estado: sí, todas las filas se almacenan en caché.

  2. Estructura de arriba

    1. En el lado de la base de datos: encabezado de
      página y fila en el lado de la base de datos: la cantidad de filas que caben en una página está determinada por muchos factores que podrían arrojar estimaciones. Incluso con un valor FILLFACTORde 100 (o 0), es probable que quede algo de espacio no utilizado en la página debido a que no es suficiente para una fila completa. Y eso es además del encabezado de página. Además, si se habilita alguna funcionalidad de aislamiento de instantáneas, creo que habrá 13 bytes adicionales por fila ocupados por el número de versión, y eso arrojará estimaciones. Hay otras minucias relacionadas con el tamaño real de la fila (mapa de bits NULL, columnas de longitud variable, etc.), pero los elementos mencionados hasta ahora solo deberían hacer el punto.
    2. En el lado del servidor de aplicaciones:
      ¿Qué tipo de colección se está utilizando para almacenar los resultados en caché? Supongo que esta es una aplicación .NET, ¿entonces es una DataTable? ¿Una lista genérica? Un SortedDictionary? Cada tipo de colección tiene una cantidad diferente de escucha. No esperaría que ninguna de las opciones refleje necesariamente los encabezados de página y fila en el lado de la base de datos, especialmente a escala (estoy seguro de que una pequeña cantidad de fila puede no tener suficientes variables para importar, pero no está buscando diferencias en cientos de bytes o solo unos pocos KB).
  3. Tipos de datos
    1. En el lado de la base de datos:
      CHAR/ los VARCHARdatos se almacenan a 1 byte por carácter (ignorando los caracteres de doble byte por el momento). XMLestá optimizado para no ocupar casi tanto espacio como implicaría la representación de texto. Este tipo de datos crea un diccionario de nombres de elementos y atributos y reemplaza las referencias reales a ellos en el documento con sus respectivos ID (un poco agradable, en realidad). De lo contrario, los valores de cadena son todos UTF-16 (2 o 4 bytes por "carácter"), al igual que NCHAR/ NVARCHAR. DATETIME2tiene entre 6 y 8 bytes. DECIMALtiene entre 5 y 17 bytes (dependiendo de la precisión).
    2. En el lado del servidor de aplicaciones: las
      cadenas (de nuevo, suponiendo .NET) siempre son UTF-16. No hay optimización para cadenas de 8 bits como lo que VARCHARcontiene. PERO, las cadenas también se pueden "internar", que es una copia compartida a la que se puede hacer referencia muchas veces (pero no sé si esto funciona para cadenas en colecciones, o si es así, si funciona para todo tipo de colecciones). XMLpuede o no almacenarse de la misma manera en la memoria (tendré que buscar eso). DateTimees siempre de 8 bytes (como T-SQL DATETIME, pero no como DATE, TIMEo DATETIME2). siempreDecimal es de 16 bytes .

Todo eso para decir: no hay prácticamente nada que pueda hacer en el lado de la base de datos para obtener un tamaño de huella de memoria incluso bastante preciso en el lado del servidor de aplicaciones. Debe encontrar una manera de interrogar al servidor de aplicaciones en sí, después de cargarlo con una tabla en particular, así que sepa qué tan grande es. Y no estoy seguro de si un depurador le permitiría ver el tamaño de tiempo de ejecución de una colección completa. Si no, entonces la única forma de acercarse sería pasar por todas las filas de una tabla, multiplicando cada columna por el tamaño apropiado de .NET (p. Ej. INT= * 4, VARCHAR= DATALENGTH() * 2, NVARCHAR= DATALENGTH(), XML= 🙃, etc.), pero eso todavía deja la pregunta de los gastos generales de la colección más cada elemento de la colección.

Dada alguna nueva definición en la pregunta, uno probablemente podría hacer la siguiente consulta para acercarse bastante. Y no importa si la tabla está comprimida o no, aunque depende de cada persona determinar si el escaneo de todas las filas es apropiado en Producción (tal vez desde una restauración o durante las horas de menor actividad):

SELECT
   SUM( DATALENGTH([NVarcharColumn_1]) + DATALENGTH([NVarcharColumn_N]) ) + 
   SUM( (DATALENGTH([VarcharColumn_1]) + DATALENGTH([VarcharColumn_N])) * 2 ) + 
   SUM(4 * [number_of_INT_columns]) +
   SUM(8 * [number_of_BIGINT_and_DATETIME_columns]) +
   SUM(16 * [number_of_DECIMAL/NUMERIC_and_UNIQUEIDENTIFIER_columns]) +
   etc..
FROM [SchemaName].[TableName] WITH (NOLOCK) -- assuming no Snapshot Isolation

Pero recuerde, esto no tiene en cuenta la recaudación o la sobrecarga del elemento de recopilación. Y no estoy seguro de si podemos obtener ese valor sin un depurador (o posiblemente algo como ILSpy, pero no lo recomiendo, ya que podría violar el EULA según las leyes locales).

Solomon Rutzky
fuente
Terminamos implementando las comprobaciones en el código para asegurarnos del tamaño del búfer tal como se presenta a la aplicación.
Tom V - prueba topanswers.xyz
6

Según su pregunta, parece que tiene un tamaño máximo de caché Sy no desea cargar tablas en el caché que excedan ese tamaño. Si eso es cierto, entonces no necesita saber el tamaño exacto de cada tabla. Solo necesita saber si una tabla es más grande o más pequeña que el tamaño máximo de caché S. Ese es un problema significativamente más fácil dependiendo de las definiciones de columnas y los recuentos de filas de sus tablas.

Estoy de acuerdo con la gran respuesta de Solomon Rutzky en que mirar datos sin comprimir no es el camino a seguir y puede ser difícil encontrar una buena aproximación para el tamaño real de una tabla en caché. Sin embargo, voy a trabajar dentro del marco de la pregunta y supongo que puede desarrollar una fórmula lo suficientemente cercana en función de las definiciones de columna para los tipos de datos estáticos y la longitud real de sus columnas dinámicas.

Si tiene esa asignación de tipos de datos al tamaño de caché, entonces debería poder evaluar algunas tablas sin siquiera mirar los datos en ellas:

  1. Si una tabla solo tiene tipos de datos estáticos (sin cadenas ni blobs), puede aproximar el número de filas al observar sys.partitionsy calcular el tamaño de la tabla utilizando definiciones de columna.
  2. Si una tabla con muchas filas tiene suficientes columnas de tipo de datos estáticos, entonces puede eliminarla como demasiado grande sin mirar sus datos. Por ejemplo, una tabla con 10 millones de filas y 5 BIGINTcolumnas podría tener el tamaño de esos datos como 10000000 * (8 + 8 + 8 + 8 + 8) = 400 M bytes que podrían ser mayores que el límite de tamaño de su caché S. No importa si también tiene un montón de columnas de cadena.
  3. Si una tabla con pocas filas es lo suficientemente pequeña, entonces puede confirmar que está por debajo del límite simplemente asumiendo que cada tipo de datos dinámicos tiene el tamaño máximo posible. Por ejemplo, una tabla de 100 filas con una BIGINTcolumna y una NVARCHAR(20)columna no puede exceder 100 * (8 + 2 * 20) = 4800 bytes.
  4. Puede ser cierto que si una tabla tiene un tamaño comprimido en SQL Server que es mayor por algún factor S, es extremadamente improbable que quepa en la memoria caché. Tendría que hacer pruebas para determinar si existe tal valor.
  5. Podrías tener suerte en que todas las columnas dinámicas tengan estadísticas sobre ellas. Las estadísticas contienen información sobre la longitud promedio y que puede ser lo suficientemente precisa para sus propósitos.

Es posible que deba consultar los datos de las tablas que no se ajustan a ninguno de los criterios anteriores. Hay algunos trucos que puede usar para minimizar el impacto en el rendimiento de esto. Diría que aquí tiene dos prioridades en competencia: valora la precisión pero tampoco desea escanear todos los datos de su base de datos. Es posible agregar algún tipo de búfer a sus cálculos. No sé si es más aceptable excluir una tabla que esté ligeramente por debajo del tamaño máximo de caché So incluir una tabla que esté ligeramente por encima del tamaño máximo de caché.

Aquí hay algunas ideas para hacer que las consultas que miran los datos de la tabla sean más rápidas:

  1. Para tablas grandes, puede utilizar TABLESAMPLEsiempre que el tamaño de su muestra sea lo suficientemente grande.
  2. Para tablas grandes con una clave agrupada, puede ser útil procesarlas en lotes en la clave agrupada. Desafortunadamente, no conozco una manera de calcular un SUM()cierre temprano debido al valor de ese agregado. Solo he visto ese trabajo para ROW_NUMBER(). Pero puede escanear el primer 10% de la tabla, guardar el tamaño de datos calculado, escanear el siguiente 10%, etc. Para las tablas que son demasiado grandes para la memoria caché, puede ahorrar una gran cantidad de trabajo con este enfoque si abandona antes de tiempo.
  3. Para algunas tablas, puede tener la suerte de tener índices de cobertura en todas las columnas dinámicas. Dependiendo del tamaño de la fila u otros factores, el escaneo de cada índice a la vez podría ser más rápido que el escaneo de una tabla. También podría salir de este proceso antes si el tamaño de la tabla es demasiado grande después de leer un índice en una sola columna.
  4. Las longitudes promedio de sus columnas dinámicas pueden no cambiar mucho con el tiempo. Puede ser práctico ahorrar las longitudes promedio que calcula y utilizar esos valores en sus cálculos por un tiempo. Puede restablecer estos valores según la actividad DML en las tablas o según alguna otra métrica.
  5. Si es posible ejecutar pruebas en todas las tablas para desarrollar un algoritmo, entonces puede aprovechar los patrones en los datos. Por ejemplo, si procesa tablas que comienzan con la más pequeña primero, puede encontrar que una vez que procesa 10 tablas (hice este número) en una fila que son demasiado grandes para el caché, es muy poco probable que las tablas más grandes encajen en el cache. Esto podría ser aceptable si está bien excluir algunas tablas que podrían haber cabido en la memoria caché.

Me doy cuenta de que no incluí ningún código SQL en esta respuesta. Avíseme si sería útil escribir un código de demostración para cualquiera de las ideas que discutí aquí.

Joe Obbish
fuente
2
No había pensado en el enfoque de excluir tablas como esa, me gusta el enfoque
Tom V - intente topanswers.xyz