Hantera filutrymme för databaser i Azure SQL Database

Gäller för:Azure SQL Database

Den här artikeln beskriver olika typer av lagringsutrymme för databaser i Azure SQL Database och steg som kan vidtas när det allokerade filutrymmet måste hanteras explicit.

Översikt

Med Azure SQL Database finns det arbetsbelastningsmönster där allokeringen av underliggande datafiler för databaser kan bli större än antalet använda datasidor. Det här villkoret kan inträffa när utrymmet som används ökar och data tas bort senare. Det beror på att tilldelat filutrymme inte återkallas automatiskt när data tas bort.

I följande scenarier kan det vara nödvändigt att övervaka användningen av filutrymmet och att krympa datafiler:

  • Tillåt datatillväxt i en elastisk pool när filutrymmet som tilldelats dess databaser når poolens maxstorlek.
  • Tillåt att maxstorleken för en enskild databas eller elastisk pool minskas.
  • Tillåt att en enskild databas eller elastisk pool ändras till en annan tjänstnivå eller prestandanivå med en mindre maxstorlek.

Kommentar

Krympningsåtgärder bör inte betraktas som en vanlig underhållsåtgärd. Data och loggfiler som växer på grund av regelbundna, återkommande affärsåtgärder kräver inte krympningsåtgärder.

Övervaka filutrymmesanvändning

De flesta mått för lagringsutrymme som visas i följande API:er mäter bara storleken på använda datasidor:

  • Azure Resource Manager-baserade mått-API:er inklusive PowerShell-get-metrics

Följande API:er mäter dock även storleken på allokerat utrymme för databaser och elastiska pooler:

Förstå typer av lagringsutrymme för en databas

Det är viktigt att förstå följande lagringsutrymmeskvantiteter för att hantera filutrymmet i en databas.

Databaskvantitet Definition Kommentarer
Datautrymme som används Mängden utrymme som används för att lagra databasdata. I allmänhet ökar utrymmet som används (minskar) vid infogningar (borttagningar). I vissa fall ändras inte det utrymme som används vid infogningar eller borttagningar beroende på mängden och mönstret för data som ingår i åtgärden och eventuell fragmentering. Om en rad tas bort från alla datasidor minskar till exempel inte nödvändigtvis utrymmet som används.
Allokerat datautrymme Mängden formaterat filutrymme som görs tillgängligt för lagring av databasdata. Mängden allokerat utrymme växer automatiskt, men minskar aldrig efter borttagningar. Det här beteendet säkerställer att framtida infogningar går snabbare eftersom utrymmet inte behöver formateras om.
Allokerat datautrymme men oanvänt Skillnaden mellan mängden datautrymme som har allokerats och mängden datautrymme som används. Den här kvantiteten representerar den maximala mängden ledigt utrymme som kan frigöras genom krympande databasdatafiler.
Maximal datastorlek Den maximala mängden utrymme som kan användas för att lagra databasdata. Mängden allokerat datautrymme kan inte bli större än den maximala datastorleken.

Följande diagram illustrerar relationen mellan de olika typerna av lagringsutrymme för en databas.

Diagram that demonstrates the size of difference database space concepts in the database quantity table.

Fråga en enskild databas om information om filutrymme

Använd följande fråga på sys.database_files för att returnera mängden allokerat databasfilutrymme och mängden oanvänt utrymme som allokerats. Enheter i frågeresultatet är i MB.

-- Connect to a user database
SELECT file_id, type_desc,
       CAST(FILEPROPERTY(name, 'SpaceUsed') AS decimal(19,4)) * 8 / 1024. AS space_used_mb,
       CAST(size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0 AS decimal(19,4)) AS space_unused_mb,
       CAST(size AS decimal(19,4)) * 8 / 1024. AS space_allocated_mb,
       CAST(max_size AS decimal(19,4)) * 8 / 1024. AS max_size_mb
FROM sys.database_files;

Förstå typer av lagringsutrymme för en elastisk pool

Det är viktigt att förstå följande lagringsutrymmeskvantiteter för att hantera filutrymmet i en elastisk pool.

