QLDBAmazon-Treiber für Go — Kochbuch-Referenz - Amazon Quantum Ledger-Datenbank (AmazonQLDB)

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.

QLDBAmazon-Treiber für Go — Kochbuch-Referenz

Wichtig

Hinweis zum Ende des Supports: Bestandskunden können Amazon QLDB bis zum Ende des Supports am 31.07.2025 nutzen. Weitere Informationen finden Sie unter Migrieren eines Amazon QLDB Ledgers zu Amazon Aurora SQL Postgre.

Dieses Referenzhandbuch zeigt allgemeine Anwendungsfälle des QLDB Amazon-Treibers für Go. Es enthält Go-Codebeispiele, die zeigen, wie der Treiber verwendet wird, um grundlegende Erstellungs-, Lese-, Aktualisierungs- und Löschvorgänge (CRUD) auszuführen. Es enthält auch Codebeispiele für die Verarbeitung von Amazon Ion-Daten. Darüber hinaus werden in diesem Leitfaden bewährte Verfahren zur idempotenten Gestaltung von Transaktionen und zur Implementierung von Eindeutigkeitsbeschränkungen beschrieben.

Anmerkung

Gegebenenfalls gibt es in einigen Anwendungsfällen unterschiedliche Codebeispiele für jede unterstützte Hauptversion des QLDB Treibers für Go.

Der Treiber wird importiert

Das folgende Codebeispiel importiert den Treiber und andere erforderliche AWS Pakete.

3.x
import ( "github.com/amzn/ion-go/ion" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/qldbSession" "github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver" )
2.x
import ( "github.com/amzn/ion-go/ion" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/qldbsession" "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver" )
Anmerkung

In diesem Beispiel wird auch das Amazon Ion-Paket (amzn/ion-go/ion) importiert. Sie benötigen dieses Paket, um Ion-Daten zu verarbeiten, wenn Sie einige Datenoperationen in dieser Referenz ausführen. Weitere Informationen hierzu finden Sie unter Arbeiten mit Amazon Ion.

Der Treiber wird instanziiert

Im folgenden Codebeispiel wird eine Instanz des Treibers erstellt, die eine Verbindung zu einem bestimmten Ledgernamen in einem angegebenen Verzeichnis herstellt. AWS-Region

3.x
cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } qldbSession := qldbsession.NewFromConfig(cfg, func(options *qldbsession.Options) { options.Region = "us-east-1" }) driver, err := qldbdriver.New( "vehicle-registration", qldbSession, func(options *qldbdriver.DriverOptions) { options.LoggerVerbosity = qldbdriver.LogInfo }) if err != nil { panic(err) } defer driver.Shutdown(context.Background())
2.x
awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1"))) qldbSession := qldbsession.New(awsSession) driver, err := qldbdriver.New( "vehicle-registration", qldbSession, func(options *qldbdriver.DriverOptions) { options.LoggerVerbosity = qldbdriver.LogInfo }) if err != nil { panic(err) }

CRUDOperationen

QLDBführt die Operationen create, read, update und delete (CRUD) als Teil einer Transaktion aus.

Warnung

Als bewährte Methode sollten Sie Ihre Schreibtransaktionen strikt idempotent gestalten.

Transaktionen idempotent machen

Wir empfehlen, Schreibtransaktionen idempotent zu machen, um unerwartete Nebenwirkungen bei Wiederholungsversuchen zu vermeiden. Eine Transaktion ist idempotent, wenn sie mehrfach ausgeführt werden kann und jedes Mal identische Ergebnisse liefert.

Stellen Sie sich zum Beispiel eine Transaktion vor, die ein Dokument in eine Tabelle mit dem Namen einfügt. Person Bei der Transaktion sollte zunächst geprüft werden, ob das Dokument bereits in der Tabelle vorhanden ist. Ohne diese Prüfung könnte die Tabelle doppelte Dokumente enthalten.

Nehmen wir an, die Transaktion wird serverseitig QLDB erfolgreich festgeschrieben, aber der Client läuft ab, während er auf eine Antwort wartet. Wenn die Transaktion nicht idempotent ist, könnte dasselbe Dokument bei einem erneuten Versuch mehrmals eingefügt werden.

Verwendung von Indizes zur Vermeidung vollständiger Tabellenscans

Es wird außerdem empfohlen, Anweisungen mit einer WHERE Prädikatklausel unter Verwendung eines Gleichheitsoperators für ein indiziertes Feld oder eine Dokument-ID auszuführen, z. B. oder. WHERE indexedField = 123 WHERE indexedField IN (456, 789) Ohne diese indizierte Suche QLDB muss ein Tabellenscan durchgeführt werden, was zu Transaktions-Timeouts oder Konflikten mit optimistischer Parallelitätssteuerung () führen kann. OCC

Mehr über OCC erfahren Sie unter QLDBAmazon-Parallelitätsmodell.

Implizit erstellte Transaktionen

Die Funktion QLDBDriver.Execute akzeptiert eine Lambda-Funktion, die eine Instanz von Transaction empfängt, mit der Sie Anweisungen ausführen können. Die Instanz von Transaction umschließt eine implizit erstellte Transaktion.

Sie können Anweisungen innerhalb der Lambda-Funktion ausführen, indem Sie die Funktion verwenden. Transaction.Execute Der Treiber schreibt die Transaktion implizit fest, wenn die Lambda-Funktion zurückkehrt.

In den folgenden Abschnitten wird gezeigt, wie grundlegende CRUD Operationen ausgeführt, eine benutzerdefinierte Wiederholungslogik angegeben und Eindeutigkeitsbeschränkungen implementiert werden.

Erstellen von Tabellen

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE TABLE Person") })

Erstellen von Indizes

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE INDEX ON Person(GovId)") })

Dokumente lesen

var decodedResult map[string]interface{} // Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName": "Brent" } _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'") if err != nil { return nil, err } for result.Next(txn) { ionBinary := result.GetCurrentData() err = ion.Unmarshal(ionBinary, &decodedResult) if err != nil { return nil, err } fmt.Println(decodedResult) // prints map[GovId: TOYENC486FH FirstName:Brent] } if result.Err() != nil { return nil, result.Err() } return nil, nil }) if err != nil { panic(err) }

Verwenden von Abfrageparametern

Das folgende Codebeispiel verwendet einen systemeigenen Abfrageparameter.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("SELECT * FROM Person WHERE GovId = ?", "TOYENC486FH") }) if err != nil { panic(err) }

