java - 如何在并发环境中获取 DynamoDB 中的下一个免费项目
问题描述
我在 DynamoDB 中有一个表和我的用户(部分键 = 键,排序键 = 否):
关键 是活动
用户 1 真
用户 2 假
……
在我的代码中,我需要返回下一个状态为未激活(isActive=false)的用户。如果我需要解决方案,那么最好的方法是什么?
- 一张巨大的桌子
- 并发环境
我编写了有效的代码,但由于扫描和过滤表达式,我不确定这是一个好的解决方案:
public String getFreeUser() throws IOException {
Table table = dynamoDB.getTable("usersTableName");
ScanSpec spec = new ScanSpec()
.withFilterExpression("isActive = :is_active")
.withValueMap(new ValueMap().withBoolean(":is_active", false));
ItemCollection<ScanOutcome> items = table.scan(spec);
Iterator<Item> iterator = items.iterator();
Item item = null;
while (iterator.hasNext()) {
item = iterator.next();
try {
UpdateItemSpec updateItemSpec = new UpdateItemSpec()
.withPrimaryKey(new PrimaryKey("key", item.getString("key")))
.withUpdateExpression("set #ian=:is_active_new")
.withConditionExpression("isActive = :is_active_old")
.withNameMap(new NameMap()
.with("#ian", "isActive"))
.withValueMap(new ValueMap()
.withBoolean(":is_active_old", false)
.withBoolean(":is_active_new", true))
.withReturnValues(ReturnValue.ALL_OLD);
UpdateItemOutcome outcome = table.updateItem(updateItemSpec);
return outcome.getItem().getString("key");
} catch (Exception e) {
}
}
throw new IOException("No active users were found");
}
解决方案
GSI + 查询 == 好
userID (PK) | isActive | otherAttribute | ...
user1 | true | foo | ...
user2 | false | bar | ...
user3 | true | baz | ...
user4 | false | 42 | ...
...
GSI:
userID | isActive (GSI-PK)
user1 | true
user2 | false
user3 | true
user4 | false
添加一个散列键为 的 GSI isActive
。这将允许您直接查询isActive
==所在的项目false
。
与扫描和过滤相比的好处是读取效率更高。代价是您的 GSI 需要它自己的存储空间,因此如果您的表很大(根据您的假设),那么您可能需要考虑使用稀疏索引。
稀疏索引 GSI + 查询 == 更好
userID (PK) | isNotActive | otherAttribute | ...
user1 | | foo | ...
user2 | false | bar | ...
user3 | | baz | ...
user4 | false | 42 | ...
...
GSI:
userId | isNotActive (GSI-PK)
user2 | false
user4 | false
考虑将属性替换为isActive
并且isNotActive
不要将此属性提供给活动用户。也就是说,非活动用户将具有true
但活动用户根本没有此属性。然后,您可以使用此isNotActive
属性创建 GSI。由于它只包含不活跃的用户,因此存储和查询将更小,更高效。
请注意,当用户变为活动状态时,您需要删除此属性,对于变为非活动状态的活动用户,反之亦然。
属性投影
无论您决定哪种 GSI 最适合您,如果您知道在查询这些非活动用户时需要哪些属性(即使只是“所有这些”),您都可以将这些属性投射到您的 GSI 中,这样您就不需要t 需要按键进行第二次查找。这将增加您的 GSI 的大小,但根据您的表大小、活跃用户与非活跃用户的比率以及您预期的访问模式,这可能是一个值得权衡的选择。
更新
针对第一条评论,要明确 GSI 密钥(现在标记为“GSI-PK”)不是用户 ID。我可以将isActive
oractive
列放在 GSI 表的最左侧,但这不是它在 AWS 控制台中的显示方式,因此为了与 AWS 显示它的方式保持一致,我将其保留为原始顺序。
关于并发的第二条评论,你说得对,我没有解决这个问题。我的解决方案将在并发环境中工作,除了一件事 - 你只能进行最终一致的读取,而不是强一致的读取。这意味着最近的新非活动用户(在大多数情况下,我指的是几分之一秒)可能尚未复制到 GSI。同样,最近从非活动更改为活动的用户可能尚未更新 GSI。您需要考虑您的用例是否可以接受最终一致的读取。
另一个考虑因素是,如果这将是一个非常大的表,如果查询结果总计 >1MB,那么您无论如何都会得到分页结果,因为 DynamoDB 强制执行该限制。如果没有全局表锁,由于页面查询之间来自其他客户端的更新,您将获得一些不一致,在这种情况下,最终一致的读取将需要为您工作。
推荐阅读
- grafana - `label_replace` 引入的基于过滤器向量的新标签
- javascript - MongoDB 卡在保存状态
- reactjs - Elm vs React:为什么 Elm 中没有生命周期函数?
- css - 如何将一个弹性项目与容器顶部对齐,而其他项目则遵循 justify-content:中心?
- c# - C# 从字符串日期字段中检查和删除非 ASCII 字符
- typescript - 为什么 TypeScript 的类型检查器认为泛型对象的属性不能是函数?
- python - 如何让 oauth2 服务器生成 JWT 令牌以避免存储令牌
- python - 两台或多台计算机python应用程序之间的Mysql数据库访问
- flutter - 将图像加载到 Flutter Gridview 时内存不足
- c# - 重定向的输出未显示在文本框中