使用局部事務功能,建立資料範圍在一個分區索引值內的局部事務。對局部事務中的資料進行讀寫操作後,可以根據實際提交或者丟棄局部事務。局部事務通過悲觀鎖(Pessimistic Lock)實現並發控制。
目前局部事務功能處於邀測中,預設關閉。如果需要使用該功能,請提交工單進行申請。
使用局部事務功能,可以實現單行或多行讀寫的原子操作,擴充了使用情境。
情境
- 讀-寫情境(簡單情境) 當需要進行讀取-修改-寫回(Read-Modify-Write)操作時,可以選擇如下兩種方式,但兩種方式有一些限制。使用局部事務可以實現一個分區索引值範圍內的通用讀取-修改-寫迴流程。
- 使用StartLocalTransaction為分區索引值建立一個局部事務,並擷取局部事務ID。
- 使用GetRow或GetRange介面擷取資料,且請求中需要帶上局部事務ID。
- 用戶端本地修改資料。
- 使用PutRow、UpdateRow、DeleteRow或BatchWriteRow介面將修改後的資料寫回,且請求中需要帶上事務ID。
- 使用CommitTransaction提交局部事務。
- 郵箱情境(複雜情境)
使用局部事務可以實現對同一個使用者郵件的原子操作。
為了能正常使用局部事務功能,在一張物理表上同時使用了一張主表和兩張索引表,其主鍵列請參見下表。
其中使用Type列區分主表和不同的索引表,不同的索引行使用IndexField列儲存不同含義的欄位,而主表無IndexField列。
表 UserID Type IndexField MailID 主表 使用者ID "Main" "N/A" 郵件ID Folder表 使用者ID "Folder" $Folder 郵件ID SendTime表 使用者ID "SendTime" $SendTime 郵件ID 使用局部事務功能可以完成如下操作。
- 列出某個使用者發送的最近100封郵件,操作步驟如下:
- 使用UserID建立一個局部事務,並擷取局部事務ID。
- 使用GetRange介面從SendTime表擷取100封郵件,且請求中需要帶上局部事務ID。
- 使用BatchGetRow介面從主表擷取100封郵件的詳細資料,且請求中需要帶上局部事務ID。
- 使用CommitTransaction提交局部事務或者使用AbortTransaction丟棄局部事務。
由於未對該局部事務進行寫操作,所以提交或丟棄局部事務的操作是等同的。
- 將某個目錄下的所有郵件移到另一個目錄下,操作步驟如下:
- 使用UserID建立一個局部事務,並擷取局部事務ID。
- 使用GetRange介面從Folder表擷取若干封郵件,且請求中需要帶上局部事務ID。
- 使用BatchWriteRow介面對Folder表進行寫操作,且請求中需要帶上局部事務ID。
每封郵件對應兩行寫操作,一行是將對應舊Folder的行刪掉,另一行是對應新Folder增加一行。
- 使用CommitTransaction提交局部事務。
- 統計某個目錄下已讀郵件與未讀郵件的數量(非最優方案,詳情說明請參見下文),操作步驟如下:
- 使用UserID建立一個局部事務,並擷取局部事務ID。
- 使用GetRange介面從Folder表擷取若干封郵件,且請求中需要帶上局部事務ID。
- 使用BatchGetRow介面從主表擷取每封郵件的已讀狀態。
- 使用CommitTransaction提交局部事務或者使用AbortTransaction丟棄局部事務。
由於未對該局部事務進行寫操作,所以提交或丟棄局部事務的操作是等同的。
在此情境中,可以通過增加新的索引表加速常用操作。使用局部事務後,無需擔心主表與索引表的狀態不一致,降低開發難度。例如“統計郵件數量”功能在上面的方案中需要讀取很多封郵件,開銷較大,可以使用一個新的索引表儲存已讀和未讀郵件的數量,從而降低開銷,加速查詢。
- 列出某個使用者發送的最近100封郵件,操作步驟如下:
限制
- 每個局部事務從建立開始生命週期最長為60秒。
如果超過60秒未提交或丟棄局部事務,Table Store服務端會認為此局部事務逾時,並將局部事務丟棄。
- 如果建立局部事務時逾時,此請求可能在Table Store服務端已執行成功,此時使用者需要等待該局部事務逾時後重新建立。
- 未提交的局部事務可能失效,如果出現此情況,需要重試該局部事務內的操作。
- 在局部事務中讀寫資料有如下限制:
- 不能使用局部事務ID訪問局部事務範圍(即建立時使用的分區索引值)以外的資料。
- 同一個局部事務中所有寫請求的分區索引值必須與建立局部事務時的分區索引值相同,讀請求則無此限制。
- 一個局部事務同時只能用於一個請求中,在使用局部事務期間,其它使用此局部事務ID的操作均會失敗。
- 每個局部事務中兩次讀寫操作的最大間隔為60秒。
如果超過60秒未操作局部事務,Table Store服務端會認為此局部事務逾時,並將局部事務丟棄。
- 每個局部事務中寫入的資料量最大為4 MB,按正常的寫請求資料量計算規則累加。
- 如果在局部事務中寫入了未指定版本號碼的Cell,該Cell的版本號碼會在寫入時(而非提交時)由Table Store服務端自動產生,建置規則與正常寫入一個未指定版本號碼的Cell相同。
- 如果BatchWriteRow請求中帶有局部事務ID,則此請求中所有行只能操作該局部事務ID對應的表。
- 在使用局部事務期間,對應分區索引值的資料會被上寫鎖,只有持有局部事務ID在局部事務範圍內的寫請求才會成功。其它非事務請求或持有其他局部事務ID在局部事務範圍內的寫請求均會失敗。在局部事務提交、丟棄或逾時後,對應的鎖也會被釋放。
- 帶有局部事務ID的讀寫請求失敗不會影響局部事務本身的存活情況,您可以指定重試規則進行重試或者主動丟棄當前局部事務。
介面
支援對局部事務進行操作的介面請參見下表。介面 | 說明 |
---|---|
StartLocalTransaction | 建立一個局部事務。 |
CommitTransaction | 提交一個局部事務。 |
AbortTransaction | 丟棄一個局部事務。 |
GetRow | 對局部事務範圍內的資料進行讀寫操作。具體操作,請參見寫入資料、讀取資料和刪除資料。 说明 當前局部事務範圍在一個分區索引值內。關於分區鍵的更多資訊,請參見分區鍵。 |
PutRow | |
UpdateRow | |
DeleteRow | |
BatchWriteRow | |
GetRange |
使用
您可以使用如下語言的SDK實現局部事務功能。
參數
參數 | 說明 |
---|---|
TableName | 資料表名稱。 |
PrimaryKey | 資料表主鍵。
|
TransactionId | 局部事務ID,用於唯一標識一個局部事務。 建立局部事務後,操作局部事務時均需要帶上局部事務ID。 |
樣本
- 調用AsyncClient或SyncClient的startLocalTransaction方法使用指定分區索引值建立一個局部事務,並擷取局部事務ID。
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("txnKey")); PrimaryKey primaryKey = primaryKeyBuilder.build(); StartLocalTransactionRequest request = new StartLocalTransactionRequest(tableName, primaryKey); String txnId = client.startLocalTransaction(request).getTransactionID();
- 對局部事務範圍內的資料進行讀寫操作。
對局部事務範圍內資料的讀寫操作與正常讀寫資料操作基本相同,只需填入局部事務ID即可。
- 寫入一行資料。
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("txnKey")); primaryKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong("userId")); PrimaryKey primaryKey = primaryKeyBuilder.build(); RowPutChange rowPutChange = new RowPutChange(tableName, primaryKey); rowPutChange.addColumn(new Column("Col", ColumnValue.fromLong(columnValue))); PutRowRequest request = new PutRowRequest(rowPutChange); request.setTransactionId(txnId); client.putRow(request);
- 讀取此行資料。
PrimaryKeyBuilder primaryKeyBuilder; primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn("pk1", PrimaryKeyValue.fromString("txnKey")); primaryKeyBuilder.addPrimaryKeyColumn("pk2", PrimaryKeyValue.fromLong("userId")); PrimaryKey primaryKey = primaryKeyBuilder.build(); SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(tableName, primaryKey); criteria.setMaxVersions(1); //設定讀取最新版本的資料。 GetRowRequest request = new GetRowRequest(criteria); request.setTransactionId(txnId); GetRowResponse getRowResponse = client.getRow(request);
- 寫入一行資料。
- 提交或丟棄局部事務。
- 提交局部事務,使局部事務中的所有資料修改生效。
CommitTransactionRequest commitRequest = new CommitTransactionRequest(txnId); client.commitTransaction(commitRequest);
- 丟棄局部事務,局部事務中的所有資料修改均不會應用到原有資料。
AbortTransactionRequest abortRequest = new AbortTransactionRequest(txnId); client.abortTransaction(abortRequest);
- 提交局部事務,使局部事務中的所有資料修改生效。
計費
- StartLocalTransaction、CommitTransaction和AbortTransaction操作分別消耗1單位寫CU。
- 讀寫操作的計費與正常的讀寫請求相同。關於計費的更多資訊,請參見計費概述。
錯誤碼
錯誤碼 | 說明 |
---|---|
OTSRowOperationConflict | 該分區索引值已被其它局部事務佔用。 |
OTSSessionNotExist | 事務ID對應的事務不存在,或該事務已失效或逾時。 |
OTSSessionBusy | 該事務的上一次請求尚未結束。 |
OTSOutOfTransactionDataSizeLimit | 事務內的資料量超過上限。 |
OTSDataOutOfRange | 使用者操作資料的分區鍵超出了事務建立的分區鍵範圍。 |