Kvantitet för elastisk pool Definition Kommentarer
Datautrymme som används Sammanfattning av datautrymmet som används av alla databaser i den elastiska poolen.
Allokerat datautrymme Sammanfattning av datautrymmet som har allokerats av alla databaser i den elastiska poolen.
Allokerat datautrymme men oanvänt Skillnaden mellan mängden datautrymme som har allokerats och mängden datautrymme som används av alla databaser i den elastiska poolen. Den här kvantiteten representerar den maximala mängden utrymme som allokerats för den elastiska poolen som kan frigöras genom krympande databasdatafiler.
Maximal datastorlek Den maximala mängden datautrymme som kan användas av den elastiska poolen för alla dess databaser. Det utrymme som har allokerats för den elastiska poolen får inte överskrida maxstorleken för den elastiska poolen. Om det här villkoret inträffar kan det utrymme som allokeras som inte används frigöras genom krympande databasdatafiler.

Kommentar

Felmeddelandet "Den elastiska poolen har nått sin lagringsgräns" anger att databasobjekten har allokerats tillräckligt med utrymme för att uppfylla lagringsgränsen för elastisk pool, men det kan finnas outnyttjat utrymme i allokeringen av datautrymmet. Överväg att öka den elastiska poolens lagringsgräns, eller som en kortsiktig lösning, frigöra datautrymme med hjälp av exemplen i Frigöra oanvänt allokerat utrymme. Du bör också vara medveten om den potentiella negativa prestandapåverkan av krympande databasfiler, se Indexunderhåll efter krympning.

Fråga efter information om lagringsutrymme i en elastisk pool

Följande frågor kan användas för att fastställa det tillgängliga lagringsutrymmet för en elastisk pool.

Använt datautrymme för elastisk pool

Ändra följande fråga för att returnera mängden elastiskt pooldatautrymme som används. Enheter i frågeresultatet är i MB.

-- Connect to master
-- Elastic pool data space used in MB  
SELECT TOP 1 avg_storage_percent / 100.0 * elastic_pool_storage_limit_mb AS ElasticPoolDataSpaceUsedInMB
FROM sys.elastic_pool_resource_stats
WHERE elastic_pool_name = 'ep1'
ORDER BY end_time DESC;

Elastiskt pooldatautrymme allokerat och oanvänt allokerat utrymme

Ändra följande exempel för att returnera en tabell som visar det allokerade och oanvända allokerade utrymmet för varje databas i en elastisk pool. Tabellen beställer databaser från dessa databaser med den största mängden oanvänt allokerat utrymme till den minsta mängden oanvänt allokerat utrymme. Enheter i frågeresultatet är i MB.

Frågeresultaten för att fastställa det tilldelade utrymmet för varje databas i poolen kan läggas till tillsammans för att fastställa det totala utrymmet som allokerats för den elastiska poolen. Det elastiska poolutrymme som allokerats får inte överskrida maxstorleken för den elastiska poolen.

Viktigt!

PowerShell Azure Resource Manager-modulen stöds fortfarande av Azure SQL Database, men all framtida utveckling gäller för Az.Sql-modulen. AzureRM-modulen fortsätter att ta emot felkorrigeringar fram till åtminstone december 2020. Argumenten för kommandona i Az-modulen och i AzureRm-modulerna är väsentligen identiska. Mer information om deras kompatibilitet finns i Introduktion till den nya Azure PowerShell Az-modulen.

PowerShell-skriptet kräver SQL Server PowerShell-modul – se Ladda ned PowerShell-modulen för att installera.

$resourceGroupName = "<resourceGroupName>"
$serverName = "<serverName>"
$poolName = "<poolName>"
$userName = "<userName>"
$password = "<password>"

