Ich muss meine Datenbank verkleinern - Ich habe gerade viel Speicherplatz freigegeben


35

Diese Frage wird in verschiedenen Formen hier gestellt, aber die Frage läuft auf Folgendes hinaus:

Ich weiß, dass das Verkleinern einer Datenbank riskant ist. In diesem Fall habe ich so viele Daten entfernt, dass ich sie nie wieder verwenden werde.

  • Wie kann ich meine Datenbank verkleinern? Welche Dateien verkleinere ich?
  • Was sollten meine Überlegungen dabei sein?
  • Soll ich danach noch etwas unternehmen?
  • Was ist, wenn es sich um eine große Datenbank handelt? Kann ich es in kleineren Schritten verkleinern?

2
Ich hatte vor einiger Zeit Probleme damit : dba.stackexchange.com/questions/47310/… Ich habe versucht, meine Erfahrungen in meiner Antwort zusammenzufassen
Csaba Toth

Antworten:


30

Einige Vorsichtsmaßnahmen:

  1. Es ist allgemein als die schlechteste Vorgehensweise bekannt, eine Produktionsdatenbank oder -datei jemals zu verkleinern (Protokolldateien sind ein weiteres Problem, um das es in dieser Frage geht ). Ich rate den Leuten, ihre Datenbanken in Blog-Posts wie diesen nicht zu verkleinern, in denen es um "richtige Größe" und gute Planung geht. Ich bin nicht allein dort ( Paul Randal , Brent Ozar , nur um ein paar weitere Links bereitzustellen). Das Verkleinern einer Datendatei oder von Datenbankfragmentindizes ist langsam und mühsam für Ihre Ressourcen, kann Ihr System belasten und ist im Allgemeinen nur eine schlechte Sache
  2. In diesem Fall wissen wir alle, dass das Risiko besteht, wir sind bereit, uns damit auseinanderzusetzen, aber wir haben viel Platz frei gemacht, von dem wir wissen, dass wir ihn nie wieder brauchen werden. In diesem speziellen Fall ist das Schrumpfen als eine unserer Optionen sehr sinnvoll.

Wenn Sie sich über die Sorgen und Risiken gelesen haben und Sie immer noch diesen Schrumpf tun müssen , weil Sie eine befreit erhebliche Menge an Speicherplatz, hoffentlich der Rest dieser Antwort wird Ihnen helfen. Aber bedenken Sie die Risiken.

Es gibt zwei Hauptansätze, die zwei hier berücksichtigen:

1.) Verkleinern Ja, machen Sie das eigentliche Verkleinern - Ziehen Sie in Betracht, DBCC SHRINKFILEstatt zu verwenden DBCC SHRINKDATABASE, haben Sie mehr Kontrolle darüber, was und wie geschrumpft wird. Dies führt mit Sicherheit zu Leistungseinbußen - es handelt sich um eine umfangreiche Operation, die viel E / A-Aufwand verursacht. Sie können möglicherweise mit wiederholten Verkleinerungen auf eine Zielgröße, die immer kleiner wird, davonkommen.

Dies ist das "A.)" - Beispiel im obigen DBCC SHRINKFILELink. In diesem Beispiel wird eine Datendatei auf eine Zielgröße von 7 MB verkleinert. Dieses Format ist ein guter Weg, um wiederholt zu verkleinern, wenn Ihr Ausfallzeitfenster dies zulässt. Ich würde dies beim Testen der Entwicklung tun, um zu sehen, wie die Leistung aussieht und wie hoch / niedrig Sie ein Inkrement einstellen können, und um den erwarteten Zeitpunkt in der Produktion zu bestimmen. Dies ist ein Online- Vorgang - Sie können ihn mit Benutzern im System ausführen, die auf die Datenbank zugreifen, die verkleinert wird, aber es kommt fast garantiert zu Leistungseinbußen. Überwachen und beobachten Sie also, was Sie mit dem Server tun, und wählen Sie im Idealfall ein Ausfallzeitfenster oder eine Zeitspanne geringerer Aktivität aus.

USE YourDatabase;
GO
DBCC SHRINKFILE (DataFile1, 7);
GO

Immer daran denken: Sie - Bei jedem Verkleinern fragmentieren Sie Ihre Indizes und sollten einen Indexneubau durchführen, wenn Sie über einen längeren Zeitraum Teile verkleinern möchten. Diese Kosten fallen jetzt jedes Mal an, wenn Sie nicht alles in einem Fenster erledigen können.