Das folgende Codebeispiel verwendet mehrere Abfrageparameter.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", "TOYENC486FH", "Brent") }) if err != nil { panic(err) }

Das folgende Codebeispiel verwendet eine Liste von Abfrageparametern.

govIDs := []string{}{"TOYENC486FH", "ROEE1", "YH844"} result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", govIDs...) }) if err != nil { panic(err) }
Anmerkung

Wenn Sie eine Abfrage ohne indizierte Suche ausführen, wird ein vollständiger Tabellenscan aufgerufen. In diesem Beispiel empfehlen wir, einen Index für das GovId Feld zu verwenden, um die Leistung zu optimieren. Ohne aktivierten GovId Index können Abfragen eine höhere Latenz haben und auch zu OCC Konfliktausnahmen oder Transaktions-Timeouts führen.

Dokumente werden eingefügt

Im folgenden Codebeispiel werden systemeigene Datentypen eingefügt.

_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { // Check if a document with a GovId of TOYENC486FH exists // This is critical to make this transaction idempotent result, err := txn.Execute("SELECT * FROM Person WHERE GovId = ?", "TOYENC486FH") if err != nil { return nil, err } // Check if there are any results if result.Next(txn) { // Document already exists, no need to insert } else { person := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } _, err = txn.Execute("INSERT INTO Person ?", person) if err != nil { return nil, err } } return nil, nil })

Diese Transaktion fügt ein Dokument in die Person Tabelle ein. Vor dem Einfügen wird zunächst geprüft, ob das Dokument bereits in der Tabelle vorhanden ist. Diese Prüfung macht die Transaktion ihrem Wesen nach idempotent. Selbst wenn Sie diese Transaktion mehrmals ausführen, hat sie keine unbeabsichtigten Nebenwirkungen.

Anmerkung

In diesem Beispiel empfehlen wir, einen Index für das GovId Feld zu verwenden, um die Leistung zu optimieren. Wenn ein Index nicht aktiviert istGovId, können Anweisungen eine längere Latenz haben und auch zu OCC Konfliktausnahmen oder Transaktions-Timeouts führen.

Einfügen mehrerer Dokumente in eine Anweisung

Um mehrere Dokumente mit einer einzigen INSERT Anweisung einzufügen, können Sie der Anweisung wie folgt einen Parameter vom Typ list übergeben.

// people is a list txn.Execute("INSERT INTO People ?", people)

Sie setzen den Platzhalter für die Variable (?) nicht in doppelte spitze Klammern (<<...>>), wenn Sie eine Liste übergeben. In manuellen PartiQL-Anweisungen bezeichnen doppelte spitze Klammern eine ungeordnete Sammlung, die als Tasche bezeichnet wird.

Dokumente werden aktualisiert

Das folgende Codebeispiel verwendet native Datentypen.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", "John", "TOYENC486FH") })
Anmerkung

In diesem Beispiel empfehlen wir, einen Index für das GovId Feld zu verwenden, um die Leistung zu optimieren. Wenn ein Index nicht aktiviert istGovId, können Anweisungen eine längere Latenz haben und auch zu OCC Konfliktausnahmen oder Transaktions-Timeouts führen.

Dokumente löschen

Im folgenden Codebeispiel werden systemeigene Datentypen verwendet.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("DELETE FROM Person WHERE GovId = ?", "TOYENC486FH") })
Anmerkung

