¿Por qué el espacio de datos de una tabla puede ocupar 4 veces el tamaño de los datos sin procesar?

18

Tengo una tabla con 490 M de filas y 55 GB de espacio de tabla, por lo que unos 167 bytes por fila. La tabla tiene tres columnas: a VARCHAR(100), a DATETIME2(0)y a SMALLINT. La longitud promedio del texto en el VARCHARcampo es de aproximadamente 21.5, por lo que los datos sin procesar deben ser de alrededor de 32 bytes por fila: 22 + 2 para el VARCHAR, 6 para el DATETIME2y 2 para el entero de 16 bits.

Tenga en cuenta que el espacio anterior es solo de datos, no de índices. Estoy usando el valor informado en Propiedades | Almacenamiento | General | Espacio de datos.

Por supuesto, debe haber algo de sobrecarga, pero 135 bytes por fila parece mucho, especialmente para una tabla grande. ¿Por qué podría ser esto? ¿Alguien más ha visto multiplicadores similares? ¿Qué factores pueden influir en la cantidad de espacio extra requerido?

A modo de comparación, intenté crear una tabla con dos INTcampos y 1 M de filas. El espacio de datos requerido fue de 16.4 MB: 17 bytes por fila, en comparación con 8 bytes de datos sin procesar. Otra tabla de prueba con una INTy una VARCHAR(100)con el mismo texto que la tabla real usa 39 bytes por fila (44 K filas), donde esperaría 28 más un poco.

Por lo tanto, la tabla de producción tiene considerablemente más gastos generales. ¿Es esto porque es más grande? Esperaría que los tamaños de índice fueran aproximadamente N * log (N), pero no veo por qué el espacio requerido para que los datos reales no sean lineales.

¡De antemano, gracias por cualquier consejo!

EDITAR:

Todos los campos enumerados son NOT NULL. La tabla real tiene un PK agrupado en el VARCHARcampo y el DATETIME2campo, en ese orden. Para las dos pruebas, la primera INTfue la PK (agrupada).

Si es importante: la tabla es un registro de resultados de ping. Los campos son URL, fecha / hora de ping y latencia en milisegundos. Los datos se agregan constantemente y nunca se actualizan, pero los datos se eliminan periódicamente para reducirlos a solo unos pocos registros por hora por URL.

EDITAR:

Una respuesta muy interesante aquí sugiere que, para un índice con mucha lectura y escritura, la reconstrucción puede no ser beneficiosa. En mi caso, el espacio consumido es una preocupación, pero si el rendimiento de escritura es más importante, uno podría estar mejor con índices flácidos.

Jon de todos los oficios
fuente

Respuestas:

11

Después de las discusiones en los comentarios sobre la pregunta original, parece que en este caso el espacio perdido es causado por la elección de la clave agrupada, lo que ha llevado a una fragmentación masiva.

Siempre vale la pena verificar el estado de fragmentación a través de sys.dm_db_index_physical_stats en estas situaciones.

Editar: siguiente actualización en los comentarios

La densidad de página promedio (antes de la reconstrucción del índice agrupado) fue del 24%, lo que encaja perfectamente con la pregunta original. Las páginas tenían solo 1/4 de espacio, por lo que el tamaño total era 4 veces el tamaño de los datos sin procesar.

Mark Storey-Smith
fuente
7

Las estructuras en disco tienen sobrecarga:

  • encabezado de fila
  • mapa de bits nulo + puntero
  • desplazamientos de columna de longitud variable
  • punteros de versión de fila (opcional)
  • ...

Tomando columnas int de 2 x 4 bytes, tienes

  • Encabezado de fila de 4 bytes
  • Puntero de 2 bytes al mapa de bits NULL
  • 8 bytes para 2 columnas int
  • Mapa de bits NULL de 3 bytes

¡Guau, 17 bytes!

Puede hacer lo mismo para su segunda tabla de prueba que tiene más sobrecarga como la original:

  • 2 bytes para el recuento de columnas de longitud variable
  • 2 bytes por columna de longitud variable