2.) Neue Datenbank - Sie können eine neue Datenbank erstellen und Daten in diese migrieren. Sie müssten die leere Datenbank mit allen Schlüsseln, Indizes, Objekten, Prozessen, Funktionen usw. per Skript auslesen und dann Daten in sie migrieren. Sie könnten Skripte dafür schreiben oder ein Tool wie SQL Data Compare von Red Gate oder anderen Anbietern mit ähnlichen Tools verwenden. Dies ist mehr Einrichtungsarbeit auf Ihrer Seite, mehr Entwicklung und Testen, und abhängig von Ihrer Umgebung kann auch das Ausfallzeitfenster ausgeblasen werden, es ist jedoch eine zu berücksichtigende Option.

Wenn ich gezwungen bin , eine Datenbank zu verkleinern Wenn dies meine Umgebung wäre, würde ich versuchen, eine angemessene Menge an Leerraum in der Datendatei zu belassen, da ich gerne ein Festplattenfresser bin und auf zukünftiges / unerwartetes Wachstum vorbereitet bin. Wenn wir nur einen Großteil des Speicherplatzes löschen würden, wäre es in Ordnung, Platz zurückzugeben, aber ich würde diesen Worten niemals vertrauen, "aber es wird nie wieder wachsen", und trotzdem etwas Leerraum lassen. Die Route, mit der ich wahrscheinlich fahren würde ( seufz) ) ist der Schrumpf Ansatz , wenn ich kleinere Ausfallzeiten Fenster hatte und Ich wollte nicht die Komplexität aufbringen, eine leere Datenbank zu erstellen und Daten in diese zu migrieren. Also habe ich es ein paar Mal inkrementell verkleinert (basierend darauf, wie oft ich dachte, ich müsste basierend auf meinen Tests in dev und der gewünschten Größe nach und nach eine kleinere Dateigröße auswählen) und dann die Indizes neu erstellt. Und dann habe ich‘ würde niemals jemandem sagen, dass ich meine Datenbank verkleinert habe ;-)


1
Ich würde den Sonderfall hinzufügen, dass, wenn Sie viele Daten von einem Heap gelöscht haben (insbesondere aus der Mitte des Heaps), Sie diesen Speicherplatz erst dann zurückerhalten können, wenn Sie einen Clustered-Index hinzufügen (hoffentlich für immer). und löschen Sie den Clustered-Index anschließend (und verwandeln Sie ihn wieder in einen Heap). Wenn der Haufen regelmäßig abgeschnitten wird, gibt es natürlich keine Bedenken. Aber immer noch erwähnenswert.
Jonathan Fite

Kann jemand die Auswirkungen von NOTRUNCATE AND TRUNCATEONLY erklären, anscheinend ordnet letzteres die Seiten nicht neu und verursacht daher keine Indexfragmentierung?
David Garcia

4
  1. Wie kann ich meine Datenbank verkleinern? Welche Dateien verkleinere ich? : Sie können die Dateien mit dem angegebenen DBCC SHRINKFILEBefehl einzeln verkleinern . Es hängt von Ihrem Server ab, aus wie vielen Dateien Ihre Datenbank besteht. Eine einfache Datenbank verfügt über eine Datenbankdatei und eine Transaktionsprotokolldatei.
  2. Was sollten meine Überlegungen dabei sein?: Die Verkleinerung wirkt sich auf Ihre Indexfragmentierung aus, siehe 3. Punkt. Beachten Sie auch, dass Sie die Datenbankdatei nicht auf eine minimale Größe verkleinern möchten, da sie in einer realen Umgebung sowieso größer wird. Daher würde ich die Größe (in Ihrem Beispiel haben Sie 7 Megabyte angegeben) so anpassen, dass in der Datenbankdatei 10% bis 20% freier Speicherplatz verbleibt, da diese sowieso in der Produktionsumgebung gefüllt ist und Sie dies können Sparen Sie auf diese Weise einige Auto-Wachstums-Zyklen. Die tatsächliche Anzahl muss also sorgfältig berechnet werden. Beachten Sie außerdem, dass die von Ihnen durchgeführte "große Speicherplatzfreigabe" die Transaktionsprotokolldatei noch mehr aufbläht als den Speicherplatz, den Sie in der DB-Datei gewonnen haben. Außerdem wird der tatsächliche Raumgewinn, den Sie möglicherweise erfahren, unter dem liegen, was Sie rechnerisch erwarten! Nehmen wir also an, Sie haben mathematisch 12 Konzerte frei,
  3. Soll ich danach noch etwas unternehmen? : Wie bereits erwähnt, möchten Sie die Indizes neu indizieren, deren Fragmentierung aufgrund der Änderungen von SHRINK verzerrt wurde. Ich habe nicht genug experimentiert, wenn Sie etwas Besonderes in Bezug auf Abfragestatistiken tun müssen.
  4. Was ist, wenn es sich um eine große Datenbank handelt? Kann ich es in kleineren Schritten verkleinern? Der SHRINK-Vorgang kann jederzeit unterbrochen werden und Sie können später fortfahren. Ich würde empfehlen, es in einer Offline-Datenbank durchzuführen, wenn dies möglich ist. Durch Unterbrechen und Fortsetzen würde es jedoch die gleiche Schrumpfgröße erreichen. Theoretisch können Sie in kleineren Schritten verkleinern, indem Sie eine weniger enge Zielgröße anstelle von 7 Megabyte angeben, aber ich würde sagen, wenn Sie es in der Produktion ausführen, geben Sie es einfach einmal. Wie Sie sehen, gibt es Probleme mit der Indexfragmentierung und dem möglichen Wachstum des Transaktionsprotokolls. Also würde ich das nur einmal durchgehen.

