局部事務
Java SDK 在指定分區索引值範圍內建立局部事務,事務內的所有操作要麼全部成功要麼全部失敗(隔離等級為讀已提交 Read Committed),適用於實現單行或多行的原子讀寫。
前提條件
安裝 Tablestore Java SDK並初始化用戶端。
已為資料表開啟局部事務功能。
說明建立資料表時如果未啟用局部事務,建表後需要使用局部事務功能,或者需要查看錶是否開啟局部事務,請提交工單。
功能說明
// 在 SyncClient 上調用
public StartLocalTransactionResponse startLocalTransaction(StartLocalTransactionRequest request) throws TableStoreException, ClientException
public CommitTransactionResponse commitTransaction(CommitTransactionRequest request) throws TableStoreException, ClientException
public AbortTransactionResponse abortTransaction(AbortTransactionRequest request) throws TableStoreException, ClientException
// 在事務請求(繼承 TxnRequest)上攜帶事務 ID
public void setTransactionId(String transactionId)局部事務的作用範圍限定在單個分區索引值內。事務內的所有讀寫請求共用同一個事務 ID,Table Store通過該 ID 保證原子性和隔離性。三個核心介面配合使用:
startLocalTransaction(StartLocalTransactionRequest):傳入分區索引值,建立局部事務並擷取事務 ID。在事務內執行讀寫操作,每個請求需通過
setTransactionId(txnId)攜帶事務 ID。支援的操作:GetRow/PutRow/UpdateRow/DeleteRow/BatchWriteRow/GetRange。commitTransaction(CommitTransactionRequest)提交事務使修改生效;或abortTransaction(AbortTransactionRequest)丟棄事務放棄所有修改。
以下樣本為分區索引值 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);參數說明
名稱 | 類型 | 說明 |
tableName(必選) | String | 資料表名稱。 |
primaryKey(必選) | PrimaryKey | 資料表主鍵。
|
transactionId(必選) | String | 局部事務 ID,由 事務內的每個讀寫請求都需通過 |
使用限制
主鍵自增列與局部事務功能不能同時使用。
局部事務通過悲觀鎖(Pessimistic Lock)實現並發控制;事務期間分區索引值的資料加寫鎖,僅持有事務 ID 的寫請求成功。
事務生命週期最長 60 秒;兩次操作間隔超過 60 秒視為逾時,服務端自動丟棄。
同一事務只能用於一個請求,並發使用同一事務 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);