Berechnen der Gesamtabrechnungsgröße eines Blobcontainers

Mit diesem Skript berechnen Sie die Größe eines Containers in Azure Blob Storage zur Schätzung der Abrechnungskosten. Mit dem Skript wird die Größe der Blobs im Container addiert.

Wichtig

Mit dem in diesem Artikel angegebenen Beispielskript wird die Abrechnungsgröße für Blobmomentaufnahmen ggf. nicht exakt berechnet.

Für dieses Beispiel ist Azure PowerShell erforderlich. Führen Sie Get-Module -ListAvailable Az aus, um die Version zu ermitteln. Wenn Sie eine Installation oder ein Upgrade ausführen müssen, finden Sie unter Install and configure Azure PowerShell (Installieren des Azure PowerShell-Moduls) Informationen dazu.

Führen Sie das Cmdlet Connect-AzAccount aus, um eine Verbindung mit Azure herzustellen.

Sollten Sie über kein Azure-Abonnement verfügen, können Sie zunächst ein kostenloses Azure-Konto erstellen.

Hinweis

Dieses PowerShell-Skript berechnet die Größe eines Containers zu Abrechnungszwecken. Wenn Sie die Containergröße zu anderen Zwecken berechnen möchten, finden Sie unter Berechnen der Gesamtgröße eines Blob Storage-Containers ein einfacheres Skript zur Angabe einer Schätzung.

Bestimmen der Größe des Blobcontainers

Die Gesamtgröße des Blobcontainers beinhaltet die Größe des eigentlichen Containers sowie die Größe aller Blobs unter dem Container.

In den folgenden Abschnitten erfahren Sie, wie die Speicherkapazität für Blobcontainer und Blobs berechnet wird. Im folgenden Abschnitt steht Len(X) für die Anzahl von Zeichen in der Zeichenfolge.

Blobcontainer

Die folgende Berechnung zeigt, wie Sie den voraussichtlichen Speicherbedarf pro Blobcontainer ermitteln:

48 bytes + Len(ContainerName) * 2 bytes +
For-Each Metadata[3 bytes + Len(MetadataName) + Len(Value)] +
For-Each Signed Identifier[512 bytes]

Aufschlüsselung:

  • Die 48 Bytes für den Verarbeitungsaufwand für jeden Container setzen sich aus der Uhrzeit der letzten Änderung, Berechtigungen, öffentlichen Einstellungen und einigen Systemmetadaten zusammen.

  • Da der Containername als Unicode gespeichert wird, muss die Zeichenanzahl verdoppelt werden.

  • Für jeden gespeicherten Block von Blobcontainer-Metadaten speichern wir die Länge des Namens (ASCII) plus die Länge des Zeichenfolgenwerts.

  • Die 512 Bytes pro signiertem Bezeichner umfassen den Namen des signierten Bezeichners, die Start- und Ablaufzeit sowie Berechtigungen.

BLOBs

Die folgenden Berechnungen zeigen, wie Sie den voraussichtlichen Speicherbedarf pro Blob ermitteln:

  • Blockblob (Basisblob oder Momentaufnahme):

    124 bytes + Len(BlobName) * 2 bytes +
    For-Each Metadata[3 bytes + Len(MetadataName) + Len(Value)] +
    8 bytes + number of committed and uncommitted blocks * Block ID Size in bytes +
    SizeInBytes(data in unique committed data blocks stored) +
    SizeInBytes(data in uncommitted data blocks)
    
  • Seitenblob (Basisblob oder Momentaufnahme):

    124 bytes + Len(BlobName) * 2 bytes +
    For-Each Metadata[3 bytes + Len(MetadataName) + Len(Value)] +
    number of nonconsecutive page ranges with data * 12 bytes +
    SizeInBytes(data in unique pages stored)
    

