Hoje eu descobri o disco rígido que armazena meus bancos de dados estava cheio. Isso já aconteceu antes, geralmente a causa é bastante evidente. Geralmente, há uma consulta incorreta, que causa um grande derramamento no tempdb, que aumenta até o disco ficar cheio. Dessa vez, ficou um pouco menos evidente o que aconteceu, já que o tempdb não foi a causa da unidade completa, foi o próprio banco de dados.
Os fatos:
- O tamanho usual do banco de dados é de cerca de 55 GB, cresceu para 605 GB.
- O arquivo de log tem tamanho normal, o arquivo de dados é enorme.
- O arquivo de dados possui 85% de espaço disponível (eu interpreto isso como 'air': espaço que foi usado, mas foi liberado. O SQL Server reserva todo o espaço depois de alocado).
- O tamanho do tempdb é normal.
Eu encontrei a causa provável; há uma consulta que seleciona muitas linhas (junção incorreta causa seleção de 11 bilhões de linhas, onde são esperadas algumas centenas de milhares). Esta é uma SELECT INTO
consulta, que me fez pensar se o seguinte cenário poderia ter acontecido:
- SELECT INTO é executado
- A tabela de destino é criada
- Os dados são inseridos à medida que são selecionados
- O disco enche, causando falha na inserção
- SELECT INTO é abortado e revertido
- A reversão libera espaço (os dados já inseridos são removidos), mas o SQL Server não libera o espaço liberado.
Nessa situação, no entanto, eu não esperava que a tabela criada pelo SELECT INTO
ainda existisse, ela deveria ser descartada pela reversão. Eu testei isso:
BEGIN TRANSACTION
SELECT T.x
INTO TMP.test
FROM (VALUES(1))T(x)
ROLLBACK
SELECT *
FROM TMP.test
Isto resulta em:
(1 row affected)
Msg 208, Level 16, State 1, Line 8
Invalid object name 'TMP.test'.
No entanto, a tabela de destino existe. Porém, a consulta real não foi executada em uma transação explícita. Isso pode explicar a existência da tabela de destino?
As suposições aqui esboçadas estão corretas? É provável que isso tenha acontecido?