Dela via


Hantera samtidighet i Blob Storage

Moderna program har ofta flera användare som visar och uppdaterar data samtidigt. Programutvecklare måste tänka noga på hur de ska ge slutanvändarna en förutsägbar upplevelse, särskilt för scenarier där flera användare kan uppdatera samma data. Azure Storage-klientbiblioteken stöder inte samtidiga skrivningar till samma blob, med undantag för tilläggsblobar om skrivordningen inte spelar någon roll. Om din app kräver att flera processer skrivs till samma blob bör du implementera en strategi för samtidighetskontroll. Det finns tre huvudsakliga strategier för samtidighet av data som utvecklare vanligtvis överväger:

  • Optimistisk samtidighet: Ett program som utför en uppdatering avgör, som en del av uppdateringen, om data har ändrats sedan programmet senast läste dessa data. Om till exempel två användare som visar en wiki-sida gör en uppdatering av den sidan måste wiki-plattformen se till att den andra uppdateringen inte skriver över den första uppdateringen. Den måste också se till att båda användarna förstår om uppdateringen lyckades. Den här strategin används oftast i webbprogram.

  • Pessimistisk samtidighet: Ett program som vill utföra en uppdatering låser ett objekt som hindrar andra användare från att uppdatera data tills låset släpps. I till exempel ett primärt/sekundärt datareplikeringsscenario där endast den primära utför uppdateringar, har den primära vanligtvis ett exklusivt lås på data under en längre tid för att säkerställa att ingen annan kan uppdatera dem.

  • Senaste skrivaren vinner: En metod som möjliggör att uppdateringar kan fortsätta utan att först avgöra om en annan applikation har uppdaterat datan sedan den lästes. Den här metoden används vanligtvis när data partitioneras på ett sådant sätt att flera användare inte kommer åt samma data samtidigt. Det kan också vara användbart när kortlivade dataströmmar bearbetas.

Azure Storage har stöd för alla tre strategierna, även om dess unika förmåga att ge fullt stöd för optimistisk och pessimistisk samtidighet utmärker sig. Azure Storage har utformats för att omfatta en stark konsekvensmodell som garanterar att efterföljande läs- eller liståtgärder returnerar den senaste uppdateringen efter att tjänsten har utför en infognings- eller uppdateringsåtgärd.

Förutom att välja en lämplig samtidighetsstrategi bör utvecklare också vara medvetna om hur en lagringsplattform isolerar ändringar, särskilt ändringar i samma objekt mellan transaktioner. Azure Storage använder ögonblicksbildisolering för att tillåta läsåtgärder samtidigt med skrivåtgärder inom en enda partition. Isolering av ögonblicksbilder garanterar att alla läsåtgärder returnerar en konsekvent ögonblicksbild av data även när uppdateringar sker.

Du kan välja att använda antingen de optimistiska eller pessimistiska samtidighetsmodellerna för att hantera åtkomst till blobar och containrar. Om du inte uttryckligen anger en strategi vinner den senaste skrivaren som standard.

Optimistisk konkurrenshantering

Azure Storage tilldelar en identifierare till varje objekt som lagras. Den här identifieraren uppdateras varje gång en skrivåtgärd utförs på ett objekt. Identifieraren returneras till klienten som en del av ett HTTP GET-svar i ETag-huvudet som definieras av HTTP-protokollet.

En klient som utför en uppdatering kan skicka den ursprungliga ETag tillsammans med en villkorsstyrd rubrik för att säkerställa att en uppdatering endast inträffar om ett visst villkor har uppfyllts. Om till exempel If-Match-huvudet har angetts kontrollerar Azure Storage att värdet för ETag som anges i uppdateringsbegäran är detsamma som ETag för objektet som uppdateras. Mer information om villkorsstyrda rubriker finns i Ange villkorsstyrda huvuden för Blob Service-åtgärder.

