SQL Server armazenamento de TinyInt

12

No SQL Server, por que um tinyint é armazenado com 9B na linha. Por alguma razão, parece haver um byte adicional no final da máscara de bitmap NULL.

    USE tempdb;
    IR

    CRIAR TABELA tbl
    (
        i TINYINT NÃO NULO
    );
    IR

    INSERIR EM tbl (i)
        VALORES (1);
    IR

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

    DBCC TRACEON (3604); - Despejo de página irá para o console
    IR

    PÁGINA DBCC ('tempdb', 1.168,3);
    IR

Resultados (inverti os bytes devido às páginas do DBCC mostrando primeiro o byte menos significativo):

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!?)
ooutwire
fonte
1
Isso é apenas educacional? Eu sou tudo para aparar espaço onde necessário, mas isso não é provavelmente a 1 byte eu vou estar preocupado com ...
Aaron Bertrand
Isso é educativo. Minha próxima palestra no SQLSaturday é sobre compressin; portanto, criei exemplos para cada tipo de dados para ajudar as pessoas a entender as implicações de suas escolhas e mostrar o efeito da compactação em todos os tipos de dados.
ooutwire
Eu assumi que tinyint seria armazenado como 1B (é) com 7B de sobrecarga. Gostaria de saber qual é o byte extra no final do registro ???
31512 ooutwire
Vejo resultados diferentes (embora não tenha certeza se eles estão mais alinhados com o que você espera) quando a coluna TINYINT não é a única coluna na tabela. Parece um caso de uso bastante raro.
Aaron Bertrand
Certamente não é uma preocupação comum do caso de uso. Eu estava apenas tentando mostrar cada tipo de dados sozinho para direcionar para casa os custos indiretos envolvidos no armazenamento e permitir que os iniciantes vejam como é a coluna na página. Acho estranho ter o byte extra ... me deixa louco por vê-lo lá e sem motivo.
11382 ooutwire

Respostas:

12

Se você calcular o registro usando a adição de tamanho simples, obterá 8: 4 + 1 + 2 + 1 (cabeçalho + tamanho fixo + contagem de bitmap nulo + próprio bitmap nulo). Mas um registro de pilha não pode ser menor que o tamanho do stub de encaminhamento , que é de 9 bytes, pois o registro deve garantir que possa ser substituído por um stub de encaminhamento. Portanto, o registro será na verdade 9 bytes. A smallintterá 9 bytes, tanto por meio do tamanho da computação quanto do tamanho mínimo. Qualquer coisa maior já é maior que o stub de encaminhamento, portanto, o tamanho da sua computação corresponde ao tamanho do registro.

Remus Rusanu
fonte
Os 9 bytes também se aplicam a essa definição; CREATE TABLE tbl (i TINYINT NOT NULL PRIMARY KEY)portanto, é apenas uma regra geral para todas as linhas, sejam elas parte de um heap ou não?
Martin Smith
1
A árvore b pode ser transformada em heap ( alter table ... drop constraint) e a operação não é totalmente reconstruída (as páginas superiores da árvore b são jogadas fora, as páginas folha deixadas são desvinculadas e o resultado é a pilha), portanto a lógica de reserva ainda se aplica .
Remus Rusanu
Eu acho que isso prova o que Remus afirmou ... improve.dk/archive/2011/06/07/...
ooutwire
6

É bom ter a orelha do autor. :-) Kalen suspeita que isso seja apenas uma imposição de algum tipo de comprimento mínimo de linha, onde qualquer coisa <9 é aumentada para 9. É claro que existem apenas alguns casos em que isso é possível. Você encontrará este byte fantasma para TINYINT e BIT, bem como VARCHAR (1) / CHAR (1). Não aumentará além de 9 se você mudar para SMALLINT ou CHAR (2), mas aumentará se você mudar para, digamos, CHAR (3).

Portanto, essencialmente, você pode apontar as eficiências que pode obter escolhendo tipos de dados com sabedoria, mas ressalte que existem alguns casos extremos em que as regras não se aplicam devido a outros fatores na camada de armazenamento.

EDIT Espero ter informações mais concretas para você. Só queria que você soubesse que é isso que o autor do livro Internals pensa atualmente. Ela não tem 100% de certeza.

Aaron Bertrand
fonte
Obrigado Aaron por entrar em contato com Kalen. Eu estava vasculhando aquele livro ontem à noite e puxando meu cabelo. Isso é como os bytes de metadados extras para sql_variant, exceto que aqui não tenho como explicar o byte fantasma, exceto a mão que acena e grita: "É assim que é amigo!"
ooutwire
1
Bem, você pode juntar esse comentário com "este é um caso extremo, pois poucas tabelas foram projetadas para tentar armazenar um único tinyint ou char (1) em cada linha".
Aaron Bertrand