Melhor maneira de reduzir um banco de dados após anular dados de varbinary (max)?

8

Temos um banco de dados com uma grande quantidade de dados armazenados em um campo do tipo varbinary (max) . Em algum momento, podemos limpar esses dados para a maioria das linhas, mas não para todas. Nosso plano é tornar esse campo anulável e simplesmente anular os dados quando não forem mais necessários. Depois de fazer isso, gostaríamos de reduzir o tamanho do banco de dados. Qual é o melhor jeito de concluir isso?

Se não houver uma boa maneira de recuperar espaço com a configuração atual, uma idéia que tenho é mover esse campo de dados para uma tabela separada com apenas duas colunas: a chave da tabela principal e o campo de dados. Em seguida, poderíamos simplesmente excluir as linhas quando elas não forem mais necessárias. (E então faça algum tipo de redução.) No entanto, isso seria uma alteração muito mais difícil de fazer do que simplesmente tornar o campo existente anulável.

Nota: Na verdade, não me importo muito em diminuir o arquivo do banco de dados, mas me importo com o espaço recém-liberado se tornando reutilizável.

Mais de 90% do tamanho do banco de dados é esse campo. Eu já estou em 3 TB.

TTT
fonte

Respostas:

13

Parece-me que apenas atualize as colunas para NULLliberar páginas para reutilização. Aqui está uma demonstração do Very Scottish®, para comemorar quase 5 da tarde, EST.

USE tempdb;

DROP TABLE IF EXISTS dbo.RobertBurns;

CREATE TABLE dbo.RobertBurns
(
    Id INT IDENTITY(1, 1) PRIMARY KEY CLUSTERED,
    Scotch VARCHAR(50),
    HaggisAddress VARBINARY(MAX)
);

DECLARE @AddressToAVarbinaryHaggis VARBINARY(MAX); 
DECLARE @AddressToAHaggis NVARCHAR(MAX) = N'
Good luck to you and your honest, plump face,
Great chieftain of the pudding race!
Above them all you take your place,
        gut, stomach-lining, or intestine,
You''re well worth a grace
        as long as my arm.

The overloaded serving tray there you fill,
Your buttocks shaped like a distant hilltop,
Your wooden skewer could be used to fix a mill
         if need be,
While through your pores your juices drip
         like liquid gold.

His knife see the serving-man clean,
And then cut you up with great skill,
Making a trench in your bright, gushing guts
        To form a ditch,
And then, 0h! What a glorious sight!
        Warm, steaming, and rich!

Then, spoonful after spoonful, they eagerly eat,
The devil will get the last bit, on they go,
Until all their well-stretched stomachs, by-and-by,
        are bent like drums,
Then the head of the family, about to burst,
        murmurs “Thank the Lord".

Is there a pretentious soul who, over his French ragout,
Or Italian cuisine that would make a pig sick,
Or French stew that would make that same pig ill
        with complete and utter disgust,
Looks down with a sneering, scornful attitude,
        on such a meal? (as Haggis)

Poor devil! See him over his trash!
As feeble as a withered bullrush,
His skinny leg no thicker than a thin rope,
        His fist the size of a nut,
Through a river or field to travel,
        Completely unfit!

But look at the healthy, Haggis-fed person!
The trembling earth respects him as a man!
Put a knife in his fist,
        He''ll make it work!
And legs, and arms, and heads will come off,
        Like the tops of thistle.

You Powers who look after mankind,
And dish out his bill of fare,
Old Scotland wants no watery, wimpy stuff
        That splashes about in little wooden bowls!
But, if You will grant her a grateful prayer,
        Give her a Haggis!';


INSERT dbo.RobertBurns (Scotch, HaggisAddress )
SELECT TOP 1000 
CASE WHEN x.c % 15 = 0 THEN 'Laphroaig'
     WHEN x.c % 5 = 0 THEN 'Lagavulin'
     WHEN x.c % 3 = 0 THEN 'Port Ellen'
     ELSE 'Ardbeg'
END AS Scotch, 
CONVERT(VARBINARY(MAX), REPLICATE(@AddressToAHaggis, x.c % 20 + 1))
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY @@ROWCOUNT) AS c
FROM sys.messages AS m
) AS x;

CREATE INDEX ix_novarbinary ON  dbo.RobertBurns (Scotch, Id);
CREATE INDEX ix_yesvarbinary ON dbo.RobertBurns (Scotch, Id) INCLUDE (HaggisAddress);

Com as linhas inseridas, vamos verificar nossas páginas de índice.

SELECT   OBJECT_NAME(i.object_id) AS table_name,
         i.name AS index_name,
         MAX(a.used_pages) AS leaf_me_alone
FROM     sys.indexes AS i
JOIN     sys.partitions AS p
ON p.object_id = i.object_id
   AND p.index_id = i.index_id
JOIN     sys.allocation_units AS a
ON a.container_id = p.partition_id
WHERE OBJECT_NAME(i.object_id) = 'RobertBurns'
GROUP BY i.object_id, i.index_id, i.name
ORDER BY OBJECT_NAME(i.object_id), i.index_id;

Após a inserção, eu entendo isso. As páginas reais podem variar para você.

table_name  index_name                      leaf_me_alone
RobertBurns PK__RobertBu__3214EC074BE633A2  5587
RobertBurns ix_novarbinary                  10
RobertBurns ix_yesvarbinary                 5581

Vamos NULLsair algumas linhas!

UPDATE rb
    SET rb.HaggisAddress = NULL
FROM dbo.RobertBurns AS rb
WHERE rb.Id % 15 = 0;

E volte em nossas páginas:

table_name  index_name                      leaf_me_alone
RobertBurns PK__RobertBu__3214EC074BE633A2  5300
RobertBurns ix_novarbinary                  10
RobertBurns ix_yesvarbinary                 5273

Portanto, a contagem de páginas foi reduzida. Huzzah! Para os dois índices que tocam nossos VARBINARYdados, eles perderam uma página de buncha. Isso significa que eles estão de volta à circulação para outros objetos usarem. Já que estou no tempdb, eles provavelmente são engolidos rapidamente por todas as coisas de lixo que acontecem aqui.

Agora vamos colocar alguns dados novamente em:

INSERT dbo.RobertBurns (Scotch, HaggisAddress )
SELECT TOP 10 rb.Scotch, rb.HaggisAddress
FROM dbo.RobertBurns AS rb;

E retornando:

table_name  index_name                      leaf_me_alone
RobertBurns PK__RobertBu__3214EC074BE633A2  5330
RobertBurns ix_novarbinary                  11
RobertBurns ix_yesvarbinary                 5305

A contagem de páginas aumentou um pouco.

Portanto, parece que você não precisa fazer nada muito louco ou até mesmo reduzir seu banco de dados para obter reutilização de espaço. Eu acho que você estava confundindo o comportamento de soltar colunas e precisando executar DBCC CLEANTABLEo que realmente está fazendo.

Espero que isto ajude!

Erik Darling
fonte