O Database Snaphot (ou “instantâneo de Banco de Dados”, se você estiver no SQL Server em português – Que nome terrível, hehehe) é um banco de dados somente leitura, onde o SQL Server guarda os dados originais de tudo o que está sendo alterado pelos usuários, para que seja possível realizar verificações (dentre outras coisas) de como estava o banco antes destas ocorrerem.
Ao contrário do que muita gente pensa, as alterações ocorrem nas páginas de dados e não no objeto que está sofrendo alteração. O SQL Server utiliza um recurso chamado “Copy-on-write” onde, antes da página ser alterada, os dados originais são armazenados no snapshot.
A minha ideia incial era tentar mostrar dentro das páginas de dados quando estaríamos acessando o banco original (consultando o snapshot) e quando estaríamos vendo os dados alterados e enviados para o snapshot, porém em conversa com o grande Luciano Moreira (twitter | blog), ele me falou que isso não era possível (após um papo com o Paul Randal… Loucura esse mundo, ehehe), que cada página fica em um mapa de bits na memória e que para buscar isso apenas usando um debugger no SQL Server… Aí já viu né… Ficamos assim, hehehehe
Abaixo, tentarei demonstrar o funcionamento do snapshot… Mas vou tentar fazer algo diferente: ao invés de jogar todo o script, vou colocar ele por blocos e, no final, disponibilizo o script para download. Depois me digam o que acharam.
Primeiro, vamos criar a base e inserir alguns dados…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
/* Criando o banco, a tabela e inserindo alguns registros... */ CREATE DATABASE DatabaseSnapshot CREATE DATABASE Origem ON (NAME ='Origem_Data', FILENAME = N'D:\Bases\DatabaseSnapshot\Origem_Data.mdf') LOG ON (NAME = 'Origem_Log', FILENAME = N'D:\Bases\DatabaseSnapshot\Origem_Log.ldf') GO USE Origem CREATE TABLE Pessoa (Codigo int identity(1,1), Nome char(2000), nascimento date) GO INSERT INTO Pessoa VALUES ('Logan', '1981-05-25'), ('Kratos', '1980-05-25'), ('Cronos', '1979-05-25'), (, '1980-07-25'), ('Fulano', '1980-07-25') |
Vejam que eu criei a coluna Nome como char(2000), apenas para que alguns registros estejam em cada página de dados (entenda o que é uma página de dados aqui).
Feito isso, vamos criar um snapshot:
1 2 3 4 5 6 7 |
CREATE DATABASE DatabaseSnapshot ON (name = DBSS_Data , FILENAME = 'D:\Bases\DatabaseSnapshot\DBSS_Data.ss') AS SNAPSHOT OF Origem |
Notem a sintaxe da criação do snapshot… Ela é muito semelhante com a de criação de um banco comum, com exceção do Log (que não existe no snapshot) e a informação do banco de origem (AS SNAPSHOT OF …). E aos que estão querendo saber como fazer pelo SSMS, eu sinto muito, mas não é possível. Tem que ser na munheca mesmo (e nem é tão complicado assim, vai).
Com isso, a nossa estrutura ficou assim, até o momento:
Dando uma olhada nas propriedades do .ss que foi criado dá pra ver que o arquivo está praticamente vazio, apesar de uma consulta no banco trazer os dados normalmente (faça um select para confirmar). Isso ocorre pois o snapshot trabalha com o conceito de Sparse Files. Veja a imagem abaixo:
Agora, vamos alterar um registro, apagar outro e inserir um novo e, então, vamos comparar com o snapshot para ver o resultado:
1 2 3 4 5 6 7 8 |
UPDATE Pessoa SET nome = 'Logan Merazzi', nascimento = '2013-04-10' WHERE Codigo = 1 DELETE FROM Pessoa WHERE Codigo = 5 INSERT INTO Pessoa VALUES ('Waldisnei','1985-05-30') |
Viram os valores originais no snapshot?
Com isso, a estrutura passou para a seguinte situação:
É possível realizar a criação de “N” snapshots, um para cada momento desejado.
Vamos criar um novo snapshot, fazer algumas alterações, comparar o resultados novamente e então partir pra exclusão e restauração…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
CREATE DATABASE DatabaseSnapshot_V2 ON (name = Data , FILENAME = 'D:\Bases\DatabaseSnapshot\DBSSV2_Data.ss') AS SNAPSHOT OF Origem GO update Pessoa set nome = RTRIM(Nome) + ' A', nascimento = '2020-10-10' where Codigo in (3,6) -- Comparando SELECT POrig.Codigo, POrig.Nome, POrig.nascimento, PSnap.Codigo, PSnap.Nome, PSnap.nascimento, PSnap2.Codigo, PSnap2.Nome, PSnap2.nascimento FROM Pessoa POrig FULL JOIN DatabaseSnapshot.dbo.Pessoa PSnap ON (POrig.Codigo = PSnap.Codigo) FULL JOIN DatabaseSnapshot_V2.dbo.Pessoa PSnap2 ON (POrig.Codigo = PSnap2.Codigo) |
A sintaxe para restaurar um snapshot é a seguinte:
1 2 |
RESTORE DATABASE DatabaseSnapshot FROM DATABASE_SNAPSHOT = 'DatabaseSnapshot'; |
Porém é importante frisar que para a restauração funcionar, deve existir apenas um snapshot associado, desta forma, todos os demais snapshots devem ser excluídos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
use master RESTORE DATABASE Origem FROM DATABASE_SNAPSHOT = 'DatabaseSnapshot'; -- Erro? -- Precisamos dropar os demais snapshots... DROP DATABASE DatabaseSnapshot_V2 -- E agora? Vai? RESTORE DATABASE Origem FROM DATABASE_SNAPSHOT = 'DatabaseSnapshot'; select * from DatabaseSnapshot.dbo.Pessoa select * from Origem.dbo.Pessoa |
Com a base restaurada a partir do snapshot, vejam que a base voltou para o mesmo estado do início do post.
Considerações:
- Snapshot não substitui backup. Ele ajuda para restaurações pontuais, porém como o snapshot é diretamente dependente da base original (você não consegue apagar o banco ou restaurá-lo se houver um snapshot associado), qualquer erro que ocorra na base original (base corrompida, falha no disco, etc), os dados não estarão mais seguros.
- Para criação de bases de testes, homologação e demonstração é uma mão na roda… voltar uma base de um snapshot, dependendo do tamanho da base, é muito mais rápido. Sem contar que as comparações das alterações se tornam muito mais simples de serem verificadas.
- Esse recurso está disponível a partir da versão 2005 do SQL Server, porém apenas na edição Enterprise (ou Developer).
- A complexidade de administração aumenta um pouco, pois você tem que prestar muita atenção quando houverem vários snapshots criados para saber qual ponto restaurar e avaliar a perda do “histórico”.
- Você terá um aumento considerável de I/O de disco… então use-o com sabedoria.
Bom, fico por aqui… Façam as suas considerações e vamos discutindo.
O script completo está aqui. Enjoy!
[]’s!