Java SDK 基於指定分區索引值執行局部事務,使事務內的寫操作全部提交或全部丟棄,隔離等級為讀已提交(Read Committed)。
前提條件
-
安裝 Tablestore Java SDK並初始化用戶端。
-
資料表已開啟局部事務功能。建立資料表時可直接開啟;如果需要為已有資料表開啟局部事務,或者確認資料表是否已開啟該功能,請提交工單。
功能說明
局部事務的作用範圍限定在單個分區索引值內。事務內的所有讀寫請求共用同一個事務 ID,Table Store通過該 ID 保證原子性和隔離性。操作分為以下三個階段:
-
startLocalTransaction(StartLocalTransactionRequest):傳入分區索引值,建立局部事務並擷取事務 ID。 -
在事務內執行讀寫操作,每個請求需通過
setTransactionId(txnId)攜帶事務 ID。支援的操作:GetRow/PutRow/UpdateRow/DeleteRow/BatchWriteRow/GetRange。 -
commitTransaction(CommitTransactionRequest)提交事務使修改生效;或abortTransaction(AbortTransactionRequest)丟棄事務放棄所有修改。
public StartLocalTransactionResponse startLocalTransaction(StartLocalTransactionRequest request) throws TableStoreException, ClientException
public CommitTransactionResponse commitTransaction(CommitTransactionRequest request) throws TableStoreException, ClientException
public AbortTransactionResponse abortTransaction(AbortTransactionRequest request) throws TableStoreException, ClientException
public void setTransactionId(String transactionId)
以下樣本為分區索引值 pkvalue 建立局部事務,在事務內寫入主鍵為 (pkvalue, 10001) 的一行資料,然後提交事務。
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);
參數說明
建立事務請求
StartLocalTransactionRequest 包含以下參數。
|
名稱 |
類型 |
說明 |
|
tableName(必選) |
String |
資料表名稱。 |
|
primaryKey(必選) |
PrimaryKey |
用於限定事務範圍的分區索引值。建立局部事務時只需設定資料表的第一個主鍵列。 |
|
rowKeys(可選) |
List<PrimaryKey> |
啟動事務時要鎖定的行主鍵列表。通過 |
事務內請求
事務內的資料讀寫請求繼承 TxnRequest,包含以下參數。
|
名稱 |
類型 |
說明 |
|
transactionId(必選) |
String |
局部事務 ID,由 |
提交或丟棄事務請求
CommitTransactionRequest 和 AbortTransactionRequest 包含以下參數。
|
名稱 |
類型 |
說明 |
|
transactionID(必選) |
String |
要提交或丟棄的局部事務 ID。構造請求時設定。 |
傳回值
建立事務結果
StartLocalTransactionResponse 包含以下業務欄位。
|
名稱 |
類型 |
說明 |
|
transactionID |
String |
建立的局部事務 ID。通過 |
使用限制
-
主鍵自增列與局部事務功能不能同時使用。
-
局部事務通過悲觀鎖(Pessimistic Lock)實現並發控制;事務期間分區索引值的資料加寫鎖,僅持有事務 ID 的寫請求成功。
-
事務生命週期最長 60 秒;兩次操作間隔超過 60 秒視為逾時,服務端自動丟棄。
-
同一時間只能有一個請求使用事務 ID;並發使用同一事務 ID 的操作均失敗。
-
事務內寫請求的分區索引值必須與建立事務時一致;讀請求無此限制。
-
單個事務寫入資料量最大 4 MB。
-
事務內寫入列值時未指定版本號碼(timestamp)的,版本號碼在寫入資料時(而非提交事務時)由服務端自動產生,規則與普通寫入一致。
-
BatchWriteRow請求攜帶事務 ID 時,所有行只能操作該事務對應的表。 -
未對範圍內資料執行寫操作時,提交與丟棄等同。
-
帶事務 ID 的讀寫請求失敗不影響事務存活,可指定重試規則或主動丟棄。
情境樣本
事務內讀取一行
為指定分區索引值建立局部事務,在事務內讀取一行資料。事務內沒有寫操作時,提交和丟棄的效果相同,均會釋放事務。
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);
事務內批量寫入多行
通過 BatchWriteRowRequest.setTransactionId(txnId) 在批量寫入中攜帶事務 ID。所有行的分區索引值必須與建立事務時一致,單次提交原子生效。
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
BatchWriteRowRequest batchRequest = new BatchWriteRowRequest();
batchRequest.setTransactionId(txnId);
// 添加多行(所有行的 pk1 必須等於事務的分區索引值 "pkvalue")
for (long pk2 = 20001; pk2 <= 20003; pk2++) {
PrimaryKeyBuilder rowKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
rowKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("pkvalue"));
rowKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong(pk2));
RowPutChange rowPutChange = new RowPutChange(tableName, rowKeyBuilder.build());
rowPutChange.addColumn(new Column("col1", ColumnValue.fromString("batch_" + pk2)));
batchRequest.addRowChange(rowPutChange);
}
BatchWriteRowResponse batchResponse = client.batchWriteRow(batchRequest);
System.out.println("Batch all succeeded: " + batchResponse.isAllSucceed());
// 3. 提交事務,使所有批量寫入原子生效
CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId);
client.commitTransaction(commitRequest);