Aufschlüsselung:

  • 124 Bytes zusätzlicher Speicherbedarf für das Blob, einschließlich:

    • Uhrzeit der letzten Änderung
    • Size
    • Cachesteuerung
    • Content-Type
    • Inhaltssprache
    • Content-Encoding
    • Content-MD5
    • Berechtigungen
    • Momentaufnahmeinformationen
    • Lease
    • Einige Systemmetadaten
  • Da der Blobname als Unicode gespeichert wird, muss die Zeichenanzahl verdoppelt werden.

  • Addieren Sie für jeden gespeicherten Metadatenblock die Länge des (als ASCII gespeicherten) Namens sowie die Länge des Zeichenfolgenwerts.

  • Für die Blockblobs:

    • 8 Bytes für die Blockliste.

    • Anzahl von Blöcken mal Größe der Block-ID in Bytes.

    • Die Größe der Daten in allen Blöcken (mit und ohne Commit).

      Hinweis

      Bei Verwendung von Momentaufnahmen beinhaltet diese Größe nur die eindeutigen Daten für dieses Basis- oder Momentaufnahmeblob. Wenn die Blöcke ohne Commit nicht innerhalb einer Woche verwendet werden, werden sie der Garbage Collection zugeführt. Danach sind sie für die Abrechnung nicht mehr relevant.

  • Für Seitenblobs:

    • Die Anzahl nicht aufeinanderfolgender Seitenbereiche mit Daten mal 12 Bytes. Dies ist die Anzahl eindeutiger Seitenbereiche, die beim Aufrufen der API GetPageRanges angezeigt wird.

    • Die Größe der Daten aller gespeicherten Seiten (in Bytes).

      Hinweis

      Bei Verwendung von Momentaufnahmen beinhaltet diese Größe nur die eindeutigen Seiten für das Basis- oder Momentaufnahmeblob, für das die Zählung durchgeführt wird.

Beispielskript

# this script will show how to get the total size of the blobs in a container
# before running this, you need to create a storage account, create a container,
#    and upload some blobs into the container
# note: this retrieves all of the blobs in the container in one command.
#       connect Azure with Login-AzAccount before you run the script.
#       requests sent as part of this tool will incur transactional costs.
# command line usage: script.ps1 -ResourceGroup {YourResourceGroupName} -StorageAccountName {YourAccountName} -ContainerName {YourContainerName}
#

param(
    [Parameter(Mandatory=$true)]
    [string]$ResourceGroup,

    [Parameter(Mandatory=$true)]
    [string]$StorageAccountName,

    [Parameter(Mandatory=$true)]
    [string]$ContainerName
)

#Set-StrictMode will cause Get-AzStorageBlob returns result in different data types when there is only one blob
#Set-StrictMode -Version 2

$VerbosePreference = "Continue"

if((Get-Module -ListAvailable Az.Storage) -eq $null)
{
    throw "Azure Powershell not found! Please install from https://docs.microsoft.com/en-us/powershell/azure/install-Az-ps"
}

# function Retry-OnRequest
function Retry-OnRequest
{
    param(
        [Parameter(Mandatory=$true)]
        $Action)

    # It could encounter various of temporary errors, like network errors, or storage server busy errors.
    # Should retry the request on transient errors

    # Retry on storage server timeout errors
    $clientTimeOut = New-TimeSpan -Minutes 15
    $retryPolicy = New-Object -TypeName Microsoft.Azure.Storage.RetryPolicies.ExponentialRetry -ArgumentList @($clientTimeOut, 10)
    $requestOption = @{}
    $requestOption.RetryPolicy = $retryPolicy

    # Retry on temporary network errors
    $shouldRetryOnException = $false
    $maxRetryCountOnException = 3

    do
    {
        try
        {
            return $Action.Invoke($requestOption)
        }
        catch
        {
            if ($_.Exception.InnerException -ne $null -And $_.Exception.InnerException.GetType() -Eq [System.TimeoutException] -And $maxRetryCountOnException -gt 0)
            {
                $shouldRetryOnException = $true
                $maxRetryCountOnException--
            }
            else
            {
                $shouldRetryOnException = $false
                throw
            }
        }
    }
    while ($shouldRetryOnException)

}

