Eu tenho uma tabela com 64 milhões de linhas levando 4,3 GB em disco para seus dados.
Cada linha tem cerca de 30 bytes de colunas inteiras, mais uma NVARCHAR(255)
coluna variável para texto.
Eu adicionei uma coluna NULLABLE com data-type Datetimeoffset(0)
.
Atualizei esta coluna para cada linha e certifiquei-me de que todas as novas inserções colocassem um valor nesta coluna.
Uma vez que não havia entradas NULL, executei este comando para tornar meu novo campo obrigatório:
ALTER TABLE tblCheckResult
ALTER COLUMN [dtoDateTime] [datetimeoffset](0) NOT NULL
O resultado foi um ENORME crescimento no tamanho do log de transações - de 6 GB para mais de 36 GB até ficar sem espaço!
Alguém tem alguma ideia do que diabos o SQL Server 2008 R2 está fazendo para que esse comando simples resulte em um crescimento tão grande?
Quando você altera uma coluna para NOT NULL, o SQL Server precisa tocar em todas as páginas, mesmo que não haja valores NULL. Dependendo do seu fator de preenchimento, isso pode levar a muitas divisões de página. Cada página que é tocada, é claro, tem que ser registrada, e eu suspeito, devido às divisões, que duas alterações podem ter que ser registradas para muitas páginas. Como tudo é feito em uma única passagem, no entanto, o log precisa levar em conta todas as alterações para que, se você clicar em cancelar, ele saiba exatamente o que desfazer.
Um exemplo. Mesa simples:
Agora, vamos ver os detalhes da página. Primeiro precisamos descobrir com qual página e DB_ID estamos lidando. No meu caso, criei um banco de dados chamado
foo
, e o DB_ID era 5.A saída indicou que eu estava interessado na página 159 (a única linha na
DBCC IND
saída comPageType = 1
).Agora, vamos ver alguns detalhes da página selecionada enquanto percorremos o cenário do OP.
Agora, eu não tenho todas as respostas para isso, pois não sou um cara interno profundo. Mas está claro que - enquanto a operação de atualização e a adição da restrição NOT NULL gravam inegavelmente na página - a última o faz de uma maneira totalmente diferente. Parece realmente alterar a estrutura do registro, em vez de apenas mexer nos bits, trocando a coluna anulável por uma coluna não anulável. Por que ele tem que fazer isso, eu não tenho certeza - uma boa pergunta para a equipe do mecanismo de armazenamento , eu acho. Eu acredito que o SQL Server 2012 lida com alguns desses cenários muito melhor, FWIW - mas ainda não fiz nenhum teste exaustivo.
Ao executar o comando
Isso parece ser implementado como uma operação Add Column, Update, Drop Column.
sys.sysrscols
para representar uma nova coluna. Ostatus
bit for128
é definido indicando que a coluna não permiteNULL
ssys.sysrscols
.rscolid
atualizado para um número inteiro grande estatus
o bit 2 definido como indicado como descartado)sys.sysrscols
para a nova coluna é alterada para dar-lhe orscolid
valor da coluna antiga.A operação que tem o potencial de causar muitos logs é a
UPDATE
de todas as linhas da tabela, mas isso não significa que isso sempre ocorrerá. Se as imagens "antes" e "depois" da linha forem idênticas, isso será tratado como uma atualização sem atualização e não será registrado nos meus testes até agora.Portanto, a explicação de por que você está recebendo muitos registros dependerá de por que exatamente as versões "antes" e "depois" da linha não são as mesmas.
Para colunas de comprimento variável armazenadas no
FixedVar
formato, descobri que a configuraçãoNOT NULL
sempre causa uma alteração na linha que precisa ser registrada. A contagem de colunas e a contagem de colunas de comprimento variável são incrementadas e a nova coluna é adicionada ao final da seção de comprimento variável duplicando os dados.datetimeoffset(0)
é de comprimento fixo no entanto e para colunas de comprimento fixo armazenadas noFixedVar
formato as colunas antigas e novas parecem ter o mesmo slot na parte de dados de comprimento fixo da linha e como ambas têm o mesmo comprimento e valorizam o "antes" e As versões "depois" da linha são as mesmas . Isso pode ser visto na resposta de @Aaron. Ambas as versões da linha antes e depois doALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL;
sãoIsso não é registrado.
Logicamente, a partir da minha descrição de eventos, a linha deve ser diferente aqui, pois a contagem de colunas
02
deve ser aumentada,03
mas essa mudança não acontece na prática.Algumas possíveis razões pelas quais isso pode ocorrer em uma coluna de comprimento fixo são
SPARSE
então a nova coluna seria armazenada em uma parte da linha diferente da original, fazendo com que as imagens da linha anterior e posterior fossem diferentes.ALTER TABLE
operação anterior que foi implementada apenas como uma alteração de metadados e ainda não foi aplicada à linha. Por exemplo, se uma nova coluna de comprimento variável anulável foi adicionada, então isso é originalmente aplicado como uma alteração apenas de metadados e só é realmente gravado nas linhas quando elas são atualizadas em seguida (a gravação que realmente ocorre nesta última instância é apenas atualizações para a seção de contagem de colunas eNULL_BITMAP
como umaNULL
varchar
coluna no final da linha não ocupa nenhum espaço)Eu enfrentei o mesmo problema em relação a uma tabela com 200.000.000 linhas. Inicialmente, adicionei a coluna anulável, atualizei todas as linhas e, finalmente, alterei a coluna por
NOT NULL
meio de umaALTER TABLE ALTER COLUMN
instrução. Isso resultou em duas transações enormes explodindo incrivelmente o arquivo de log (crescimento de 170 GB).A maneira mais rápida que encontrei foi a seguinte:
Adicione a coluna usando um valor padrão
Elimine a restrição padrão usando SQL dinâmico, pois a restrição não foi nomeada antes:
O tempo de execução foi reduzido de > 30 minutos para 10 minutos, incluindo a replicação das alterações por meio da Replicação Transacional. Estou executando uma instalação do SQL Server 2008 (SP2).
Fiz o seguinte teste:
Acredito que isso tenha a ver com o espaço reservado que o log mantém apenas no caso de você reverter a transação. Procure na função fn_dblog na coluna 'Log Reserve' para a linha LOP_BEGIN_XACT e veja quanto espaço ela está tentando reservar.
O comportamento para isso é diferente no SQL Server 2012. Consulte http://rusanu.com/2011/07/13/online-non-null-with-values-column-add-in-sql-server-11/
O número de registros de log gerados para o SQL Server 2008 R2 e versões anteriores será significativamente maior do que o número de registros de log para o SQL Server 2012.