Konturen av den här processen är följande:

  1. Hämta en blob från Azure Storage. Svaret innehåller ett HTTP ETag-huvudvärde som identifierar den aktuella versionen av objektet.
  2. När du uppdaterar bloben tar du med det ETag-värde som du fick i steg 1 i villkorsrubriken If-Match i skrivbegäran. Azure Storage jämför ETag-värdet i begäran med blobens aktuella ETag-värde.
  3. Om blobens aktuella ETag-värde skiljer sig från det ETag-värde som anges i villkorsrubriken If-Match som anges i begäran returnerar Azure Storage HTTP-statuskod 412 (förhandsvillkoret misslyckades). Det här felet anger för klienten att en annan process har uppdaterat bloben sedan klienten först hämtade den. Klienten bör hämta bloben igen för att hämta uppdaterat innehåll och egenskaper.
  4. Om det aktuella ETag-värdet för bloben är samma version som ETag i villkorsrubriken If-Match i begäran, utför Azure Storage den begärda åtgärden och uppdaterar blobens aktuella ETag-värde.

Följande kodexempel visar hur du konstruerar ett If-Match-villkor för skrivbegäran som kontrollerar ETag-värdet för en blob. Azure Storage utvärderar om blobens aktuella ETag är samma som ETag som anges i begäran och utför skrivåtgärden endast om de två ETag-värdena matchar. Om en annan process har uppdaterat bloben under tiden returnerar Azure Storage ett HTTP 412-statusmeddelande (villkorsfel).

private static async Task DemonstrateOptimisticConcurrencyBlob(BlobClient blobClient)
{
    Console.WriteLine("Demonstrate optimistic concurrency");

    try
    {
        // Download a blob
        Response<BlobDownloadResult> response = await blobClient.DownloadContentAsync();
        BlobDownloadResult downloadResult = response.Value;
        string blobContents = downloadResult.Content.ToString();

        ETag originalETag = downloadResult.Details.ETag;
        Console.WriteLine("Blob ETag = {0}", originalETag);

        // This function simulates an external change to the blob after we've fetched it
        // The external change updates the contents of the blob and the ETag value
        await SimulateExternalBlobChangesAsync(blobClient);

        // Now try to update the blob using the original ETag value
        string blobContentsUpdate2 = $"{blobContents} Update 2. If-Match condition set to original ETag.";

        // Set the If-Match condition to the original ETag
        BlobUploadOptions blobUploadOptions = new()
        {
            Conditions = new BlobRequestConditions()
            {
                IfMatch = originalETag
            }
        };

        // This call should fail with error code 412 (Precondition Failed)
        BlobContentInfo blobContentInfo =
            await blobClient.UploadAsync(BinaryData.FromString(blobContentsUpdate2), blobUploadOptions);
    }
    catch (RequestFailedException e) when (e.Status == (int)HttpStatusCode.PreconditionFailed)
    {
        Console.WriteLine(
            @"Blob's ETag does not match ETag provided. Fetch the blob to get updated contents and properties.");
    }
}

private static async Task SimulateExternalBlobChangesAsync(BlobClient blobClient)
{
    // Simulates an external change to the blob for this example

    // Download a blob
    Response<BlobDownloadResult> response = await blobClient.DownloadContentAsync();
    BlobDownloadResult downloadResult = response.Value;
    string blobContents = downloadResult.Content.ToString();

    // Update the existing block blob contents
    // No ETag condition is provided, so original blob is overwritten and ETag is updated
    string blobContentsUpdate1 = $"{blobContents} Update 1";
    BlobContentInfo blobContentInfo =
        await blobClient.UploadAsync(BinaryData.FromString(blobContentsUpdate1), overwrite: true);
    Console.WriteLine("Blob update. Updated ETag = {0}", blobContentInfo.ETag);
}

Azure Storage har också stöd för andra villkorsstyrda rubriker, till exempel If-Modified-Since, If-Unmodified-Since och If-None-Match. För mer information, se Ange villkorsstyrda rubriker för Blob Service-åtgärder.

Pessimistisk samtidighet för blobar

Om du vill låsa en blob för exklusiv användning kan du skaffa ett leasingavtal på den. När du skaffar lånet anger du lånets varaktighet. Ett ändligt leasingavtal kan gälla mellan 15 och 60 sekunder. Ett hyresavtal kan också vara på obestämd tid, vilket innebär ett exklusivt lås. Du kan förnya ett ändligt avtal för att förlänga det och du kan frigöra avtalet när du är klar med det. Azure Storage släpper automatiskt ändliga hyresavtal när de upphör att gälla.

Leasingavtal möjliggör stöd för olika synkroniseringsstrategier, inklusive exklusiva skriv-/delade läsåtgärder, exklusiva skriv-/exklusiva läsåtgärder och delade skriv-/exklusiva läsåtgärder. När ett utrymmesavtal finns, säkerställer Azure Storage exklusiv åtkomst för skrivåtgärder åt innehavaren av avtalet. För att säkerställa exklusivitet för läsåtgärder måste utvecklaren dock se till att alla klientprogram använder ett låne-ID och att endast en klient i taget har ett giltigt låne-ID. Läsåtgärder som inte innehåller ett låne-ID resulterar i delade läsningar.

Följande kodexempel visar hur du skaffar en exklusiv låsning på en blob, uppdaterar innehållet i bloben genom att ange lås-ID:t och sedan släpper låsningen. Om lånet är aktivt och låne-ID:t inte tillhandahålls på en skrivbegäran misslyckas skrivåtgärden med felkoden 412 (villkoret misslyckades).

public static async Task DemonstratePessimisticConcurrencyBlob(BlobClient blobClient)
{
    Console.WriteLine("Demonstrate pessimistic concurrency");

    BlobContainerClient containerClient = blobClient.GetParentBlobContainerClient();
    BlobLeaseClient blobLeaseClient = blobClient.GetBlobLeaseClient();

    try
    {
        // Create the container if it does not exist.
        await containerClient.CreateIfNotExistsAsync();

        // Upload text to a blob.
        string blobContents1 = "First update. Overwrite blob if it exists.";
        byte[] byteArray = Encoding.ASCII.GetBytes(blobContents1);
        using (MemoryStream stream = new MemoryStream(byteArray))
        {
            BlobContentInfo blobContentInfo = await blobClient.UploadAsync(stream, overwrite: true);
        }

        // Acquire a lease on the blob.
        BlobLease blobLease = await blobLeaseClient.AcquireAsync(TimeSpan.FromSeconds(15));
        Console.WriteLine("Blob lease acquired. LeaseId = {0}", blobLease.LeaseId);

        // Set the request condition to include the lease ID.
        BlobUploadOptions blobUploadOptions = new BlobUploadOptions()
        {
            Conditions = new BlobRequestConditions()
            {
                LeaseId = blobLease.LeaseId
            }
        };

        // Write to the blob again, providing the lease ID on the request.
        // The lease ID was provided, so this call should succeed.
        string blobContents2 = "Second update. Lease ID provided on request.";
        byteArray = Encoding.ASCII.GetBytes(blobContents2);

        using (MemoryStream stream = new MemoryStream(byteArray))
        {
            BlobContentInfo blobContentInfo = await blobClient.UploadAsync(stream, blobUploadOptions);
        }

        // This code simulates an update by another client.
        // The lease ID is not provided, so this call fails.
        string blobContents3 = "Third update. No lease ID provided.";
        byteArray = Encoding.ASCII.GetBytes(blobContents3);

        using (MemoryStream stream = new MemoryStream(byteArray))
        {
            // This call should fail with error code 412 (Precondition Failed).
            BlobContentInfo blobContentInfo = await blobClient.UploadAsync(stream);
        }
    }
    catch (RequestFailedException e)
    {
        if (e.Status == (int)HttpStatusCode.PreconditionFailed)
        {
            Console.WriteLine(
                @"Precondition failure as expected. The lease ID was not provided.");
        }
        else
        {
            Console.WriteLine(e.Message);
            throw;
        }
    }
    finally
    {
        await blobLeaseClient.ReleaseAsync();
    }
}

Pessimistisk konkurrens för containrar

Hyror på containrar möjliggör samma synkroniseringsstrategier som stöds för blobbar, inklusive exklusiv skrivning/delad läsning, exklusiv skrivning/exklusiv läsning och delad skrivning/exklusiv läsning. För containrar framtvingas dock det exklusiva låset endast vid borttagningsåtgärder. Om du vill ta bort en container med ett aktivt lån måste en klient inkludera det aktiva låne-ID:t med borttagningsbegäran. Alla andra containeråtgärder är framgångsrika på en container med hyra, utan hyres-ID.

Nästa steg

Resurser

Relaterade kodexempel med inaktuella .NET version 11.x SDK:er finns i Kodexempel med .NET version 11.x.