# function Get-BlobBytes

function Get-BlobBytes
{
    param(
        [Parameter(Mandatory=$true)]
        $Blob,
        [Parameter(Mandatory=$false)]
        [bool]$IsPremiumAccount = $false)

    # Base + blobname
    $blobSizeInBytes = 124 + $Blob.Name.Length * 2

    # Get size of metadata
    $metadataEnumerator=$Blob.ICloudBlob.Metadata.GetEnumerator()
    while($metadataEnumerator.MoveNext())
    {
        $blobSizeInBytes += 3 + $metadataEnumerator.Current.Key.Length + $metadataEnumerator.Current.Value.Length
    }

    if (!$IsPremiumAccount)
    {
        if($Blob.BlobType -eq [Microsoft.Azure.Storage.Blob.BlobType]::BlockBlob)
        {
            $blobSizeInBytes += 8
            # Default is Microsoft.Azure.Storage.Blob.BlockListingFilter.Committed. Need All
            $action = { param($requestOption) return $Blob.ICloudBlob.DownloadBlockList([Microsoft.Azure.Storage.Blob.BlockListingFilter]::All, $null, $requestOption) }

            $blocks=Retry-OnRequest $action

            if ($null -eq $blocks)
            {
                $blobSizeInBytes += $Blob.ICloudBlob.Properties.Length
            }
            else
            {
                $blocks | ForEach-Object { $blobSizeInBytes += $_.Length + $_.Name.Length }
            }
        }
        elseif($Blob.BlobType -eq [Microsoft.Azure.Storage.Blob.BlobType]::PageBlob)
        {
            # It could cause server time out issue when trying to get page ranges of highly fragmented page blob
            # Get page ranges in segment can mitigate chance of meeting such kind of server time out issue
            # See https://blogs.msdn.microsoft.com/windowsazurestorage/2012/03/26/getting-the-page-ranges-of-a-large-page-blob-in-segments/ for details.
            $pageRangesSegSize = 148 * 1024 * 1024L
            $totalSize = $Blob.ICloudBlob.Properties.Length
            $pageRangeSegOffset = 0

            $pageRangesTemp = New-Object System.Collections.ArrayList

            while ($pageRangeSegOffset -lt $totalSize)
            {
                $action = {param($requestOption) return $Blob.ICloudBlob.GetPageRanges($pageRangeSegOffset, $pageRangesSegSize, $null, $requestOption) }

                Retry-OnRequest $action | ForEach-Object { $pageRangesTemp.Add($_) }  | Out-Null
                $pageRangeSegOffset += $pageRangesSegSize
            }

            $pageRanges = New-Object System.Collections.ArrayList

            foreach ($pageRange in $pageRangesTemp)
            {
                if($lastRange -eq $Null)
                {
                    $lastRange = New-Object PageRange
                    $lastRange.StartOffset = $pageRange.StartOffset
                    $lastRange.EndOffset =  $pageRange.EndOffset
                }
                else
                {
                    if (($lastRange.EndOffset + 1) -eq $pageRange.StartOffset)
                    {
                        $lastRange.EndOffset = $pageRange.EndOffset
                    }
                    else
                    {
                        $pageRanges.Add($lastRange)  | Out-Null
                        $lastRange = New-Object PageRange
                        $lastRange.StartOffset = $pageRange.StartOffset
                        $lastRange.EndOffset =  $pageRange.EndOffset
                    }
                }
            }

            $pageRanges.Add($lastRange) | Out-Null
            $pageRanges |  ForEach-Object {
                    $blobSizeInBytes += 12 + $_.EndOffset - $_.StartOffset
                }
        }
        else
        {
            $blobSizeInBytes += $Blob.ICloudBlob.Properties.Length
        }
        return $blobSizeInBytes
    }
    else
    {
        $blobSizeInBytes += $Blob.ICloudBlob.Properties.Length
    }
    return $blobSizeInBytes
}