In diesem Beispiel empfehlen wir, einen Index für das GovId Feld zu verwenden, um die Leistung zu optimieren. Wenn ein Index nicht aktiviert istGovId, können Anweisungen eine längere Latenz haben und auch zu OCC Konfliktausnahmen oder Transaktions-Timeouts führen.

Ausführung mehrerer Anweisungen in einer Transaktion

// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. func InsureCar(driver *qldbdriver.QLDBDriver, vin string) (bool, error) { insured, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin) if err != nil { return false, err } hasNext := result.Next(txn) if !hasNext && result.Err() != nil { return false, result.Err() } if hasNext { _, err = txn.Execute( "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin) if err != nil { return false, err } return true, nil } return false, nil }) if err != nil { panic(err) } return insured.(bool), err }

Logik für Wiederholversuche

Die Execute Treiberfunktion verfügt über einen integrierten Wiederholungsmechanismus, der die Transaktion wiederholt, wenn eine Ausnahme auftritt, die wiederholt werden kann (z. B. Timeouts oder Konflikte). OCC Die maximale Anzahl von Wiederholungsversuchen und die Backoff-Strategie sind konfigurierbar.

Das Standardlimit für Wiederholungsversuche ist4, und die Standard-Backoff-Strategie basiert ExponentialBackoffStrategyauf einer Basis von Millisekunden. 10 Sie können die Wiederholungsrichtlinie pro Treiberinstanz und auch pro Transaktion festlegen, indem Sie eine Instanz von verwenden. RetryPolicy

Das folgende Codebeispiel spezifiziert die Wiederholungslogik mit einem benutzerdefinierten Wiederholungslimit und einer benutzerdefinierten Backoff-Strategie für eine Instanz des Treibers.

import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/qldbsession" "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver" ) func main() { awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1"))) qldbSession := qldbsession.New(awsSession) // Configuring retry limit to 2 retryPolicy := qldbdriver.RetryPolicy{MaxRetryLimit: 2} driver, err := qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) { options.RetryPolicy = retryPolicy }) if err != nil { panic(err) } // Configuring an exponential backoff strategy with base of 20 milliseconds retryPolicy = qldbdriver.RetryPolicy{ MaxRetryLimit: 2, Backoff: qldbdriver.ExponentialBackoffStrategy{SleepBase: 20, SleepCap: 4000, }} driver, err = qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) { options.RetryPolicy = retryPolicy }) if err != nil { panic(err) } }

Das folgende Codebeispiel spezifiziert die Wiederholungslogik mit einem benutzerdefinierten Wiederholungslimit und einer benutzerdefinierten Backoff-Strategie für eine bestimmte anonyme Funktion. Die SetRetryPolicy Funktion überschreibt die Wiederholungsrichtlinie, die für die Treiberinstanz festgelegt ist.

