情境
適用於對高並發應用進行更新的情境,用於保證資料正確性和一致性。
在高並發情境下,原有列值(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介面操作資料表中的資料時,請根據操作類型查看對應介面的更新規則。
使用方式
您可以通過命令列工具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());
}
}
計費說明
Tablestore包括VCU模式(原預留模式)和CU模式(原按量模式)兩種計費模式。使用不同的計費模式時,計算部分的計費方式不同。
資料寫入或者更新成功,不影響各個介面的CU計算規則,如果條件更新失敗,則會消耗1單位寫CU和1單位讀CU。根據執行個體類型不同,計費時需要區分按量讀寫CU以及預留讀寫CU。