# function Get-ContainerBytes

function Get-ContainerBytes
{
    param(
        [Parameter(Mandatory=$true)]
        [Microsoft.Azure.Storage.Blob.CloudBlobContainer]$Container,
        [Parameter(Mandatory=$false)]
        [bool]$IsPremiumAccount = $false)

    # Base + name of container
    $containerSizeInBytes = 48 + $Container.Name.Length*2

    # Get size of metadata
    $metadataEnumerator = $Container.Metadata.GetEnumerator()
    while($metadataEnumerator.MoveNext())
    {
        $containerSizeInBytes += 3 + $metadataEnumerator.Current.Key.Length + $metadataEnumerator.Current.Value.Length
    }

    # Get size for SharedAccessPolicies
    $containerSizeInBytes += $Container.GetPermissions().SharedAccessPolicies.Count * 512

    # Calculate size of all blobs.
    $blobCount = 0
    $Token = $Null
    $MaxReturn = 5000

    do {
        $Blobs = Get-AzStorageBlob -Context $storageContext -Container $Container.Name -MaxCount $MaxReturn -ContinuationToken $Token
        if($Blobs -eq $Null) { break }

        #Set-StrictMode will cause Get-AzStorageBlob returns result in different data types when there is only one blob
        if($Blobs.GetType().Name -eq "AzureStorageBlob")
        {
            $Token = $Null
        }
        else
        {
            $Token = $Blobs[$Blobs.Count - 1].ContinuationToken;
        }

        $Blobs | ForEach-Object {
                $blobSize = Get-BlobBytes $_ $IsPremiumAccount
                $containerSizeInBytes += $blobSize
                $blobCount++

                if(($blobCount % 1000) -eq 0)
                {
                    Write-Verbose("Counting {0} Sizing {1} " -f $blobCount, $containerSizeInBytes)
                }
            }
    }
    While ($Token -ne $Null)

    return @{ "containerSize" = $containerSizeInBytes; "blobCount" = $blobCount }
}

#Login-AzAccount

$storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroup -Name $StorageAccountName -ErrorAction SilentlyContinue
if($storageAccount -eq $null)
{
    throw "The storage account specified does not exist in this subscription."
}

$storageContext = $storageAccount.Context

if (-not ([System.Management.Automation.PSTypeName]'PageRange').Type)
{
    $Source = "
        public class PageRange
        {
            public long StartOffset;
            public long EndOffset;
        }"
    Add-Type -TypeDefinition $Source
}

$containers = New-Object System.Collections.ArrayList
if($ContainerName.Length -ne 0)
{
    $container = Get-AzStorageContainer -Context $storageContext -Name $ContainerName -ErrorAction SilentlyContinue |
        ForEach-Object { $containers.Add($_) } | Out-Null
}
else
{
    Get-AzStorageContainer -Context $storageContext | ForEach-Object { $containers.Add($_) } | Out-Null
}

$sizeInBytes = 0
$IsPremiumAccount = ($storageAccount.Sku.Tier -eq "Premium")

if($containers.Count -gt 0)
{
    $containers | ForEach-Object {
        Write-Output("Calculating container {0} ..." -f $_.CloudBlobContainer.Name)
        $result = Get-ContainerBytes $_.CloudBlobContainer $IsPremiumAccount
        $sizeInBytes += $result.containerSize

        Write-Output("Container '{0}' with {1} blobs has a sizeof {2:F2} MB." -f $_.CloudBlobContainer.Name,$result.blobCount,($result.containerSize/1MB))
    }
}
else
{
    Write-Warning "No containers found to process in storage account '$StorageAccountName'."
}

Nächste Schritte