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 entiretablecache
y 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 NULL
valores, se almacenan como una cadena vacía 1900-01-01
o 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.
fuente
Respuestas:
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:
¿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é.
Estructura de arriba
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
FILLFACTOR
de 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.¿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).CHAR
/ losVARCHAR
datos se almacenan a 1 byte por carácter (ignorando los caracteres de doble byte por el momento).XML
está 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 queNCHAR
/NVARCHAR
.DATETIME2
tiene entre 6 y 8 bytes.DECIMAL
tiene entre 5 y 17 bytes (dependiendo de la precisión).cadenas (de nuevo, suponiendo .NET) siempre son UTF-16. No hay optimización para cadenas de 8 bits como lo que
VARCHAR
contiene. 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).XML
puede o no almacenarse de la misma manera en la memoria (tendré que buscar eso).DateTime
es siempre de 8 bytes (como T-SQLDATETIME
, pero no comoDATE
,TIME
oDATETIME2
). 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):
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).
fuente
Según su pregunta, parece que tiene un tamaño máximo de caché
S
y 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:
sys.partitions
y calcular el tamaño de la tabla utilizando definiciones de columna.BIGINT
columnas 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.BIGINT
columna y unaNVARCHAR(20)
columna no puede exceder 100 * (8 + 2 * 20) = 4800 bytes.S
, es extremadamente improbable que quepa en la memoria caché. Tendría que hacer pruebas para determinar si existe tal valor.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é
S
o 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:
TABLESAMPLE
siempre que el tamaño de su muestra sea lo suficientemente grande.SUM()
cierre temprano debido al valor de ese agregado. Solo he visto ese trabajo paraROW_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.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í.
fuente