import ( "context" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/qldbsession" "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver" ) func main() { awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1"))) qldbSession := qldbsession.New(awsSession) // Configuring retry limit to 2 retryPolicy1 := qldbdriver.RetryPolicy{MaxRetryLimit: 2} driver, err := qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) { options.RetryPolicy = retryPolicy1 }) if err != nil { panic(err) } // Configuring an exponential backoff strategy with base of 20 milliseconds retryPolicy2 := qldbdriver.RetryPolicy{ MaxRetryLimit: 2, Backoff: qldbdriver.ExponentialBackoffStrategy{SleepBase: 20, SleepCap: 4000, }} // Overrides the retry policy set by the driver instance driver.SetRetryPolicy(retryPolicy2) driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE TABLE Person") }) }

Implementierung von Eindeutigkeitsbeschränkungen

QLDBunterstützt keine eindeutigen Indizes, aber Sie können dieses Verhalten in Ihrer Anwendung implementieren.

Angenommen, Sie möchten eine Eindeutigkeitsbeschränkung für das GovId Feld in der Person Tabelle implementieren. Zu diesem Zweck können Sie eine Transaktion schreiben, die Folgendes tut:

  1. Bestätigen Sie, dass die Tabelle keine vorhandenen Dokumente mit einem angegebenen Wert enthältGovId.

  2. Fügt das Dokument ein, wenn die Assertion erfolgreich ist.

Wenn eine konkurrierende Transaktion gleichzeitig die Assertion besteht, wird nur eine der Transaktionen erfolgreich festgeschrieben. Die andere Transaktion schlägt mit einer OCC Konfliktausnahme fehl.

Das folgende Codebeispiel zeigt, wie diese Eindeutigkeitsbeschränkungslogik implementiert wird.

govID := "TOYENC486FH" document := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { // Check if doc with GovId = govID exists result, err := txn.Execute("SELECT * FROM Person WHERE GovId = ?", govID) if err != nil { return nil, err } // Check if there are any results if result.Next(txn) { // Document already exists, no need to insert return nil, nil } return txn.Execute("INSERT INTO Person ?", document) }) if err != nil { panic(err) }
Anmerkung

In diesem Beispiel empfehlen wir, einen Index für das GovId Feld zu verwenden, um die Leistung zu optimieren. Wenn ein Index nicht aktiviert istGovId, können Anweisungen eine längere Latenz haben und auch zu OCC Konfliktausnahmen oder Transaktions-Timeouts führen.

Arbeiten mit Amazon Ion

In den folgenden Abschnitten wird gezeigt, wie das Amazon Ion-Modul zur Verarbeitung von Ion-Daten verwendet wird.

Das Ion-Modul importieren

import "github.com/amzn/ion-go/ion"

Ion-Typen erstellen

Die Ion-Bibliothek für Go unterstützt derzeit das Document Object Model (DOM) nicht, sodass Sie keine Ion-Datentypen erstellen können. Sie können jedoch zwischen systemeigenen Go-Typen und Ion-Binärdateien wechseln, wenn Sie damit arbeiten. QLDB

Ion-Binärdatei herunterladen

aDict := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } ionBytes, err := ion.MarshalBinary(aDict) if err != nil { panic(err) } fmt.Println(ionBytes) // prints [224 1 0 234 238 151 129 131 222 147 135 190 144 133 71 111 118 73 100 137 70 105 114 115 116 78 97 109 101 222 148 138 139 84 79 89 69 78 67 52 56 54 70 72 139 133 66 114 101 110 116]

Ion-Text abrufen

aDict := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } ionBytes, err := ion.MarshalText(aDict) if err != nil { panic(err) } fmt.Println(string(ionBytes)) // prints {FirstName:"Brent",GovId:"TOYENC486FH"}

Weitere Informationen zu Ion finden Sie in der Amazon Ion-Dokumentation unter GitHub. Weitere Codebeispiele für die Arbeit mit Ion in QLDB finden Sie unterArbeiten mit Amazon Ion-Datentypen in Amazon QLDB.