データテーブルのローカルトランザクションを有効にした後、指定されたパーティションキー値に基づいてローカルトランザクションを作成し、トランザクション内のデータに対して読み取りおよび書き込み操作を実行します。ローカルトランザクションは、1 つ以上の行に対するアトミックな読み取りおよび書き込み操作をサポートします。
シナリオ
ローカル トランザクション機能を使用すると、1 つ以上の行に対してアトミックな読み書き操作を実行できます。以下にシナリオの例を説明します。
シンプルなシナリオ:データの読み書き
読み取り、変更、書き込み (RMW) 操作を実行するには、次の 2 つの方法があります。各方法には特定の制限があります。
-
条件付き更新:一度に単一の行を対象とするリクエストのみを処理します。この方法を使用して、複数のデータ行を含むリクエストや、複数の書き込み操作を含むリクエストを処理することはできません。詳細については、「条件付き更新」をご参照ください。
-
アトミックカウンター:一度に単一の行を対象とするリクエストのみを処理し、列値のインクリメントのみをサポートします。詳細については、「アトミックカウンター機能の使用」をご参照ください。
上記の問題を解決するために、ローカル トランザクションを作成し、パーティションキー値で指定された範囲内のデータに対して RMW 操作を実行できます。
-
StartLocalTransaction API を呼び出して、指定されたパーティションキー値に基づいてローカル トランザクションを作成し、ローカル トランザクション ID を取得します。
-
GetRow API または GetRange API を呼び出してデータを読み取ります。リクエストにはローカル トランザクション ID を含める必要があります。
-
クライアントでデータを変更します。
-
PutRow、UpdateRow、DeleteRow、または BatchWriteRow API を呼び出して、変更されたデータを書き戻します。リクエストにはローカル トランザクション ID を含める必要があります。
-
CommitTransaction API を呼び出して、ローカル トランザクションをコミットします。
複雑なシナリオ:メールボックスシナリオ
ローカル トランザクションを作成して、特定のユーザーのメールに対してアトミック操作を実行できます。
ローカル トランザクション機能を正しく使用するには、データテーブルに 2 つのインデックス テーブルを作成する必要があります。次の表は、これらのテーブルのプライマリキー列を示しています。この表では、Type 列がデータテーブルとインデックス テーブルを区別します。各インデックス行は IndexField 列を使用して特定の意味を持つフィールドを格納しますが、データテーブルには IndexField 列は含まれません。
|
テーブル |
プライマリキー列 |
|||
|
UserID |
Type |
IndexField |
MailID |
|
|
データテーブル |
ユーザー ID |
"Main" |
N/A |
メール ID |
|
フォルダーインデックス テーブル |
ユーザー ID |
"Folder" |
$Folder |
メール ID |
|
SendTime インデックス テーブル |
ユーザー ID |
"SendTime" |
$SendTime |
メール ID |
具体的には、ローカル トランザクション機能を使用して、メールに対して次のような操作を実行できます。
シナリオ 1:ユーザーが送信した最新 100 件のメールを一覧表示
-
ユーザー ID を使用してローカル トランザクションを作成し、ローカル トランザクション ID を取得します。
-
GetRange API を呼び出して、SendTime インデックス テーブルから 100 件のメールをクエリします。リクエストにはローカル トランザクション ID を含める必要があります。
-
BatchGetRow API を呼び出して、データテーブルから 100 件のメールの詳細情報をクエリします。リクエストにはローカル トランザクション ID を含める必要があります。
-
CommitTransaction API を呼び出してローカル トランザクションをコミットするか、AbortTransaction API を呼び出してローカル トランザクションをアボートします。
シナリオ 2:あるフォルダー内のすべてのメールを別のフォルダーに移動
-
ユーザー ID を使用してローカル トランザクションを作成し、ローカル トランザクション ID を取得します。
-
GetRange API を呼び出して、フォルダーインデックス テーブルからメールをクエリします。リクエストにはローカル トランザクション ID を含める必要があります。
-
BatchWriteRow API を呼び出して、フォルダーインデックス テーブルに対して書き込み操作を実行します。リクエストにはローカル トランザクション ID を含める必要があります。
メールが転送されるたびに、2 つの行に対して書き込み操作が実行されます。具体的には、元のフォルダーを示す行がフォルダーインデックス テーブルから削除され、新しいフォルダーを示す行がフォルダーインデックス テーブルに追加されます。
-
CommitTransaction API を呼び出して、ローカル トランザクションをコミットします。
シナリオ 3:フォルダー内の既読メールと未読メールの数をカウント
-
ユーザー ID を使用してローカル トランザクションを作成し、ローカル トランザクション ID を取得します。
-
GetRange API を呼び出して、フォルダーインデックス テーブルからメールをクエリします。リクエストにはローカル トランザクション ID を含める必要があります。
-
BatchGetRow API を呼び出して、データテーブルから各メールの既読状態をクエリします。リクエストにはローカル トランザクション ID を含める必要があります。
-
CommitTransaction API を呼び出してローカル トランザクションをコミットするか、AbortTransaction API を呼び出してローカル トランザクションをアボートします。
この解決策は最適ではありません。このシナリオでは、インデックス テーブルをさらに追加すると、クエリを高速化できます。ローカル トランザクション機能は、データテーブルとインデックス テーブル間の状態の一貫性を保証し、開発を簡素化します。たとえば、メールをカウントするこの解決策では、多くのメールを読み取る必要があり、高いオーバーヘッドが発生します。オーバーヘッドを削減し、クエリを高速化するには、新しいインデックス テーブルを使用して既読メールと未読メールの数を格納します。
事前準備
データテーブルを作成するときに、ローカル トランザクションを有効にする必要があります。Tablestore コンソール、Tablestore SDK for Java V5.11.0 以降、または最新バージョンの Tablestore SDK for Go を使用します。詳細については、「データテーブルの作成」をご参照ください。
既存のデータテーブルでローカル トランザクションが必要であるにもかかわらず、テーブル作成時にこの機能を有効にしなかった場合は、チケットを送信してください。また、DingTalk グループ 36165029092 (Tablestore 技術ディスカッショングループ-3) に参加してサポートを求めることもできます
制限
-
自動インクリメントプライマリキー列機能とローカル トランザクション機能を同時に使用することはできません。
-
ローカル トランザクションでの同時実行操作を制御するために、悲観的ロックが使用されます。
-
ローカル トランザクションの有効期間は最大 60 秒です。
ローカル トランザクションが 60 秒以内にコミットまたはアボートされない場合、Tablestore サーバーはローカル トランザクションがタイムアウトしたと判断し、トランザクションをアボートします。
-
タイムアウトエラーが返された場合でも、Tablestore サーバー側でトランザクションが作成されることがあります。この場合、作成されたトランザクションがタイムアウトした後に、トランザクション作成リクエストを再送信できます。
-
ローカル トランザクションがコミットされない場合、無効になる可能性があります。この場合、このトランザクションの操作を再試行してください。
-
ローカル トランザクション内のデータに対して書き込み操作が実行されない場合、コミット操作とアボート操作は同じ効果を持ちます。
-
Tablestore は、ローカル トランザクション内のデータの読み書き操作に次の制限を課します。
-
ローカル トランザクション ID を使用して、トランザクションの作成に使用されたパーティションキー値に基づいて指定された範囲外のデータにアクセスすることはできません。
-
同じトランザクション内のすべての書き込みリクエストのパーティションキー値は、トランザクションの作成に使用されたパーティションキー値と同じである必要があります。この制限は読み取りリクエストには適用されません。
-
ローカル トランザクションは、一度に 1 つのリクエストでのみ使用できます。ローカル トランザクションが使用中の場合、同じローカル トランザクション ID を使用する他の操作は失敗します。
-
ローカル トランザクション内のデータに対する 2 つの連続した読み取りまたは書き込み操作の最大間隔は 60 秒です。
ローカル トランザクション内のデータに対して 60 秒を超えて読み取りまたは書き込み操作が実行されない場合、Tablestore サーバーはトランザクションがタイムアウトしたと判断し、トランザクションをアボートします。
-
各トランザクションには最大 4 MB のデータを書き込むことができます。各トランザクションに書き込まれるデータの量は、通常の書き込みリクエストと同じ方法で計算されます。
-
セルのバージョン番号を指定しない場合、Tablestore サーバーは、トランザクションがコミットされた時点ではなく、セルがトランザクションに書き込まれた時点で、通常の方法でセルにバージョン番号を自動的に割り当てます。
-
BatchWriteRow リクエストにローカル トランザクション ID が含まれている場合、リクエスト内のすべての行は、ローカル トランザクション ID に関連付けられたテーブルにのみ書き込むことができます。
-
ローカル トランザクションを使用すると、ローカル トランザクションが作成されたパーティションキー値のデータに書き込みロックが適用されます。ローカル トランザクション内のデータを書き込むための書き込みリクエストは、そのトランザクションの ID を含んでいる場合にのみ成功します。ローカル トランザクション ID を含まないリクエストや、他のローカル トランザクションの ID を含む書き込みリクエストは失敗します。ローカル トランザクション内のデータは、トランザクションがコミットまたはアボートされた場合、またはトランザクションがタイムアウトした場合にロック解除されます。
-
ローカル トランザクション ID を持つ読み取りまたは書き込みリクエストが拒否された場合でも、ローカル トランザクションは有効なままです。再試行ルールを指定してリクエストを再送信するか、トランザクションをアボートしてください。
-
手順
ローカル トランザクションは Tablestore SDK を使用してのみ管理してください。
ローカル トランザクションを使用するには、まずパーティションキー値に対してローカル トランザクションを作成し、次にトランザクション内でデータを読み書きし、最後に必要に応じてトランザクションをコミットまたはアボートします。次の例では Tablestore SDK for Java を使用します。
ローカル トランザクション機能を使用したデータ行の書き込み
次の例では、テーブルの指定されたパーティションキー値に対してローカル トランザクションを作成し、ローカル トランザクション内に行を書き込みます。
String tableName = "local_tx_demo";
// 1. 指定したパーティションキー値でローカル トランザクションを開始し、トランザクション ID を取得します。
PrimaryKeyBuilder pkBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pkBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
PrimaryKey partitionKey = pkBuilder.build();
StartLocalTransactionRequest startRequest =
new StartLocalTransactionRequest(tableName, partitionKey);
String txnId = client.startLocalTransaction(startRequest).getTransactionID();
// 2. トランザクション内で行を書き込みます。完全なプライマリキーを指定し、トランザクション ID を設定する必要があります。
PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(10001));
PrimaryKey rowKey = rowKeyBuilder.build();
RowPutChange rowPutChange = new RowPutChange(tableName, rowKey);
rowPutChange.addColumn(new Column("col1", ColumnValue.fromString("colvalue")));
rowPutChange.addColumn(new Column("col2", ColumnValue.fromLong(10)));
PutRowRequest putRequest = new PutRowRequest(rowPutChange);
putRequest.setTransactionId(txnId);
client.putRow(putRequest);
// 3. トランザクションをコミットして、すべての書き込みを有効にします。変更を破棄するには、代わりに abortTransaction() を呼び出します。
CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
client.commitTransaction(commitRequest);
ローカル トランザクション機能を使用したデータ行の読み取り
次の例では、テーブルの指定されたパーティションキー値に対してローカル トランザクションを作成し、ローカル トランザクション内で行を読み取ります。
String tableName = "local_tx_demo";
// 1. 指定したパーティションキー値でローカル トランザクションを開始します。
PrimaryKeyBuilder pkBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
pkBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
PrimaryKey partitionKey = pkBuilder.build();
StartLocalTransactionRequest startRequest =
new StartLocalTransactionRequest(tableName, partitionKey);
String txnId = client.startLocalTransaction(startRequest).getTransactionID();
// 2. トランザクション内で行を読み取ります。完全なプライマリキーを指定し、トランザクション ID を設定する必要があります。
PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(10001));
PrimaryKey rowKey = rowKeyBuilder.build();
SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(tableName, rowKey);
criteria.setMaxVersions(1);
GetRowRequest getRequest = new GetRowRequest(criteria);
getRequest.setTransactionId(txnId);
GetRowResponse getResponse = client.getRow(getRequest);
// 3. トランザクションをコミットまたはアボートします。読み取り専用トランザクションの場合、どちらも同じ効果になり、トランザクションを解放します。
CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
client.commitTransaction(commitRequest);
Row row = getResponse.getRow();
System.out.println(row);
SDK との統合
次の SDK でローカル トランザクションを使用します。
課金
-
StartLocalTransaction、CommitTransaction、および AbortTransaction 操作はそれぞれ 1 書き込みキャパシティーユニット (CU) を消費します。
-
読み書き操作は、標準の読み書きリクエストと同様に課金されます。課金の詳細については、「課金の概要」をご参照ください。
エラーコード
|
エラーコード |
説明 |
|
OTSRowOperationConflict |
パーティションキー値は、別のローカル トランザクションによってすでに使用されています。 |
|
OTSSessionNotExist |
指定されたトランザクション ID に対応するトランザクションが存在しないか、トランザクションが無効であるか、タイムアウトしました。 |
|
OTSSessionBusy |
トランザクションに対する最後のリクエストが完了していません。 |
|
OTSOutOfTransactionDataSizeLimit |
トランザクション内のデータ量が上限を超えています。 |
|
OTSDataOutOfRange |
データ操作が、トランザクションの作成に使用されたパーティションキー値で指定された範囲を超えています。 |