¿Por qué la diferencia? Además (no voy a vincular a estos)

  • ¿alguna vez has reconstruido índices para desfragmentarlos?
  • elimina no reclama espacio
  • las páginas de datos se dividirán si inserta en el medio
  • las actualizaciones pueden causar punteros hacia adelante (deja un espacio)
  • desbordamiento de fila
  • columna varchar eliminada sin reconstrucción de índice o DBCC CLEANTABLE
  • montón o tabla (el montón no tiene índice agrupado = registros dispersos por todas partes)
  • Nivel de aislamiento RCSI (14 bytes adicionales por fila)
  • espacios finales (SET ANSI_PADDING está activado de forma predeterminada) en varchar. Use DATALENGTH para checl, no LEN
  • Ejecute sp_spaceused con @updateusage = 'true'
  • ...

Vea esto: SQL Server: ¿Cómo crear una tabla que llene una página de 8 KB?

De SO:

gbn
fuente
La muestra de columna int de 2x4 bytes no es 100% correcta. Tendrá un encabezado de fila de 4 bytes (2 bytes de estado y 2 bytes para el tamaño de datos de longitud fija). Entonces tendrás 2x4 bytes para los datos. Dos bytes para el recuento de columnas y un solo byte para el mapa de bits nulo, lo que da una longitud de registro total de 15 bytes, no 17.
Mark S. Rasmussen
@ Mark S. Rasmussen: ¿Dónde obtienes "2 bytes para el tamaño de datos de longitud fija"? MSDN? Y el mapa de bits nulo siempre es de 3 bytes: sqlskills.com/blogs/paul/post/… + msdn.microsoft.com/en-us/library/ms178085%28v=sql.90%29.aspx
gbn
¡Guau, gran detalle! Tomé en cuenta el campo de longitud de la VARCHARs en mi estimación anterior, pero no el recuento de columnas. Esta tabla no tiene campos NULLable (debería haber mencionado eso), ¿todavía les asigna bytes?
Jon de todos los oficios
¿Los índices de reconstrucción afectarían la parte de datos del espacio requerido? Quizás reconstruir el índice agrupado lo haría. Las inserciones ocurren en el medio, mucho, aunque si cambiara el orden de los campos de agrupación que se detendrían. La mayoría del resto no debería aplicarse en este caso, pero es una gran referencia para el caso general. Revisaré tus enlaces. ¡Buen material!
Jon de todos los oficios
1
@gbn Los 2 bytes para el tamaño de datos de longitud fija son parte del encabezado de fila de 4 bytes que usted menciona. Este es el puntero que apunta al final de la porción de longitud de datos fijos / comienzo del recuento de columnas / mapa de bits nulo. El mapa de bits NULL no siempre es de tres bytes. Si incluye el recuento de columnas, será un mínimo de tres bytes, pero puede ser más: dividí el mapa de bits y el recuento de columnas en mi descripción. Además, el mapa de bits NULL no siempre está presente, aunque lo estará en este caso.
Mark S. Rasmussen
5

¿Han cambiado los tipos de datos con el tiempo? ¿Se han eliminado las columnas de longitud variable? ¿Se han desfragmentado los índices con frecuencia pero nunca se han reconstruido? ¿Se han eliminado muchas filas o se han actualizado significativamente muchas columnas de longitud variable? Alguna buena discusión aquí .

Aaron Bertrand
fuente
Estoy 97% seguro de que no he cambiado un tipo de datos o eliminado un campo. Si lo hiciera, habría sido muy temprano cuando la mesa tenía muchas menos filas. No hay eliminaciones ni actualizaciones, los datos solo se agregan.
Jon de todos los oficios
Corrección: no son borrados, y bastante. Sin embargo, la tabla tiene un crecimiento neto considerable, por lo que me imagino que este espacio se reutilizaría rápidamente.
Jon of All Trades
Con muchas eliminaciones, los datos pueden o no reutilizarse. ¿Cuál es la clave de agrupación de la mesa? ¿Hay insertos en el medio de la mesa o al final?
mrdenny
La clave agrupada es compuesta, en los campos VARCHARy DATETIME2, en ese orden. Las inserciones se distribuirán uniformemente para el primer campo. Para el segundo campo, los valores nuevos y siempre serán mayores que los existentes.
Jon of All Trades