只有當數據滿足列判斷條件時,才能對數據表中的數據進行更新;當數據不滿足列判斷條件時,數據更新失敗。
場景
適用于對高并發應用進行更新的場景,用于保證數據正確性和一致性。
在高并發場景下,原有列值(old_value)可能會被其他客戶端更新,如果使用條件更新,則只有在當前值(value)等于原有列值時,才更新當前值為最新值(new_value)。
在網頁計數、游戲等高并發場景下,使用條件更新后,更新數據可能會失敗,此時需要一定次數的重試。
條件更新的實現過程如下:
獲取當前列值。
對當前列值進行運算,判斷是否滿足條件。
如果滿足條件,則使用最新值進行更新。
如果不滿足條件,則數據更新失敗。
功能概述
在通過PutRow、UpdateRow、DeleteRow和BatchWriteRow接口操作表中數據時,您可以使用條件更新檢查行存在性條件和列條件。只有當數據滿足列判斷條件時,才能對數據表中的數據進行更新。
使用條件更新可以實現樂觀鎖功能,即在更新某行時,先獲取某列的值,假設列A的值為1,然后設置條件為A=1
,更新行使A=2
。如果數據更新失敗,則表示有其他客戶端已成功更新該行。
條件更新中的列判斷條件包括列條件和行存在性條件。
列條件
目前支持SingleColumnValueCondition和CompositeColumnValueCondition,是基于某一列或者某些列的列值進行條件判斷,與過濾器Filter中的條件類似。
列條件中支持使用關系運算(=、!=、>、>=、<、<=)和邏輯運算(NOT、AND、OR),單個更新條件中最多支持設置10個列條件。
行存在性條件
對數據表進行更改操作時,系統會先檢查行存在性條件,如果不滿足行存在性條件,則更改失敗并給用戶報錯。
行存在性條件包括IGNORE(忽略)、EXPECT_EXIST(期望存在)和EXPECT_NOT_EXIST(期望不存在)三種類型。
通過不同接口操作數據表的數據時的行存在性條件更新規則請參見下表。
說明BatchWriteRow操作由多個PutRow、UpdateRow、DeleteRow子操作組成,所以通過BatchWriteRow接口操作數據表中的數據時,請根據操作類型查看對應接口的更新規則。
接口
IGNORE
EXPECT_EXIST
EXPECT_NOT_EXIST
PutRow(已存在行)
成功
成功
失敗
PutRow(不存在行)
成功
失敗
成功
UpdateRow(已存在行)
成功
成功
失敗
UpdateRow(不存在行)
成功
失敗
成功
DeleteRow(已存在行)
成功
成功
失敗
DeleteRow(不存在行)
成功
失敗
成功
使用方式
您可以通過命令行工具CLI或者SDK使用條件更新功能。
使用命令行工具CLI
您可以使用命令行工具在寫入數據或者更新數據時使用條件更新功能。
執行
put
命令插入新數據時使用行存在性檢查條件。更多信息,請參見插入新數據。以下示例用于在數據表中插入一行數據,該行的第一主鍵列值為“86”,第二主鍵列值為6771,屬性列有name(string類型)和country(string類型)兩列。無論此行是否存在均會插入新數據,如果之前行已存在,則寫入數據時會覆蓋原有數據。
put --pk '["86", 6771]' --attr '[{"c":"name", "v":"redchen"}, {"c":"country", "v":"china"}]' --condition ignore
執行
update
命令更新一行數據時使用行存在性檢查條件。更多信息,請參見更新一行數據。以下示例用于更新第一主鍵列為“86”,第二主鍵列為6771的行數據。無論此行是否存在均會更新數據,如果之前該行不存在,則更新數據時會插入一行數據。
update --pk '["86", 6771]' --attr '[{"c":"name", "v":"redchen"}, {"c":"country", "v":"china"}]' --condition ignore
使用SDK
您可以通過Java SDK、Go SDK、Python SDK、Node.js SDK、.NET SDK和PHP SDK使用條件更新功能。此處以Java SDK為例介紹條件更新功能的使用。
使用列判斷條件更新數據
構造SingleColumnValueCondition。
以下示例用于當數據滿足
Col0==0
條件時,更新Col0列值。private static void updateRowWithSingleColumnValueCondition(SyncClient client, String pkValue){ //構造主鍵。 PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString(pkValue)); PrimaryKey primaryKey = primaryKeyBuilder.build(); //讀取一行數據。 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<TABLE_NAME>", primaryKey); criteria.setMaxVersions(1); GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria)); Row row = getRowResponse.getRow(); long col0Value = row.getLatestColumn("Col0").getValue().asLong(); //設置數據表名稱。 RowUpdateChange rowUpdateChange = new RowUpdateChange("<TABLE_NAME>", primaryKey); //設置條件為Col0==0。 SingleColumnValueCondition singleColumnValueCondition = new SingleColumnValueCondition("Col0", SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(0)); //如果不存在Col0列,條件檢查不通過。 singleColumnValueCondition.setPassIfMissing(false); //只判斷最新版本。 singleColumnValueCondition.setLatestVersionsOnly(true); Condition condition = new Condition(); condition.setColumnCondition(singleColumnValueCondition); rowUpdateChange.setCondition(condition); //當數據滿足列判斷條件時,更新Col0列值,使Col0列值增加1。 rowUpdateChange.put(new Column("Col0", ColumnValue.fromLong(col0Value+1))); try { client.updateRow(new UpdateRowRequest(rowUpdateChange)); System.out.println("數據更新成功!"); } catch (TableStoreException ex) { System.out.println("數據更新失敗!" + ex.toString()); } }
構造CompositeColumnValueCondition。
以下示例用于當數據滿足
Col0 == 0且Col1 > 100
或者Col2 <= 10
中任意一個條件時,更新Col0列值。private static void updateRowWithCompositeColumnValueCondition(SyncClient client, String pkValue){ //構造主鍵。 PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder(); primaryKeyBuilder.addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString(pkValue)); PrimaryKey primaryKey = primaryKeyBuilder.build(); //讀取一行數據。 SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<TABLE_NAME>", primaryKey); criteria.setMaxVersions(1); GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria)); Row row = getRowResponse.getRow(); long col0Value = row.getLatestColumn("Col0").getValue().asLong(); //設置數據表名稱。 RowUpdateChange rowUpdateChange = new RowUpdateChange("<TABLE_NAME>", primaryKey); //composite1的條件為(Col0 == 0) AND (Col1 > 100)。 CompositeColumnValueCondition composite1 = new CompositeColumnValueCondition(CompositeColumnValueCondition.LogicOperator.AND); SingleColumnValueCondition single1 = new SingleColumnValueCondition("Col0", SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(0)); SingleColumnValueCondition single2 = new SingleColumnValueCondition("Col1", SingleColumnValueCondition.CompareOperator.GREATER_THAN, ColumnValue.fromLong(100)); composite1.addCondition(single1); composite1.addCondition(single2); //composite2的條件為((Col0 == 0) AND (Col1 > 100)) OR (Col2 <= 10)。 CompositeColumnValueCondition composite2 = new CompositeColumnValueCondition(CompositeColumnValueCondition.LogicOperator.OR); SingleColumnValueCondition single3 = new SingleColumnValueCondition("Col2", SingleColumnValueCondition.CompareOperator.LESS_EQUAL, ColumnValue.fromLong(10)); composite2.addCondition(composite1); composite2.addCondition(single3); Condition condition = new Condition(); condition.setColumnCondition(composite2); rowUpdateChange.setCondition(condition); //當數據滿足列判斷條件時,更新Col0列值,使Col0列值增加1。 rowUpdateChange.put(new Column("Col0", ColumnValue.fromLong(col0Value+1))); try { client.updateRow(new UpdateRowRequest(rowUpdateChange)); System.out.println("數據更新成功!"); } catch (TableStoreException ex) { System.out.println("數據更新失敗!" + ex.toString()); } }
使用樂觀鎖更新數據
通過Condition實現樂觀鎖機制,遞增一列。
private static void updateRowWithCondition(SyncClient client, String pkValue) {
//構造主鍵。
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("pk", PrimaryKeyValue.fromString(pkValue));
PrimaryKey primaryKey = primaryKeyBuilder.build();
//讀取一行數據。
SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<TABLE_NAME>", primaryKey);
criteria.setMaxVersions(1);
GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
Row row = getRowResponse.getRow();
long col0Value = row.getLatestColumn("Col0").getValue().asLong();
//條件更新Col0列,使列值加1。
RowUpdateChange rowUpdateChange = new RowUpdateChange("<TABLE_NAME>", primaryKey);
Condition condition = new Condition(RowExistenceExpectation.EXPECT_EXIST);
ColumnCondition columnCondition = new SingleColumnValueCondition("Col0", SingleColumnValueCondition.CompareOperator.EQUAL, ColumnValue.fromLong(col0Value));
condition.setColumnCondition(columnCondition);
rowUpdateChange.setCondition(condition);
rowUpdateChange.put(new Column("Col0", ColumnValue.fromLong(col0Value + 1)));
try {
client.updateRow(new UpdateRowRequest(rowUpdateChange));
} catch (TableStoreException ex) {
System.out.println(ex.toString());
}
}
計費說明
表格存儲包括VCU模式(原預留模式)和CU模式(原按量模式)兩種計費模式。使用不同的計費模式時,計算部分的計費方式不同。
計費模式 | 計算部分的計費說明 |
以包年包月方式預先購買計算能力。計算能力中涵蓋使用條件更新的計算消耗。 | |
數據寫入或者更新成功,不影響各個接口的CU計算規則,如果條件更新失敗,則會消耗1單位寫CU和1單位讀CU。根據實例類型不同,計費時需要區分按量讀寫CU以及預留讀寫CU。 |