Servidor SQL Almacenamiento de TinyInt

12

En SQL Server, ¿por qué se almacena un tinyint con 9B en la fila? Por alguna razón, parece haber un byte adicional al final de la máscara de mapa de bits NULL.

    USE tempdb;
    VAMOS

    CREAR TABLA tbl
    (
        i TINYINT NO NULL
    );
    VAMOS

    INSERTAR EN tbl (i)
        VALORES (1);
    VAMOS

    DBCC IND ('tempdb', 'tbl', - 1);
    VAMOS

    DBCC TRACEON (3604); - El volcado de página irá a la consola
    VAMOS

    PÁGINA DBCC ('tempdb', 1,168,3);
    VAMOS

Resultados (invertí los bytes debido a que DBCC PAGE muestra el byte menos significativo primero):

Record Size = 9B
10000500 01010000 00
TagA = 0x10 = 1B
TagB = 0x00 = 1B
Null Bitmap Offset = 0x0005 = 2B
Our integer column = 0x01 = 1B
Column Count = 0x0001 = 2B
NULL Bitmap = 0x0000 = 2B (what!?)
fuera de línea
fuente
1
¿Es esto solo educativo? Estoy dispuesto a recortar el espacio donde sea necesario, pero probablemente este no sea el 1 byte del que me preocuparé ...
Aaron Bertrand
Esto es educativo Mi próxima charla de SQLSaturday es sobre la compresión; Por lo tanto, he creado ejemplos para cada tipo de datos para ayudar a las personas a comprender las implicaciones de sus elecciones de tipos de datos y para mostrar el efecto de la compresión en todos los tipos de datos.
Ooutwire
Supuse que tinyint se almacenaría como 1B (lo es) con 7B de sobrecarga. Me pregunto cuál es el byte extra al final del registro.
ooutwire
Veo resultados diferentes (aunque no estoy seguro si están más en línea con lo que espera) cuando la columna TINYINT no es la única columna en la tabla. Parece un caso de uso bastante raro.
Aaron Bertrand
Ciertamente, no es una preocupación común de caso de uso. Solo intentaba mostrar cada tipo de datos solo para llevar a casa los costos generales involucrados en el almacenamiento y permitir que los principiantes vean cómo se ve la columna en la página. Me resulta extraño tener el byte extra ... me vuelve loco verlo allí y sin razón.
ooutwire

Respuestas:

12

Si calcula el registro utilizando la suma de tamaño simple, obtendrá 8: 4 + 1 + 2 + 1 (encabezado + tamaño fijo + recuento de mapa de bits nulo + mapa de bits nulo en sí mismo). Pero un registro de almacenamiento dinámico no puede ser menor que el tamaño del apéndice de reenvío , que es de 9 bytes, ya que el registro debe garantizar que se pueda reemplazar por un apéndice de reenvío. Por lo tanto, el registro tendrá en realidad 9 bytes. A smallintserá de 9 bytes tanto por cálculo como por tamaño mínimo. Cualquier cosa más grande ya es más grande que el código auxiliar de reenvío, por lo que su tamaño de cómputo coincide con el tamaño del registro.

Remus Rusanu
fuente
Los 9 bytes también se aplican a esta definición, CREATE TABLE tbl (i TINYINT NOT NULL PRIMARY KEY)entonces, ¿es solo una regla general para todas las filas, sean o no parte de un montón?
Martin Smith
1
El b-tree se puede transformar en un montón ( alter table ... drop constraint) y la operación no es una reconstrucción completa (las páginas superiores del b-tree se descartan, las páginas que quedan se desvinculan y el resultado es el montón), por lo que la lógica de reserva aún se aplica .
Remus Rusanu
Creo que esto demuestra lo que Remus ha declarado ... improve.dk/archive/2011/06/07/...
ooutwire
6

Es bueno tener el oído del autor. :-) Kalen sospecha que esto es solo el cumplimiento de algún tipo de longitud mínima de fila, donde cualquier cosa <9 se rellena a 9. Por supuesto, solo hay unos pocos casos en los que esto es posible. Encontrará este byte fantasma para TINYINT y BIT, así como VARCHAR (1) / CHAR (1). No aumentará más allá de 9 si te mueves a SMALLINT o CHAR (2), pero aumentará si te mueves a, digamos, CHAR (3).

Esencialmente, puede señalar las eficiencias que puede obtener eligiendo los tipos de datos de manera inteligente, pero señale que hay algunos casos extremos en los que las reglas no se cumplen debido a otros factores en la capa de almacenamiento.

EDITAR Espero tener más información concreta para usted. Solo quería hacerle saber que esto es lo que piensa actualmente el autor del libro Internals. Ella no está 100% segura.

Aaron Bertrand
fuente
Gracias Aaron por contactar a Kalen. Estaba cavando en ese libro anoche y arrancándome el pelo. Esto es un poco como los bytes de metadatos adicionales para sql_variant, excepto que aquí no tengo forma de explicar el byte fantasma, salvo para agitar y gritar "¡Esa es la forma en que es amigo!"
ooutwire
1
Bueno, puede combinar ese comentario con "este es un caso extremo, ya que no muchas tablas están diseñadas para tratar de almacenar un solo tinyint o char (1) en cada fila".
Aaron Bertrand