场景
适用于对高并发应用进行更新的场景,用于保证数据正确性和一致性。
在高并发场景下,原有列值(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());
}
}
计费说明
表格存储包括VCU模式(原预留模式)和CU模式(原按量模式)两种计费模式。使用不同的计费模式时,计算部分的计费方式不同。
数据写入或者更新成功,不影响各个接口的CU计算规则,如果条件更新失败,则会消耗1单位写CU和1单位读CU。根据实例类型不同,计费时需要区分按量读写CU以及预留读写CU。