Wir alle wissen, dass es sowieso nicht ratsam ist, regelmäßig SHRINK zu machen. Ich versuche, alle Warnungen und Haftungsausschlüsse, die Sie wahrscheinlich sowieso kennen, wegzulassen. Backup, und mach das nicht zu Hause wenn möglich :)

Bonus: Wenn Sie dies in der Replikationsumgebung für die Publisher-Datenbank ausführen, werden die Abonnentendatenbanken nicht verkleinert (was möglicherweise zu Größenproblemen führen kann, da es sich um Express-Editionen handelt).

Zum Schluss mein Reindex-Skript:

USE YourDBName

DECLARE @TbName VARCHAR(255)
DECLARE @FullTbName VARCHAR(255)
DECLARE @IxName VARCHAR(255)
DECLARE myCursor CURSOR FOR
    SELECT OBJECT_NAME(dmi.object_id) AS TableName,i.name AS IndexName
    FROM sys.dm_db_index_physical_stats(14, NULL, NULL, NULL , 'LIMITED') dmi
    JOIN  sys.indexes i on dmi.object_id = i.object_id and dmi.index_id = i.index_id
    WHERE avg_fragmentation_in_percent > 30
    ORDER BY avg_fragmentation_in_percent
OPEN myCursor
FETCH NEXT FROM myCursor INTO @TbName, @ixName
WHILE @@FETCH_STATUS = 0
BEGIN
    IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES  WHERE TABLE_SCHEMA = 'dba' AND TABLE_NAME = @TbName)
BEGIN
        SET @FullTbName = 'dba.' + @TbName
        IF (@ixName IS NULL)
        BEGIN
            PRINT 'Reindexing Table ' + @FullTbName
            DBCC DBREINDEX(@FullTbName, '', 0)
        END
        ELSE
        BEGIN
             PRINT 'Reindexing Table ' + @FullTbName + ', Index ' + @IxName
             DBCC DBREINDEX(@FullTbName, @IxName, 0)
        END
    END
    FETCH NEXT FROM myCursor INTO @TbName, @ixName
END
CLOSE myCursor
DEALLOCATE myCursor

Die einzige Variable ist die 14, die durch Ausgabe von select abgerufen werden kann DB_ID('YourDBName'), und das Skript geht davon aus, dass Sie nur an den Tabellen im Schema dba. * Interessiert sind.


2
Beachten Sie für die Neuerstellung des Index, dass DBREINDEX in SQL 2005 veraltet ist. Statt des umfangreichen Skripts mit Cursorn können Sie einfach Folgendes verwenden: EXEC sp_MSForeachtable @ Command1 = "ALTER INDEX ALL ON? REBUILD" Hoffe, dies hilft jemandem.
KISS

2

Sie haben alle Warnungen über das Verkleinern von Datenbanken gehört und sie sind alle wahr. Es fragmentiert Ihre Indizes und ist im Allgemeinen, Ihre Datenbank durcheinander und sollte nicht auf einem Produktionssystem durchgeführt werden.

Normalerweise mache ich das jedoch wöchentlich, wenn ich aufgrund des Speicherplatzes auf meinem SSD-Laufwerk ein Backup auf meiner Workstation wiederherstelle. Wohlgemerkt, ich habe dieses Skript nicht geschrieben, sondern vor Jahren gefunden. Auf anderen Datenbanken [250 GB] habe ich ein SSIS-Paket erstellt, das die benötigten Tabellen überträgt und dann die Indizes für dieses ach so frische Indexgefühl neu erstellt.

