翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。
Go 用 Amazon QLDBドライバー — クックブックリファレンス
重要
サポート終了通知: 既存のお客様は、07/31/2025 のサポート終了QLDBまで Amazon を使用できます。詳細については、「Amazon Ledger QLDB を Amazon Aurora Postgre に移行するSQL
このリファレンスガイドでは、Go 用 Amazon QLDBドライバーの一般的なユースケースについて説明します。ドライバーを使用して基本的な作成、読み取り、更新、削除 (CRUD) オペレーションを実行する方法を示す Go コード例を示します。Amazon Ion データを処理するためのコード例も含まれています。さらに、このガイドでは、トランザクションをべき等にし、一意性の制約を実装するためのベストプラクティスを取り上げています。
注記
該当する場合、一部のユースケースでは、サポートされている Go 用QLDBドライバーのメジャーバージョンごとに異なるコード例があります。
目次
ドライバーのインポート
次のコード例では、ドライバーおよびその他の必要な AWS パッケージをインポートします。
注記
この例では、Amazon Ion パッケージ (amzn/ion-go/ion
) もインポートします。このリファレンスでいくつかのデータオペレーションを実行するときに Ion データを処理するには、このパッケージが必要です。詳細については、「Amazon Ion の操作」を参照してください。
ドライバーのインスタンス化
次のコード例では、指定した台帳名に接続するドライバーのインスタンスを、指定した AWS リージョンに作成します。
CRUD オペレーション
QLDB は、トランザクションの一部として作成、読み取り、更新、削除 (CRUD) オペレーションを実行します。
警告
ベストプラクティスとして、書き込みトランザクションを厳密にべき等にしてください。
トランザクションをべき等にする
再試行の場合に予期しない結果を避けるために、書き込みトランザクションをべき等にすることをお勧めします。トランザクションは、複数回実行して毎回同じ結果を生成できる場合、idempotent です。
例えば、Person
という名前のテーブルにドキュメントを挿入するトランザクションについて考えてみます。トランザクションは、まずドキュメントがテーブル内に既に存在するかどうかをチェックする必要があります。このチェックを行わないと、テーブルに重複するドキュメントができる可能性があります。
がサーバー側でトランザクションをQLDB正常にコミットしたが、レスポンスを待っている間にクライアントがタイムアウトするとします。トランザクションがべき等でない場合、再試行時に同じドキュメントが複数回挿入される可能性があります。
インデックスを使用してテーブル全体のスキャンを回避する
インデックス付きフィールドまたはドキュメント ID で等価演算子を使用する WHERE
述語句でステートメントを実行する (例: WHERE indexedField = 123
または WHERE indexedField IN (456, 789)
) こともお勧めします。このインデックス付きルックアップがないと、 はテーブルスキャンを行うQLDB必要があり、トランザクションのタイムアウトやオプティミスティック同時実行制御 () の競合が発生する可能性があります。OCC
OCC の詳細については、「Amazon QLDB 同時実行モデル」を参照してください。
暗黙的に作成されたトランザクション
QLDBDriver.ExecuteTransaction
のインスタンスは、暗黙的に作成されたトランザクションをラップします。
Transaction.Execute
関数を使用して、Lambda 関数内でステートメントを実行できます。ドライバーは、Lambda 関数が戻ったときに暗黙的にトランザクションをコミットします。
以下のセクションでは、基本的なCRUDオペレーションの実行、カスタム再試行ロジックの指定、一意性制約の実装方法について説明します。
目次
テーブルの作成
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE TABLE Person") })
インデックスの作成
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE INDEX ON Person(GovId)") })
ドキュメントの読み取り
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) }
クエリパラメータの使用
次のコード例では、ネイティブ型のクエリパラメータを使用します。
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) }
次のコード例では、複数のクエリパラメータを使用します。
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) }
次のコード例では、クエリパラメータのリストを使用します。
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) }
注記
インデックス付きルックアップなしでクエリを実行すると、完全なテーブルスキャンが呼び出されます。この例では、GovId
フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId
、クエリのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。
ドキュメントの挿入
次のコード例では、ネイティブデータ型を挿入します。
_, 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 })
このトランザクションは、ドキュメントを Person
テーブルに挿入します。挿入する前に、まずドキュメントがテーブル内に既に存在しているかどうかをチェックします。このチェックにより、トランザクションは本質的にべき等になります。このトランザクションを複数回実行しても、意図しない副作用は発生しません。
注記
この例では、GovId
フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId
、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。
1 つのステートメントで複数のドキュメントの挿入
1 つの INSERT ステートメントを使用して複数のドキュメントを挿入するために、次のように型 list のパラメータをステートメントに渡すことができます。
// people is a list txn.Execute("INSERT INTO People ?", people)
リストを渡すときには、変数プレースホルダー (?
) を二重山括弧 (<<...>>
) で囲まないでください。マニュアルの PartiQL ステートメントでは、二重山括弧はバッグと呼ばれる順序付けされていないコレクションを表します。
ドキュメントの更新
次のコード例では、ネイティブデータ型を使用します。
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", "John", "TOYENC486FH") })
注記
この例では、GovId
フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId
、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。
ドキュメントの削除
次のコード例では、ネイティブデータ型を使用します。
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("DELETE FROM Person WHERE GovId = ?", "TOYENC486FH") })
注記
この例では、GovId
フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId
、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。
トランザクションで複数のステートメントの実行
// 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 }
再試行ロジック
ドライバーの Execute
関数には、再試行可能な例外 (タイムアウトやOCC競合など) が発生した場合にトランザクションを再試行する再試行メカニズムが組み込まれています。再試行の最大数とバックオフ戦略を設定できます。
デフォルトの再試行制限は で4
、デフォルトのバックオフ戦略ExponentialBackoffStrategy10
ミリ秒を基準にしています。のインスタンスを使用して、ドライバーインスタンスごと、およびトランザクションごとに再試行ポリシーを設定できますRetryPolicy
次のコード例では、ドライバーのインスタンスのカスタム再試行制限とカスタムバックオフ戦略を使用して再試行ロジックを指定します。
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) } }
次のコード例では、特定の無名関数のカスタム再試行制限とカスタムバックオフ戦略を使用して再試行ロジックを指定します。この SetRetryPolicy
関数は、ドライバーインスタンスに設定された再試行ポリシーを上書きします。
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") }) }
一意性制約の実装
QLDB は一意のインデックスをサポートしていませんが、この動作をアプリケーションに実装できます。
Person
テーブル内の GovId
フィールドに対して一意性制約を実装するとします。これを行うには、以下を実行するトランザクションを記述します。
-
テーブルに指定された
GovId
を持つ既存のドキュメントがないことをアサートします。 -
アサーションに合格した場合は、ドキュメントを挿入します。
競合するトランザクションが同時にアサーションに合格すると、一方のトランザクションだけが正常にコミットされます。他のトランザクションはOCC競合例外で失敗します。
次のコード例は、この一意性制約ロジックを実装する方法を示しています。
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) }
注記
この例では、GovId
フィールドでインデックスを使用して、パフォーマンスを最適化することをお勧めします。にインデックスがないとGovId
、ステートメントのレイテンシーが大きくなり、OCC競合例外やトランザクションタイムアウトが発生する可能性があります。
Amazon Ion の操作
以下のセクションでは、Amazon Ion モジュールを使用して Ion データを処理する方法について説明します。
Ion モジュールのインポート
import "github.com/amzn/ion-go/ion"
Ion 型の作成
現在、Go 用の Ion ライブラリはドキュメントオブジェクトモデル (DOM) をサポートしていないため、Ion データ型を作成することはできません。ただし、 の使用時には、Go ネイティブ型と Ion バイナリの間でマーシャリングとアンマーシャリングを行うことができますQLDB。
Ion バイナリの取得
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 テキストの取得
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"}
Ion の詳細については、「」の Amazon Ion ドキュメント