# get list of databases in elastic pool
$databasesInPool = Get-AzSqlElasticPoolDatabase -ResourceGroupName $resourceGroupName `
    -ServerName $serverName -ElasticPoolName $poolName
$databaseStorageMetrics = @()

# for each database in the elastic pool, get space allocated in MB and space allocated unused in MB
foreach ($database in $databasesInPool) {
    $sqlCommand = "SELECT DB_NAME() as DatabaseName, `
    SUM(size/128.0) AS DatabaseDataSpaceAllocatedInMB, `
    SUM(size/128.0 - CAST(FILEPROPERTY(name, 'SpaceUsed') AS int)/128.0) AS DatabaseDataSpaceAllocatedUnusedInMB `
    FROM sys.database_files `
    GROUP BY type_desc `
    HAVING type_desc = 'ROWS'"
    $serverFqdn = "tcp:" + $serverName + ".database.windows.net,1433"
    $databaseStorageMetrics = $databaseStorageMetrics + 
        (Invoke-Sqlcmd -ServerInstance $serverFqdn -Database $database.DatabaseName `
            -Username $userName -Password $password -Query $sqlCommand)
}

# display databases in descending order of space allocated unused
Write-Output "`n" "ElasticPoolName: $poolName"
Write-Output $databaseStorageMetrics | Sort -Property DatabaseDataSpaceAllocatedUnusedInMB -Descending | Format-Table

Följande skärmbild är ett exempel på utdata från skriptet:

A screenshot of the output of the related PowerShell cmdlet, showing elastic pool allocated space and unused allocated space.

Maximal datastorlek för elastisk pool

Ändra följande T-SQL-fråga för att returnera den senast registrerade maximala storleken för elastiska pooldata. Enheter i frågeresultatet är i MB.

-- Connect to master
-- Elastic pools max size in MB
SELECT TOP 1 elastic_pool_storage_limit_mb AS ElasticPoolMaxSizeInMB
FROM sys.elastic_pool_resource_stats
WHERE elastic_pool_name = 'ep1'
ORDER BY end_time DESC;

Återkalla outnyttjat allokerat utrymme

Viktigt!

Krympkommandon påverkar databasens prestanda när den körs och bör om möjligt köras under perioder med låg användning.

Krympa datafiler

På grund av en potentiell inverkan på databasprestandan krymper inte Azure SQL Database automatiskt datafiler. Kunder kan dock krympa datafiler via självbetjäning vid valfri tidpunkt. Detta bör inte vara en regelbundet schemalagd åtgärd, utan snarare en engångshändelse som svar på en stor minskning av datafilens användning av utrymmesförbrukning.

Dricks

Vi rekommenderar inte att du krymper datafilerna om en vanlig programarbetsbelastning gör att filerna växer till samma allokerade storlek igen.

Om du vill krympa filer i Azure SQL Database kan du använda antingen DBCC SHRINKDATABASE eller DBCC SHRINKFILE kommandon:

  • DBCC SHRINKDATABASE krymper alla data och loggfiler i en databas med hjälp av ett enda kommando. Kommandot krymper en datafil i taget, vilket kan ta lång tid för större databaser. Loggfilen krymps också, vilket vanligtvis är onödigt eftersom Azure SQL Database krymper loggfilerna automatiskt efter behov.
  • DBCC SHRINKFILE kommandot stöder mer avancerade scenarier:
    • Den kan rikta in sig på enskilda filer efter behov i stället för att krympa alla filer i databasen.
    • Varje DBCC SHRINKFILE kommando kan köras parallellt med andra DBCC SHRINKFILE kommandon för att krympa flera filer samtidigt och minska den totala tiden för krympning, på bekostnad av högre resursanvändning och en högre chans att blockera användarfrågor, om de körs under krympning.
    • Om filens svans inte innehåller data kan den minska den allokerade filstorleken TRUNCATEONLY mycket snabbare genom att ange argumentet. Detta kräver inte dataförflyttning i filen.
  • Mer information om dessa krympkommandon finns i DBCC SHRINKDATABASE och DBCC SHRINKFILE.

Följande exempel måste köras när de är anslutna till målanvändardatabasen, inte databasen master .

Så här använder DBCC SHRINKDATABASE du för att krympa alla data och loggfiler i en viss databas:

-- Shrink database data space allocated.
DBCC SHRINKDATABASE (N'database_name');

I Azure SQL Database kan en databas ha en eller flera datafiler som skapas automatiskt när data växer. Om du vill fastställa databasens fillayout, inklusive den använda och allokerade storleken på varje fil, frågar sys.database_files du katalogvyn med hjälp av följande exempelskript:

-- Review file properties, including file_id and name values to reference in shrink commands
SELECT file_id,
       name,
       CAST(FILEPROPERTY(name, 'SpaceUsed') AS bigint) * 8 / 1024. AS space_used_mb,
       CAST(size AS bigint) * 8 / 1024. AS space_allocated_mb,
       CAST(max_size AS bigint) * 8 / 1024. AS max_file_size_mb
FROM sys.database_files
WHERE type_desc IN ('ROWS','LOG');

Du kan bara köra en krympning mot en fil via DBCC SHRINKFILE kommandot, till exempel:

-- Shrink database data file named 'data_0` by removing all unused at the end of the file, if any.
DBCC SHRINKFILE ('data_0', TRUNCATEONLY);
GO

Tänk på den potentiella negativa prestandapåverkan av krympande databasfiler. Mer information finns i Indexunderhåll efter krympning.

Krymp transaktionsloggfil

Till skillnad från datafiler krymper Azure SQL Database automatiskt transaktionsloggfilen för att undvika överdriven utrymmesanvändning som kan leda till fel på grund av att utrymmet är slut. Kunder behöver vanligtvis inte krympa transaktionsloggfilen.

Om transaktionsloggen blir stor i Premium- och Affärskritisk tjänstnivåer kan den avsevärt bidra till den lokala lagringsförbrukningen mot den maximala lokala lagringsgränsen. Om den lokala lagringsförbrukningen ligger nära gränsen kan kunderna välja att krympa transaktionsloggen med hjälp av kommandot DBCC SHRINKFILE enligt följande exempel. Detta frigör lokal lagring så snart kommandot har slutförts, utan att vänta på den periodiska automatiska krympningsåtgärden.

Följande exempel ska köras när du är ansluten till målanvändardatabasen, inte databasen master .

-- Shrink the database log file (always file_id 2), by removing all unused space at the end of the file, if any.
DBCC SHRINKFILE (2, TRUNCATEONLY);

Krymp automatiskt

Som ett alternativ till att krympa datafiler manuellt kan automatisk krympning aktiveras för en databas. Automatisk krympning kan dock vara mindre effektivt när det gäller att frigöra filutrymme än DBCC SHRINKDATABASE och DBCC SHRINKFILE.

Som standard inaktiveras automatisk krympning, vilket rekommenderas för de flesta databaser. Om det blir nödvändigt att aktivera automatisk krympning rekommenderar vi att du inaktiverar det när målen för utrymmeshantering har uppnåtts, i stället för att behålla det permanent. Mer information finns i Överväganden för AUTO_SHRINK.

Till exempel kan automatisk krympning vara användbart i det specifika scenariot där en elastisk pool innehåller många databaser som upplever betydande tillväxt och minskning av det datafilutrymme som används, vilket gör att poolen närmar sig sin maximala storleksgräns. Detta är inte ett vanligt scenario.

Om du vill aktivera automatisk krympning kör du följande kommando när du är ansluten till databasen (inte master databasen).

-- Enable auto-shrink for the current database.
ALTER DATABASE CURRENT SET AUTO_SHRINK ON;

Mer information om det här kommandot finns i ALTERNATIV FÖR DATABASUPPSÄTTNING.

Indexunderhåll efter krympning

När en krympningsåtgärd har slutförts mot datafiler kan index bli fragmenterade. Detta minskar deras prestandaoptimeringseffektivitet för vissa arbetsbelastningar, till exempel frågor som använder stora genomsökningar. Om prestandaförsämringen inträffar när krympningsåtgärden är klar bör du överväga indexunderhåll för att återskapa index. Tänk på att ombyggnad av index kräver ledigt utrymme i databasen, och därför kan det allokerade utrymmet öka, vilket motverkar effekten av krympning.

Mer information om indexunderhåll finns i Optimera indexunderhåll för att förbättra frågeprestanda och minska resursförbrukningen.

Krympa stora databaser

När databasens allokerade utrymme är i hundratals gigabyte eller högre kan krympning kräva en betydande tid att slutföra, ofta mätt i timmar eller dagar för databaser med flera terabyte. Det finns processoptimeringar och metodtips som du kan använda för att göra den här processen mer effektiv och mindre påverkande för programarbetsbelastningar.

Avbildningsbaslinje för utrymmesanvändning

Innan du börjar krympa samlar du in aktuellt använt och allokerat utrymme i varje databasfil genom att köra följande fråga om utrymmesanvändning:

SELECT file_id,
       CAST(FILEPROPERTY(name, 'SpaceUsed') AS bigint) * 8 / 1024. AS space_used_mb,
       CAST(size AS bigint) * 8 / 1024. AS space_allocated_mb,
       CAST(max_size AS bigint) * 8 / 1024. AS max_size_mb
FROM sys.database_files
WHERE type_desc = 'ROWS';

När krympningen har slutförts kan du köra den här frågan igen och jämföra resultatet med den första baslinjen.

Trunkera datafiler

Vi rekommenderar att du först kör shrink för varje datafil med parametern TRUNCATEONLY . På så sätt, om det finns något allokerat men oanvänt utrymme i slutet av filen, tas det bort snabbt och utan någon dataflytt. Följande exempelkommando trunkerar datafilen med file_id 4:

DBCC SHRINKFILE (4, TRUNCATEONLY);

När det här kommandot har körts för varje datafil kan du köra frågan om utrymmesanvändning igen för att se minskningen av allokerat utrymme, om det finns några. Du kan också visa allokerat utrymme för databasen i Azure-portalen.

Utvärdera indexsidans densitet

Om trunkering av datafiler inte resulterade i en tillräcklig minskning av allokerat utrymme måste du krympa datafiler. Men som ett valfritt men rekommenderat steg bör du först bestämma genomsnittlig sidtäthet för index i databasen. För samma mängd data slutförs krympningen snabbare om sidtätheten är hög, eftersom den måste flytta färre sidor. Om sidtätheten är låg för vissa index bör du överväga att utföra underhåll på dessa index för att öka sidtätheten innan datafilerna krymps. Detta gör också att krympning kan minska det allokerade lagringsutrymmet djupare.

Använd följande fråga för att fastställa siddensitet för alla index i databasen. Siddensitet rapporteras i avg_page_space_used_in_percent kolumnen.

SELECT OBJECT_SCHEMA_NAME(ips.object_id) AS schema_name,
       OBJECT_NAME(ips.object_id) AS object_name,
       i.name AS index_name,
       i.type_desc AS index_type,
       ips.avg_page_space_used_in_percent,
       ips.avg_fragmentation_in_percent,
       ips.page_count,
       ips.alloc_unit_type_desc,
       ips.ghost_record_count
FROM sys.dm_db_index_physical_stats(DB_ID(), default, default, default, 'SAMPLED') AS ips
INNER JOIN sys.indexes AS i 
ON ips.object_id = i.object_id
   AND
   ips.index_id = i.index_id
ORDER BY page_count DESC;

Om det finns index med högt sidantal som har en sidtäthet som är lägre än 60–70 %, bör du överväga att återskapa eller omorganisera dessa index innan du krymper datafilerna.

Kommentar

För större databaser kan det ta lång tid (timmar) att slutföra frågan för att fastställa siddensiteten. Dessutom kräver återskapande eller omorganisering av stora index också betydande tids- och resursanvändning. Det finns en kompromiss mellan att spendera extra tid på att öka sidtätheten å ena sidan och minska krympningstiden och uppnå högre utrymmesbesparingar på en annan.

Om det finns flera index med låg sidtäthet kan du återskapa dem parallellt på flera databassessioner för att påskynda processen. Kontrollera dock att du inte närmar dig databasresursgränserna genom att göra det och lämna tillräckligt med resursutrymme för programarbetsbelastningar som kan köras. Övervaka resursförbrukningen (CPU, data-I/O, logg-I/O) i Azure-portalen eller använd vyn sys.dm_db_resource_stats och starta ytterligare parallella återskapanden endast om resursanvändningen för var och en av dessa dimensioner fortfarande är betydligt lägre än 100 %. Om processor-, data-I/O- eller logg-I/O-användningen är 100 %, kan du skala upp databasen för att få fler CPU-kärnor och öka I/O-dataflödet. Detta kan möjliggöra ytterligare parallella återskapanden för att slutföra processen snabbare.

Exempel på återskapningskommando för index

Följande är ett exempelkommando för att återskapa ett index och öka dess siddensitet med hjälp av ALTER INDEX-instruktionen:

ALTER INDEX [index_name] ON [schema_name].[table_name] 
REBUILD WITH (FILLFACTOR = 100, MAXDOP = 8, 
ONLINE = ON (WAIT_AT_LOW_PRIORITY (MAX_DURATION = 5 MINUTES, ABORT_AFTER_WAIT = NONE)), 
RESUMABLE = ON);

Det här kommandot initierar en online- och återupptagningsbar indexåterbyggnad. På så sätt kan samtidiga arbetsbelastningar fortsätta att använda tabellen medan återskapande pågår och du kan återuppta återskapa om den avbryts av någon anledning. Den här typen av återskapande är dock långsammare än en offline-återskapande, vilket blockerar åtkomsten till tabellen. Om inga andra arbetsbelastningar behöver komma åt tabellen under återskapande anger du ONLINE alternativen och RESUMABLE till OFF och tar bort WAIT_AT_LOW_PRIORITY satsen.

Mer information om indexunderhåll finns i Optimera indexunderhåll för att förbättra frågeprestanda och minska resursförbrukningen.

Krymp flera datafiler

Som tidigare nämnts är krympning med dataförflyttning en tidskrävande process. Om databasen har flera datafiler kan du påskynda processen genom att krympa flera datafiler parallellt. Du gör detta genom att öppna flera databassessioner och använda DBCC SHRINKFILE på varje session med ett annat file_id värde. Se till att du har tillräckligt med resursutrymme (CPU, data-I/O, logg-I/O) innan du startar varje nytt parallellt krympningskommando.

Följande exempelkommando krymper datafilen med file_id 4 och försöker minska den allokerade storleken till 52 000 MB genom att flytta sidor i filen:

DBCC SHRINKFILE (4, 52000);

Om du vill minska det allokerade utrymmet för filen så mycket som möjligt kör du -instruktionen utan att ange målstorleken:

DBCC SHRINKFILE (4);

Om en arbetsbelastning körs samtidigt med krympning kan den börja använda lagringsutrymmet som frigörs genom krympning innan krympningen slutförs och trunkerar filen. I det här fallet kan krympning inte minska allokerat utrymme till det angivna målet.

Du kan minimera detta genom att krympa varje fil i mindre steg. Det innebär att du i DBCC SHRINKFILE kommandot anger det mål som är något mindre än det aktuella allokerade utrymmet för filen, vilket visas i resultatet av frågan om baslinjeutrymmesanvändning. Om till exempel allokerat utrymme för fil med file_id 4 är 200 000 MB och du vill minska det till 100 000 MB, kan du först ange målet till 170 000 MB:

DBCC SHRINKFILE (4, 170000);

När det här kommandot har slutförts har den trunkerat filen och minskat dess allokerade storlek till 170 000 MB. Du kan sedan upprepa det här kommandot och först ange målet till 140 000 MB, sedan till 110 000 MB osv., tills filen krymps till önskad storlek. Om kommandot slutförs men filen inte trunkeras använder du mindre steg, till exempel 15 000 MB i stället för 30 000 MB.

Om du vill övervaka krympningsstatus för alla samtidiga krympningssessioner kan du använda följande fråga:

SELECT command,
       percent_complete,
       status,
       wait_resource,
       session_id,
       wait_type,
       blocking_session_id,
       cpu_time,
       reads,
       CAST(((DATEDIFF(s,start_time, GETDATE()))/3600) AS varchar) + ' hour(s), '
                     + CAST((DATEDIFF(s,start_time, GETDATE())%3600)/60 AS varchar) + 'min, '
                     + CAST((DATEDIFF(s,start_time, GETDATE())%60) AS varchar) + ' sec' AS running_time
FROM sys.dm_exec_requests AS r
LEFT JOIN sys.databases AS d
ON r.database_id = d.database_id
WHERE r.command IN ('DbccSpaceReclaim','DbccFilesCompact','DbccLOBCompact','DBCC');

Kommentar

Krympningsförloppet kan vara icke-linjärt och värdet i percent_complete kolumnen kan förbli praktiskt taget oförändrat under långa tidsperioder, även om krympningen fortfarande pågår.

När krympningen har slutförts för alla datafiler kör du frågan om utrymmesanvändning (eller checkar in Azure-portalen) för att fastställa den resulterande minskningen av allokerad lagringsstorlek. Om det fortfarande finns en stor skillnad mellan använt utrymme och allokerat utrymme kan du återskapa index enligt beskrivningen tidigare. Detta kan tillfälligt öka allokerat utrymme ytterligare, men krympande datafiler igen efter återskapande av index bör resultera i en djupare minskning av allokerat utrymme.

Tillfälliga fel under krympning

Ibland kan ett krympningskommando misslyckas med olika fel, till exempel tidsgränser och dödlägen. I allmänhet är dessa fel tillfälliga och inträffar inte igen om samma kommando upprepas. Om krympning misslyckas med ett fel behålls de framsteg som gjorts hittills när det gäller att flytta datasidor och samma krympningskommando kan köras igen för att fortsätta krympa filen.

Följande exempelskript visar hur du kan köra krympa i en återförsöksloop för att automatiskt försöka igen upp till ett konfigurerbart antal gånger när ett timeout-fel eller ett dödlägesfel inträffar. Den här metoden för återförsök gäller för många andra fel som kan uppstå under krympning.

DECLARE @RetryCount int = 3; -- adjust to configure desired number of retries
DECLARE @Delay char(12);

-- Retry loop
WHILE @RetryCount >= 0
BEGIN

BEGIN TRY

DBCC SHRINKFILE (1); -- adjust file_id and other shrink parameters

-- Exit retry loop on successful execution
SELECT @RetryCount = -1;

END TRY
BEGIN CATCH
    -- Retry for the declared number of times without raising an error if deadlocked or timed out waiting for a lock
    IF ERROR_NUMBER() IN (1205, 49516) AND @RetryCount > 0
    BEGIN
        SELECT @RetryCount -= 1;

        PRINT CONCAT('Retry at ', SYSUTCDATETIME());

        -- Wait for a random period of time between 1 and 10 seconds before retrying
        SELECT @Delay = '00:00:0' + CAST(CAST(1 + RAND() * 8.999 AS decimal(5,3)) AS varchar(5));
        WAITFOR DELAY @Delay;
    END
    ELSE -- Raise error and exit loop
    BEGIN
        SELECT @RetryCount = -1;
        THROW;
    END
END CATCH
END;

Förutom timeouter och dödlägen kan krympa stöta på fel på grund av vissa kända problem.

De fel som returneras och åtgärdsstegen är följande:

  • Felnummer: 49503, felmeddelande: %.*ls: Sidan %d:%d kunde inte flyttas eftersom det är en beständig versionsarkivsida utanför rad. Orsak till sidbrytning: %ls. Tidsstämpel för sidupplåsning: %I64d.

Det här felet uppstår när det finns långvariga aktiva transaktioner som har genererat radversioner i det beständiga versionsarkivet (PVS). De sidor som innehåller dessa radversioner kan inte flyttas genom krympning. Därför går det inte att göra framsteg och misslyckas med det här felet.

För att minimera måste du vänta tills de här långvariga transaktionerna har slutförts. Du kan också identifiera och avsluta dessa långvariga transaktioner, men det kan påverka ditt program om det inte hanterar transaktionsfel på ett korrekt sätt. Ett sätt att hitta långvariga transaktioner är genom att köra följande fråga i databasen där du körde kommandot shrink:

-- Transactions sorted by duration
SELECT st.session_id,
       dt.database_transaction_begin_time,
       DATEDIFF(second, dt.database_transaction_begin_time, CURRENT_TIMESTAMP) AS transaction_duration_seconds,
       dt.database_transaction_log_bytes_used,
       dt.database_transaction_log_bytes_reserved,
       st.is_user_transaction,
       st.open_transaction_count,
       ib.event_type,
       ib.parameters,
       ib.event_info
FROM sys.dm_tran_database_transactions AS dt
INNER JOIN sys.dm_tran_session_transactions AS st
ON dt.transaction_id = st.transaction_id
OUTER APPLY sys.dm_exec_input_buffer(st.session_id, default) AS ib
WHERE dt.database_id = DB_ID()
ORDER BY transaction_duration_seconds DESC;

Du kan avsluta en transaktion med hjälp KILL av kommandot och ange det associerade session_id värdet från frågeresultatet:

KILL 4242; -- replace 4242 with the session_id value from query results

Varning

Att avsluta en transaktion kan påverka arbetsbelastningar negativt.

När långvariga transaktioner har avslutats eller slutförts rensas inte längre nödvändiga radversioner efter en intern bakgrundsaktivitet. Du kan övervaka PVS-storleken för att mäta rensningsstatusen med hjälp av följande fråga. Kör frågan i databasen där du körde krympningskommandot:

SELECT pvss.persistent_version_store_size_kb / 1024. / 1024 AS persistent_version_store_size_gb,
       pvss.online_index_version_store_size_kb / 1024. / 1024 AS online_index_version_store_size_gb,
       pvss.current_aborted_transaction_count,
       pvss.aborted_version_cleaner_start_time,
       pvss.aborted_version_cleaner_end_time,
       dt.database_transaction_begin_time AS oldest_transaction_begin_time,
       asdt.session_id AS active_transaction_session_id,
       asdt.elapsed_time_seconds AS active_transaction_elapsed_time_seconds
FROM sys.dm_tran_persistent_version_store_stats AS pvss
LEFT JOIN sys.dm_tran_database_transactions AS dt
ON pvss.oldest_active_transaction_id = dt.transaction_id
   AND
   pvss.database_id = dt.database_id
LEFT JOIN sys.dm_tran_active_snapshot_database_transactions AS asdt
ON pvss.min_transaction_timestamp = asdt.transaction_sequence_num
   OR
   pvss.online_index_min_transaction_timestamp = asdt.transaction_sequence_num
WHERE pvss.database_id = DB_ID();

När PVS-storleken som rapporteras i persistent_version_store_size_gb kolumnen minskas avsevärt jämfört med dess ursprungliga storlek bör omkörningen av krympningen lyckas.

  • Felnummer: 5223, felmeddelande: %.*ls: Tom sida %d:%d kunde inte frigöras.

Det här felet kan inträffa om det finns pågående indexunderhållsåtgärder som ALTER INDEX. Försök att krympa kommandot igen när de här åtgärderna har slutförts.

Om det här felet kvarstår kan det associerade indexet behöva återskapas. Kör följande fråga i samma databas där du körde krympningskommandot för att hitta indexet som ska återskapas:

SELECT OBJECT_SCHEMA_NAME(pg.object_id) AS schema_name,
       OBJECT_NAME(pg.object_id) AS object_name,
       i.name AS index_name,
       p.partition_number
FROM sys.dm_db_page_info(DB_ID(), <file_id>, <page_id>, default) AS pg
INNER JOIN sys.indexes AS i
ON pg.object_id = i.object_id
   AND
   pg.index_id = i.index_id
INNER JOIN sys.partitions AS p
ON pg.partition_id = p.partition_id;

Innan du kör den här frågan ersätter <file_id> du platshållarna och <page_id> med de faktiska värdena från det felmeddelande som du fick. Om meddelandet till exempel är Tom sida 1:62669 inte kunde frigöras är <file_id>1 och <page_id> är 62669.

Återskapa indexet som identifieras av frågan och försök igen med krympningskommandot.

  • Felnummer: 5201, felmeddelande: DBCC SHRINKDATABASE: Fil-ID %d för databas-ID %d hoppades över eftersom filen inte har tillräckligt med ledigt utrymme att frigöra.

Det här felet innebär att datafilen inte kan krympas ytterligare. Du kan gå vidare till nästa datafil.

Nästa steg

Information om databasens maxstorlekar finns i: