

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Bewährte Methoden für den Umgang mit gleichzeitigen Updates in DynamoDB
<a name="BestPractices_ImplementingVersionControl"></a>

In verteilten Systemen können mehrere Prozesse oder Benutzer versuchen, dieselben Daten gleichzeitig zu ändern. Ohne Kontrolle der Parallelität können diese gleichzeitigen Schreibvorgänge dazu führen, dass Aktualisierungen verloren gehen, Daten inkonsistent sind oder es zu Wettläufen kommt. DynamoDB bietet mehrere Mechanismen, mit denen Sie den gleichzeitigen Zugriff verwalten und die Datenintegrität aufrechterhalten können.

**Anmerkung**  
Einzelne Schreibvorgänge `UpdateItem` sind z. B. atomar und werden unabhängig von der Parallelität immer mit der neuesten Version des Elements ausgeführt. Sperrstrategien sind erforderlich, wenn Ihre Anwendung ein Element lesen und es dann basierend auf dem gelesenen Wert zurückschreiben muss (ein read-modify-write Zyklus), da ein anderer Prozess das Element zwischen dem Lesen und dem Schreiben ändern könnte.

Es gibt zwei Hauptstrategien für den Umgang mit gleichzeitigen Aktualisierungen:
+ **Optimistisches Sperren** — Geht davon aus, dass Konflikte selten sind. Es ermöglicht gleichzeitigen Zugriff und erkennt Konflikte beim Schreiben mithilfe von bedingten Schreibvorgängen. Wenn ein Konflikt erkannt wird, schlägt der Schreibvorgang fehl und die Anwendung kann es erneut versuchen.
+ **Pessimistisches Sperren** — Geht davon aus, dass Konflikte wahrscheinlich sind. Es verhindert den gleichzeitigen Zugriff, indem es sich exklusiven Zugriff auf eine Ressource verschafft, bevor sie geändert wird. Andere Prozesse müssen warten, bis die Sperre aufgehoben wird.

In der folgenden Tabelle sind die in DynamoDB verfügbaren Ansätze zusammengefasst:


| Ansatz | Mechanismus | Am besten geeignet für | 
| --- | --- | --- | 
| Optimistische Sperre | Versionsattribut \$1 bedingte Schreibvorgänge | Geringer Streitwert, kostengünstige Wiederholungsversuche | 
| Pessimistisches Sperren (Transaktionen) | TransactWriteItems | Atomarität mehrerer Gegenstände, mäßiger Streit | 
| Pessimistisches Sperren (Lockclient) | Dedizierte Sperrtabelle mit Lease und Heartbeat | Langfristige Workflows, verteilte Koordination | 

# Optimistische Sperre mit Versionsnummer
<a name="BestPractices_OptimisticLocking"></a>

Optimistisches Sperren ist eine Strategie, die Konflikte beim Schreiben erkennt, anstatt sie zu verhindern. Jedes Element enthält ein Versionsattribut, das mit jeder Aktualisierung erhöht wird. Wenn Sie ein Element aktualisieren, fügen Sie einen [Bedingungsausdruck hinzu](Expressions.ConditionExpressions.md), der überprüft, ob die Versionsnummer mit dem Wert übereinstimmt, den Ihre Anwendung zuletzt gelesen hat. Wenn das Element in der Zwischenzeit von einem anderen Prozess geändert wurde, schlägt die Bedingung fehl und DynamoDB gibt a zurück. `ConditionalCheckFailedException`

## Wann sollte optimistisches Sperren verwendet werden
<a name="BestPractices_OptimisticLocking_WhenToUse"></a>

Optimistisches Sperren eignet sich gut, wenn:
+ Möglicherweise aktualisieren mehrere Benutzer oder Prozesse dasselbe Element, Konflikte treten jedoch selten auf.
+ Ein erneuter Versuch, einen fehlgeschlagenen Schreibvorgang zu wiederholen, ist für Ihre Anwendung kostengünstig.
+ Sie möchten den Aufwand und die Komplexität der Verwaltung verteilter Sperren vermeiden.

Zu den häufigsten Beispielen gehören Inventaraktualisierungen im E-Commerce, Plattformen für die gemeinsame Bearbeitung und Aufzeichnungen über Finanztransaktionen.

## Kompromisse
<a name="BestPractices_OptimisticLocking_Tradeoffs"></a>

**Mehraufwand bei hohem Datenkonflikt erneut versuchen**  
In Umgebungen mit hoher Parallelität steigt die Wahrscheinlichkeit von Konflikten, was zu mehr Wiederholungen und höheren Schreibkosten führen kann

**Komplexität der Implementierung**  
Das Hinzufügen von Versionskontrollen zu Elementen und die Handhabung bedingter Prüfungen erhöhen die Komplexität der Anwendungslogik. Das AWS SDK for Java v2 Enhanced Client bietet integrierte Unterstützung durch die [https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE)Anmerkung, die Versionsnummern automatisch für Sie verwaltet.

## Musterdesign
<a name="BestPractices_OptimisticLocking_PatternDesign"></a>

Fügen Sie jedem Element ein Versionsattribut hinzu. Hier ist ein einfacher Schemaentwurf:
+ Partitionsschlüssel — Ein eindeutiger Bezeichner für jedes Element (z. B.`ItemId`).
+ Attribute:
  + `ItemId` – der eindeutige Bezeichner für das Element
  + `Version` – eine Ganzzahl, die die Versionsnummer des Elements darstellt
  + `QuantityLeft` – der verbleibende Bestand des Elements

Wenn ein Element zum ersten Mal erstellt wird, wird das Attribut `Version` auf 1 gesetzt. Mit jeder Aktualisierung wird die Versionsnummer um 1 erhöht.


| ItemID (Partitionsschlüssel) | Version | QuantityLeft | 
| --- | --- | --- | 
| Bananen | 1 | 10 | 
| Äpfel | 1 | 5 | 
| Orangen | 1 | 7 | 

## Implementierung
<a name="BestPractices_OptimisticLocking_Implementation"></a>

Gehen Sie wie folgt vor, um optimistisches Sperren zu implementieren:

1. Die aktuelle Version des Elements.

   ```
   def get_item(item_id):
       response = table.get_item(Key={'ItemID': item_id})
       return response['Item']
   
   item = get_item('Bananas')
   current_version = item['Version']
   ```

1. Aktualisieren Sie das Element mithilfe eines Bedingungsausdrucks, der die Versionsnummer überprüft.

   ```
   def update_item(item_id, qty_bought, current_version):
       try:
           response = table.update_item(
               Key={'ItemID': item_id},
               UpdateExpression="SET QuantityLeft = QuantityLeft - :qty, Version = :new_v",
               ConditionExpression="Version = :expected_v",
               ExpressionAttributeValues={
                   ':qty': qty_bought,
                   ':new_v': current_version + 1,
                   ':expected_v': current_version
               },
               ReturnValues="UPDATED_NEW"
           )
           return response
       except ClientError as e:
           if e.response['Error']['Code'] == 'ConditionalCheckFailedException':
               print("Version conflict: another process updated this item.")
           raise
   ```

1. Behandeln Sie Konflikte, indem Sie es erneut mit einem neuen Lesevorgang versuchen.

   Für jeden erneuten Versuch ist ein zusätzlicher Lesevorgang erforderlich. Beschränken Sie daher die Gesamtzahl der Wiederholungen.

   ```
   def update_with_retry(item_id, qty_bought, max_retries=3):
       for attempt in range(max_retries):
           item = get_item(item_id)
           try:
               return update_item(item_id, qty_bought, item['Version'])
           except ClientError as e:
               if e.response['Error']['Code'] != 'ConditionalCheckFailedException':
                   raise
               print(f"Retry {attempt + 1}/{max_retries}")
       raise Exception("Update failed after maximum retries.")
   ```

Für Java-Anwendungen bietet das AWS SDK for Java v2 Enhanced Client integrierte Unterstützung für optimistisches Sperren über die [https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-extensions.html#ddb-en-client-extensions-VRE)Anmerkung, die Versionsnummern automatisch für Sie verwaltet.

Weitere Hinweise zu Bedingungsausdrücken finden Sie unter[CLI-Beispiel für DynamoDB-Bedingungsausdrücke](Expressions.ConditionExpressions.md).

# Pessimistisches Sperren mit DynamoDB-Transaktionen
<a name="BestPractices_PessimisticLocking"></a>

[DynamoDB-Transaktionen](transactions.md) bieten einen all-or-nothing Ansatz für gruppierte Operationen. Wenn Sie verwenden`TransactWriteItems`, überwacht DynamoDB alle Elemente in der Transaktion. Wenn ein Element während der Transaktion durch einen anderen Vorgang geändert wird, wird die gesamte Transaktion storniert und DynamoDB gibt a zurück. `TransactionCanceledException` Dieses Verhalten bietet eine Form der pessimistischen Parallelitätskontrolle, da widersprüchliche gleichzeitige Änderungen verhindert und nicht erst im Nachhinein erkannt werden.

## Wann sollten Transaktionen zum Sperren verwendet werden
<a name="BestPractices_PessimisticLocking_WhenToUse"></a>

Transaktionen eignen sich gut, wenn:
+ Sie müssen mehrere Elemente atomar aktualisieren, entweder innerhalb derselben Tabelle oder tabellenübergreifend.
+ Ihre Geschäftslogik erfordert all-or-nothing Semantik — entweder sind alle Änderungen erfolgreich oder es werden keine angewendet.

Zu den häufigsten Beispielen gehören der Transfer von Geld zwischen Konten, das Aufgeben von Bestellungen, mit denen sowohl das Inventar als auch die Bestelltabelle aktualisiert werden, und der Austausch von Gegenständen zwischen Spielern in einem Spiel.

## Kompromisse
<a name="BestPractices_PessimisticLocking_Tradeoffs"></a>

**Höhere Schreibkosten**  
Bei Elementen bis zu 1 KB verbrauchen Transaktionen 2 WCUs pro Element (eine für die Vorbereitung, eine für die Übertragung), verglichen mit 1 WCU für einen Standardschreibvorgang.

**Limit für Artikel**  
Eine einzelne Transaktion kann bis zu 100 Aktionen in einer oder mehreren Tabellen umfassen.

**Konfliktsensitivität**  
Wenn ein Element in der Transaktion durch einen anderen Vorgang geändert wird, schlägt die gesamte Transaktion fehl. In Szenarien mit hohem Streitaufkommen kann dies zu häufigen Stornierungen führen.

## Implementierung
<a name="BestPractices_PessimisticLocking_Implementation"></a>

Das folgende Beispiel verwendet die atomare Übertragung `TransactWriteItems` von Inventar zwischen zwei Artikeln. Wenn ein anderer Prozess einen der Artikel während der Transaktion ändert, wird der gesamte Vorgang rückgängig gemacht.

```
import boto3

client = boto3.client('dynamodb')

def transfer_inventory(source_id, target_id, quantity):
    try:
        client.transact_write_items(
            TransactItems=[
                {
                    'Update': {
                        'TableName': 'Inventory',
                        'Key': {'ItemID': {'S': source_id}},
                        'UpdateExpression': 'SET QuantityLeft = QuantityLeft - :qty',
                        'ConditionExpression': 'QuantityLeft >= :qty',
                        'ExpressionAttributeValues': {
                            ':qty': {'N': str(quantity)}
                        }
                    }
                },
                {
                    'Update': {
                        'TableName': 'Inventory',
                        'Key': {'ItemID': {'S': target_id}},
                        'UpdateExpression': 'SET QuantityLeft = QuantityLeft + :qty',
                        'ExpressionAttributeValues': {
                            ':qty': {'N': str(quantity)}
                        }
                    }
                }
            ]
        )
        return True
    except client.exceptions.TransactionCanceledException as e:
        print(f"Transaction canceled: {e}")
        return False
```

In diesem Beispiel überprüft der Bedingungsausdruck, ob ausreichend Inventar vorhanden ist, aber kein Versionsattribut erforderlich ist. DynamoDB bricht die Transaktion automatisch ab, wenn ein Element in der Transaktion durch einen anderen Vorgang zwischen den Vorbereitungs- und Übertragungsphasen geändert wird. Dies ermöglicht die pessimistische Parallelitätskontrolle — widersprüchliche gleichzeitige Änderungen werden durch die Transaktion selbst verhindert.

**Anmerkung**  
Sie können Transaktionen mit optimistischem Sperren kombinieren, indem Sie Versionsprüfungen als zusätzliche Bedingungsausdrücke hinzufügen. Dies bietet eine zusätzliche Schutzebene, ist jedoch nicht erforderlich, damit die Transaktion Konflikte erkennt.

Weitere Informationen finden Sie unter [Verwalten komplexer Workflows mit DynamoDB-Transaktionen](transactions.md).

# Verteiltes Sperren mit dem DynamoDB Lock Client
<a name="BestPractices_DistributedLocking"></a>

Für Anwendungen, die traditionelle lock-acquire-release Semantik erfordern, ist der DynamoDB Lock Client eine Open-Source-Bibliothek, die verteiltes Sperren mithilfe einer DynamoDB-Tabelle als Sperrspeicher implementiert. Dieser Ansatz ist nützlich, wenn Sie den Zugriff auf eine externe Ressource (z. B. ein S3-Objekt oder eine gemeinsam genutzte Konfiguration) über mehrere Anwendungsinstanzen hinweg koordinieren müssen.

Der Lock-Client ist als [Open-Source-Java-Bibliothek](https://github.com/awslabs/amazon-dynamodb-lock-client) verfügbar.

## Funktionsweise
<a name="BestPractices_DistributedLocking_HowItWorks"></a>

Der Lock-Client verwendet eine dedizierte DynamoDB-Tabelle, um Sperren zu verfolgen. Jede Sperre wird als ein Element mit den folgenden Schlüsselattributen dargestellt:
+ Ein Partitionsschlüssel, der die gesperrte Ressource identifiziert.
+ Eine Leasingdauer, die angibt, wie lange die Sperre gültig ist. Wenn der Schlosshalter abstürzt oder nicht mehr reagiert, läuft die Sperre nach Ablauf der Leasingdauer automatisch ab.
+ Ein Taktsignal, das der Schlossinhaber regelmäßig sendet, um den Leasingvertrag zu verlängern. Dadurch wird verhindert, dass die Sperre abläuft, während der Inhaber die Bearbeitung noch aktiv durchführt.

Der Lock-Client verwendet bedingte Schreibvorgänge, um sicherzustellen, dass jeweils nur ein Prozess eine Sperre erhalten kann. Wenn eine Sperre bereits aktiviert ist, kann der Aufrufer wählen, ob er warten und es erneut versuchen möchte oder ob er sofort fehlschlagen möchte.

## Wann sollte der Lock-Client verwendet werden
<a name="BestPractices_DistributedLocking_WhenToUse"></a>

Der Lock-Client eignet sich gut, wenn:
+ Sie müssen den Zugriff auf eine gemeinsam genutzte Ressource über mehrere Anwendungsinstanzen oder Microservices hinweg koordinieren.
+ Der kritische Abschnitt dauert lange (Sekunden bis Minuten), und im Konfliktfall wäre ein erneuter Versuch, den gesamten Vorgang zu wiederholen, teuer.
+ Sie benötigen ein automatisches Ablaufen der Sperren, um Prozessfehler ordnungsgemäß behandeln zu können.

Zu den häufigsten Beispielen gehören die Orchestrierung verteilter Workflows, die Koordination von Cron-Jobs über mehrere Instanzen hinweg und die Verwaltung des Zugriffs auf gemeinsam genutzte externe Ressourcen.

## Kompromisse
<a name="BestPractices_DistributedLocking_Tradeoffs"></a>

**Zusätzliche Infrastruktur**  
Erfordert eine dedizierte DynamoDB-Tabelle für die Sperrverwaltung mit zusätzlicher Lese- und Schreibkapazität für Sperroperationen und Heartbeats.

**Abhängigkeit von der Uhr**  
Das Ablaufen von Sperren hängt von Zeitstempeln ab. Ein erheblicher Zeitversatz zwischen den Clients kann zu unerwartetem Verhalten führen, insbesondere bei kurzen Leasingdauern.

**Deadlock-Risiko**  
Wenn Ihre Anwendung Sperren für mehrere Ressourcen erwirbt, müssen Sie diese in einer konsistenten Reihenfolge aktivieren, um Deadlocks zu vermeiden. Die Leasingdauer bietet ein Sicherheitsnetz, da Sperren von Inhabern, die nicht darauf reagieren, automatisch aufgehoben werden.

## Implementierung
<a name="BestPractices_DistributedLocking_Implementation"></a>

Das folgende Beispiel zeigt, wie der DynamoDB Lock Client verwendet wird, um eine Sperre zu erwerben und aufzuheben:

```
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

final DynamoDbClient dynamoDB = DynamoDbClient.builder()
    .region(Region.US_WEST_2)
    .build();

final AmazonDynamoDBLockClient lockClient = new AmazonDynamoDBLockClient(
    AmazonDynamoDBLockClientOptions.builder(dynamoDB, "Locks")
        .withTimeUnit(TimeUnit.SECONDS)
        .withLeaseDuration(10L)
        .withHeartbeatPeriod(3L)
        .withCreateHeartbeatBackgroundThread(true)
        .build());

// Try to acquire a lock on a resource
final Optional<LockItem> lock =
    lockClient.tryAcquireLock(AcquireLockOptions.builder("my-shared-resource").build());

if (lock.isPresent()) {
    try {
        // Perform operations that require exclusive access
        processSharedResource();
    } finally {
        // Always release the lock when done
        lockClient.releaseLock(lock.get());
    }
} else {
    System.out.println("Failed to acquire lock.");
}

lockClient.close();
```

**Wichtig**  
Geben Sie immer Sperren in einem `finally` Block auf, um sicherzustellen, dass Sperren auch dann aufgehoben werden, wenn Ihre Verarbeitungslogik eine Ausnahme auslöst. Nicht freigegebene Sperren blockieren andere Prozesse, bis das Leasing abläuft.

Sie können auch einen einfachen Sperrmechanismus ohne die Lock-Client-Bibliothek implementieren, indem Sie bedingte Schreibvorgänge direkt verwenden. Im folgenden Beispiel wird `UpdateItem` mit einem Bedingungsausdruck eine Sperre `DeleteItem` abgerufen und aufgehoben:

```
from datetime import datetime, timedelta
from boto3.dynamodb.conditions import Attr

def acquire_lock(table, resource_name, owner_id, ttl_seconds):
    """Attempt to acquire a lock. Returns True if successful."""
    expiry = (datetime.now() + timedelta(seconds=ttl_seconds)).isoformat()
    now = datetime.now().isoformat()
    try:
        table.update_item(
            Key={'LockID': resource_name},
            UpdateExpression='SET #owner = :owner, #expiry = :expiry',
            ConditionExpression=Attr('LockID').not_exists() | Attr('ExpiresAt').lt(now),
            ExpressionAttributeNames={'#owner': 'OwnerID', '#expiry': 'ExpiresAt'},
            ExpressionAttributeValues={':owner': owner_id, ':expiry': expiry}
        )
        return True
    except table.meta.client.exceptions.ConditionalCheckFailedException:
        return False

def release_lock(table, resource_name, owner_id):
    """Release a lock. Only succeeds if the caller is the lock owner."""
    try:
        table.delete_item(
            Key={'LockID': resource_name},
            ConditionExpression=Attr('OwnerID').eq(owner_id)
        )
        return True
    except table.meta.client.exceptions.ConditionalCheckFailedException:
        return False
```

Dieser Ansatz verwendet einen Bedingungsausdruck, um sicherzustellen, dass eine Sperre nur dann erworben werden kann, wenn sie nicht existiert oder abgelaufen ist, und dass sie nur von dem Prozess freigegeben werden kann, der sie erworben hat. Erwägen Sie[, Time to Live (TTL)](TTL.md) in der Sperrtabelle zu aktivieren, um abgelaufene Sperrobjekte automatisch zu bereinigen.

## Wählen Sie eine Strategie zur Kontrolle der Parallelität
<a name="BestPractices_ChoosingLockingStrategy"></a>

Verwenden Sie die folgenden Richtlinien, um den richtigen Ansatz für Ihren Workload zu wählen:

**Verwenden Sie optimistisches Sperren** in folgenden Fällen:  
+ Konflikte sind selten.
+ Der erneute Versuch, einen fehlgeschlagenen Schreibvorgang zu wiederholen, ist kostengünstig.
+ Sie aktualisieren jeweils ein einzelnes Element.

**Verwenden Sie Transaktionen**, wenn:  
+ Sie müssen mehrere Elemente atomar aktualisieren.
+ Sie benötigen all-or-nothing Semantik für alle Elemente oder Tabellen.
+ Sie müssen Bedingungsprüfungen mit Schreibvorgängen in einem einzigen Vorgang kombinieren.

**Verwenden Sie den Lock-Client**, wenn:  
+ Sie müssen den Zugriff auf externe Ressourcen über verteilte Prozesse hinweg koordinieren.
+ Der kritische Abschnitt ist langwierig und es ist teuer, bei Konflikten erneut zu versuchen.
+ Sie benötigen ein automatisches Ablaufen der Sperren, um Prozessfehler zu beheben.

**Anmerkung**  
Wenn Sie [globale DynamoDB-Tabellen](GlobalTables.md) verwenden, beachten Sie, dass globale Tabellen für gleichzeitige Aktualisierungen eine Abstimmungsstrategie verwenden, bei der der letzte Writer gewinnt. Optimistisches Sperren mit Versionsnummern funktioniert regionsübergreifend nicht wie erwartet, da ein Schreibvorgang in einer Region einen gleichzeitigen Schreibvorgang in einer anderen Region ohne Versionsprüfung überschreiben kann. Entwerfen Sie Ihre Anwendung so, dass sie Konflikte auf Anwendungsebene behandelt, wenn Sie globale Tabellen verwenden.