有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何在并发环境中获取DynamoDB中的下一个空闲项

我在DynamoDB中有一个表,其中有我的用户(Partial key=key,Sort key=no):

激活

user1 true

用户2错误

在我的代码中,我需要返回状态为not active(isActive=false)的下一个用户。如果我需要解决方案,最好的方法是什么

  1. 一张大桌子
  2. 并发环境

我编写的代码可以工作,但由于扫描和筛选表达式的原因,我不确定它是否是一个好的解决方案:

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");
    }

共 (1) 个答案

  1. # 1 楼答案

    GSI+Query==GOOD

    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
    
    

    添加哈希键为isActive的GSI。这将允许您直接查询isActive==false的项目

    与扫描和筛选相比,读取的好处是效率更高。成本是你的GSI需要它自己的存储,所以如果你的表很大(根据你的假设),那么你可能需要考虑一个稀疏索引。p>

    稀疏索引GSI+Query==更好

    userID (PK) | isNotActive | otherAttribute | ...
    user1       |             | foo            | ...
    user2       | false       | bar            | ...
    user3       |             | baz            | ...
    user4       | false       | 42             | ...
    ...
    
    GSI:
    userId | isNotActive (GSI-PK)
    user2  | false
    user4  | false
    

    考虑用^ {< CD5>}替换属性^ {CD1>},不要将此属性赋给活动用户。也就是说,非活动用户将具有true,但活动用户将根本不具有此属性。然后可以使用这个isNotActive属性创建GSI。由于它只包含非活动用户,因此存储和查询将更小、更高效

    请注意,当用户处于活动状态时,您需要删除此属性,对于处于非活动状态的活动用户,则需要删除此属性

    属性投影

    无论您决定哪个GSI最适合您,如果您知道在查询这些非活动用户时需要哪些属性(即使只是“所有用户”),您可以将这些属性投影到您的GSI,这样您就不需要按键进行第二次查找。这将增加GSI的大小,但根据表大小、活动用户与非活动用户的比率以及预期的访问模式,这可能是一个值得权衡的问题

    更新

    作为对第一条评论的回应,需要澄清的是,GSI密钥(现在标记为“GSI-PK”)不是用户ID。我可以将isActiveactive列放在GSI表的最左边,但它在AWS控制台中不是这样显示的,因此为了与AWS的显示方式保持一致,我将其保留为原始顺序

    你是关于并发的第二条评论,你是对的,我没有提到这一点。我的解决方案将在并发环境中工作,除了一件事——您只能最终执行一致性读取,而不能执行强一致性读取。这意味着一个最近的非活动用户(在大多数情况下,我指的是几分之一秒)可能还没有复制到GSI。类似地,最近从非活动更改为活动的用户可能尚未更新GSI。您需要考虑最终一致阅读是否可用于您的用例。

    另一个需要考虑的问题是,如果这将是一个非常大的表,如果查询结果将达到总计>;1MB无论如何都会得到分页结果,因为DynamoDB强制执行该限制。如果没有全局表锁,由于页面查询之间来自其他客户端的更新,您将获得一些不一致性,在这种情况下,最终一致读取将需要为您工作