DECLARE @DBFileName SYSNAME

DECLARE @TargetFreeMB INT

DECLARE @ShrinkIncrementMB INT

SET @DBFileName = 'Set Name of Database file to shrink'

-- Set Desired file free space in MB after shrink

SET @TargetFreeMB = 500
-- Set Increment to shrink file by in MB
SET @ShrinkIncrementMB = 100

SELECT [FileSizeMB] = convert(NUMERIC(10, 2),
round(a.size / 128., 2)),

[UsedSpaceMB] = convert(NUMERIC(10, 2),

round(fileproperty(a.NAME, 'SpaceUsed') / 128., 2)),

[UnusedSpaceMB] = convert(NUMERIC(10, 2),

round((a.size - fileproperty(a.NAME, 'SpaceUsed')) / 128., 2)),

[DBFileName] = a.NAME

FROM sysfiles a

DECLARE @sql VARCHAR(8000)
DECLARE @SizeMB INT
DECLARE @UsedMB INT

SELECT @SizeMB = size / 128.
FROM sysfiles
WHERE NAME = @DBFileName

SELECT @UsedMB = fileproperty(@DBFileName, 'SpaceUsed') / 128.

SELECT [StartFileSize] = @SizeMB
    ,[StartUsedSpace] = @UsedMB
    ,[DBFileName] = @DBFileName

WHILE @SizeMB > @UsedMB + @TargetFreeMB + @ShrinkIncrementMB

BEGIN
    SET @sql = 'dbcc shrinkfile ( ' + @DBFileName + ', ' + convert(VARCHAR(20), @SizeMB - @ShrinkIncrementMB) + ' ) '

    PRINT 'Start ' + @sql
    PRINT 'at ' + convert(VARCHAR(30), getdate(), 121)

    EXEC (@sql)

    PRINT 'Done ' + @sql
    PRINT 'at ' + convert(VARCHAR(30), getdate(), 121)

    SELECT @SizeMB = size / 128.
    FROM sysfiles
    WHERE NAME = @DBFileName

    SELECT @UsedMB = fileproperty(@DBFileName, 'SpaceUsed') / 128.

    SELECT [FileSize] = @SizeMB
        ,[UsedSpace] = @UsedMB
        ,[DBFileName] = @DBFileName
END

SELECT [EndFileSize] = @SizeMB
    ,[EndUsedSpace] = @UsedMB
    ,[DBFileName] = @DBFileName

SELECT [FileSizeMB] = convert(NUMERIC(10, 2), round(a.size / 128., 2))

    ,[UsedSpaceMB] = convert(NUMERIC(10, 2), round(fileproperty a.NAME, 'SpaceUsed') / 128., 2))

,[UnusedSpaceMB] = convert(NUMERIC(10, 2), round((a.size - fileproperty(a.NAME, 'SpaceUsed')) / 128., 2))

,[DBFileName] = a.NAME

FROM sysfiles a

1

Das folgende Zitat stammt direkt von Microsoft (gilt für die Versionen 2008-2016) und gibt eine Anleitung, ob / wann und wie Sie den DBCC SHRINKFILEBefehl verwenden sollten.

https://msdn.microsoft.com/en-us/library/ms189493.aspx

Best Practices

Berücksichtigen Sie die folgenden Informationen, wenn Sie eine Datei verkleinern möchten:

  • Ein Verkleinerungsvorgang ist am effektivsten nach einem Vorgang, bei dem viel ungenutzter Speicherplatz erstellt wird, z.
  • Für die meisten Datenbanken ist freier Speicherplatz für den täglichen Betrieb erforderlich. Wenn Sie eine Datenbank wiederholt verkleinern und feststellen, dass die Datenbankgröße erneut zunimmt, ist dies ein Hinweis darauf, dass der verkleinerte Speicherplatz für den regulären Betrieb erforderlich ist. In diesen Fällen ist das wiederholte Verkleinern der Datenbank eine Verschwendung.
  • Bei einem Verkleinerungsvorgang bleibt der Fragmentierungsstatus von Indizes in der Datenbank nicht erhalten, und die Fragmentierung wird im Allgemeinen bis zu einem gewissen Grad erhöht. Dies ist ein weiterer Grund, die Datenbank nicht wiederholt zu verkleinern.
  • Verkleinern Sie mehrere Dateien in derselben Datenbank nacheinander anstatt gleichzeitig. Konflikte in Systemtabellen können zu Verzögerungen aufgrund von Blockierungen führen.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.